Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10661] ファイルの概要

このコミットは、Goコンパイラの字句解析器および構文解析器に関連する部分において、Bisonパーサジェネレータの内部的な予約語との衝突を避けるために、%union ディレクティブ内のフィールド名を lint から i へと変更するものです。具体的には、#define lint というBisonのプリプロセッサ定義が、%union 内の lint というフィールド名と衝突し、コンパイルエラーや予期せぬ動作を引き起こす可能性があったため、この名称変更が行われました。

コミット

commit f00340f02287456b24a414b4b10051b727c6ad2e
Author: Russ Cox <rsc@golang.org>
Date:   Wed Dec 7 23:38:32 2011 -0500

    gc: rename %union field name from lint to i
    
    #define lint has special meaning to Bison;
    having a field named lint conflicts with that.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/5462044
---
 src/cmd/gc/go.y  | 6 +++---
 src/cmd/gc/lex.c | 2 +-\n 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
index 1b00235083..075117102b 100644
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -31,13 +31,13 @@ static void fixlbrace(int);\n 	Type*		type;\n 	Sym*		sym;\n 	struct	Val	val;\n-\tint		lint;\n+\tint		i;\n }\n \n // |sed 's/.*	//' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx		/'\n \n %token	<val>	LLITERAL\n-%token	<lint>	LASOP\n+%token	<i>	LASOP\n %token	<sym>	LBREAK LCASE LCHAN LCOLAS LCONST LCONTINUE LDDD\n %token	<sym>	LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO\n %token	<sym>	LIF LIMPORT LINTERFACE LMAP LNAME\n@@ -47,7 +47,7 @@ static void fixlbrace(int);\n %token		LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT\n %token		LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH\n \n-%type	<lint>	lbrace import_here\n+%type	<i>	lbrace import_here\n %type	<sym>	sym packname\n %type	<val>\toliteral\n \ndiff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index cf7bbae9ed..3dbd6dda1a 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -1098,7 +1098,7 @@ lx:
 	return c;
 
 asop:
-\tyylval.lint = c;	// rathole to hold which asop
+\tyylval.i = c;	// rathole to hold which asop
 	DBG("lex: TOKEN ASOP %c\n", c);
 	return LASOP;
 

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f00340f02287456b24a414b4b10051b727c6ad2e

元コミット内容

このコミットは、Goコンパイラのgc(Go Compiler)部分における、Bisonパーサジェネレータが使用する文法ファイル(go.y)と字句解析器のC言語ソースファイル(lex.c)に対して行われた修正です。具体的には、%union ディレクティブ内で定義されていた lint というフィールドの名前を i に変更し、それに伴い、このフィールドを参照していた %token および %type の宣言、そして lex.c 内の yylval.lint の使用箇所も yylval.i に修正しました。

変更の背景

この変更の背景には、Bisonパーサジェネレータの内部的な動作と、C言語のプリプロセッサの挙動が関係しています。コミットメッセージに「#define lint has special meaning to Bison; having a field named lint conflicts with that.」とあるように、Bisonは内部的に lint という名前を特別な意味を持つマクロとして定義している可能性があります。

C言語のプリプロセッサは、コンパイルの前にソースコード内のマクロ定義を置換します。もしBisonが生成するコードや、Bisonが処理する入力ファイルにおいて、#define lint ... のようなマクロ定義が存在する場合、%union 内で lint というフィールド名を使用すると、プリプロセッサがこのフィールド名をマクロの定義内容で意図せず置換してしまい、結果として構文エラーや意味の誤解釈、あるいはコンパイル時の問題を引き起こす可能性がありました。

このような衝突を避けるため、Goコンパイラの開発者は、Bisonの内部的な予約語やマクロと衝突しないように、lint という一般的な名前をより汎用的な i(おそらく "integer" の略)に変更することを決定しました。これにより、コンパイラのビルドプロセスにおける潜在的な問題を解消し、安定性を向上させることが目的です。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

Goコンパイラ (gc)

Go言語の公式コンパイラは、通常 gc と呼ばれます。これはGo言語自体で書かれており、Goのソースコードを機械語に変換する役割を担っています。src/cmd/gc ディレクトリには、このコンパイラの主要な部分が含まれています。コンパイラは、字句解析、構文解析、意味解析、中間コード生成、最適化、コード生成といった複数のフェーズを経て動作します。

Bison (Yacc)

Bisonは、GNUプロジェクトが開発しているパーサジェネレータであり、Yacc (Yet Another Compiler Compiler) のGNU版です。Bisonは、BNF (Backus-Naur Form) に似た形式で記述された文法定義ファイル(通常 .y 拡張子を持つ)を読み込み、その文法を解析するためのC言語のソースコード(パーサ)を生成します。

  • 文法ファイル (.y): 構文規則と、各規則が認識されたときに実行されるC言語のコード(セマンティックアクション)を記述します。
  • %union ディレクティブ: Bisonにおいて非常に重要なディレクティブです。パーサが扱うトークンや非終端記号の「セマンティック値」(意味的な値)を格納するためのC言語の union 型を定義します。デフォルトでは、Bisonはセマンティック値を int 型として扱いますが、%union を使うことで、整数、文字列、ポインタ、構造体など、様々な型の値を扱えるようになります。%union で定義された union 型は、通常 YYSTYPE という名前で生成されます。
  • %token <member_name>: トークン(字句解析器が生成する最小単位)に、%union で定義されたどのメンバーをセマンティック値として関連付けるかを指定します。
  • %type <member_name>: 非終端記号(文法規則の左辺に現れる記号)に、%union で定義されたどのメンバーをセマンティック値として関連付けるかを指定します。

Lex (Flex)

Lexは、字句解析器(レクサー、スキャナーとも呼ばれる)を生成するためのツールです。Flex (Fast Lexical Analyzer) はLexのGNU版です。Lexは、正規表現に似た形式で記述された規則ファイル(通常 .l 拡張子を持つ)を読み込み、入力ストリームからトークンを識別するためのC言語のソースコード(字句解析器)を生成します。

  • yylval: Lexによって生成される字句解析器とBisonによって生成される構文解析器の間で、トークンのセマンティック値をやり取りするためのグローバル変数です。yylval の型は、Bisonの %union ディレクティブによって定義された YYSTYPE になります。字句解析器は、認識したトークンのセマンティック値を yylval に格納し、構文解析器はそれを取り出して利用します。

#define プリプロセッサディレクティブ

C言語における #define は、プリプロセッサディレクティブの一つで、マクロを定義するために使用されます。プリプロセッサは、コンパイルの前にソースコードを走査し、定義されたマクロ名をその定義内容で置換します。例えば、#define PI 3.14159 と定義されていれば、ソースコード中の PI はすべて 3.14159 に置換されます。この置換は単純なテキスト置換であり、C言語の構文解析よりも前に行われます。

今回のケースでは、Bisonが内部的に #define lint ... のようなマクロを定義している可能性があり、これが union のフィールド名 lint と衝突したと考えられます。

技術的詳細

このコミットの技術的な核心は、Bisonパーサジェネレータの内部的な動作と、C言語のプリプロセッサによるマクロ展開の相互作用にあります。

  1. %union の役割とフィールド名: %union ディレクティブは、パーサのセマンティック値を保持するための union 型を定義します。この union の各フィールドは、異なる種類のセマンティック値を表します。例えば、int lint; は整数型のセマンティック値を lint という名前で参照できるようにします。Bisonは、この %union の定義に基づいて YYSTYPE という名前のC言語の union 型を生成します。

  2. lint フィールド名とBisonの衝突: コミットメッセージが示唆するように、Bisonは内部的に lint という名前を特別な目的で使用している可能性があります。これは、Bisonが生成するCコード内で lint という名前のマクロ(例: #define lint some_value)を定義しているか、あるいは特定のコンパイル環境やリンティングツールとの互換性のために lint という識別子を予約しているかのいずれかです。 もしBisonが #define lint を行っている場合、go.y ファイルがBisonによって処理され、その結果生成されるCコードがCプリプロセッサによって処理される際に問題が発生します。具体的には、%union 内で定義された int lint; という行が、プリプロセッサによって lint のマクロ定義内容で置換されてしまい、結果として union のメンバー定義として不正なCコードが生成されることになります。これは、コンパイルエラーや、最悪の場合、意図しない動作を引き起こす可能性があります。

  3. LASOP トークンと lbrace, import_here: go.y ファイルの変更箇所を見ると、LASOP というトークンと、lbrace および import_here という非終端記号(または型)が、以前は lint フィールドをセマンティック値として使用するように指定されていました(例: %token <lint> LASOP)。これは、これらの要素が整数型のセマンティック値を持つことを意味します。衝突を避けるため、これらの参照も新しいフィールド名 i に変更されました。

  4. lex.c における yylval.lint の使用: lex.c は字句解析器のC言語ソースファイルであり、yylval グローバル変数を通じてトークンのセマンティック値を構文解析器に渡します。yylval.lint = c; という行は、字句解析器が認識した文字 cLASOP トークンのセマンティック値として yylvallint フィールドに格納していることを示しています。%union のフィールド名が変更されたため、この字句解析器側のコードも yylval.i = c; に更新する必要がありました。これにより、字句解析器と構文解析器の間でセマンティック値の受け渡しが正しく行われるようになります。

この変更は、GoコンパイラのビルドシステムがBisonとCプリプロセッサをどのように連携させているかを考慮した、低レベルながらも重要な修正です。特定の識別子がツールチェーンの異なるレイヤーで衝突する可能性を考慮し、堅牢なコンパイラを構築するためのベストプラクティスを示しています。

コアとなるコードの変更箇所

--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -31,13 +31,13 @@ static void fixlbrace(int);\n 	Type*		type;\n 	Sym*		sym;\n 	struct	Val	val;\n-\tint		lint;\n+\tint		i;\n }\n \n // |sed 's/.*	//' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx		/'\n \n %token	<val>	LLITERAL\n-%token	<lint>	LASOP\n+%token	<i>	LASOP\n %token	<sym>	LBREAK LCASE LCHAN LCOLAS LCONST LCONTINUE LDDD\n %token	<sym>	LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO\n %token	<sym>	LIF LIMPORT LINTERFACE LMAP LNAME\n@@ -47,7 +47,7 @@ static void fixlbrace(int);\n %token		LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT\n %token		LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH\n \n-%type	<lint>	lbrace import_here\n+%type	<i>	lbrace import_here\n %type	<sym>	sym packname\n %type	<val>\toliteral\n \ndiff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index cf7bbae9ed..3dbd6dda1a 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -1098,7 +1098,7 @@ lx:
 	return c;
 
 asop:
-\tyylval.lint = c;	// rathole to hold which asop
+\tyylval.i = c;	// rathole to hold which asop
 	DBG("lex: TOKEN ASOP %c\n", c);
 	return LASOP;
 

コアとなるコードの解説

src/cmd/gc/go.y の変更

このファイルはGoコンパイラの構文解析器の文法定義です。Bisonによって処理されます。

  • %union ディレクティブ内の変更:

    -	int		lint;
    +	int		i;
    

    %union ブロック内で定義されていた lint という名前の int 型フィールドが i に変更されました。これにより、Bisonが生成する YYSTYPE union 型のメンバー名が lint から i に変わります。

  • %token ディレクティブの変更:

    -%token	<lint>	LASOP
    +%token	<i>	LASOP
    

    LASOP というトークン(おそらく "assignment operator" の略)のセマンティック値が、以前は %unionlint フィールドに関連付けられていましたが、新しい i フィールドに関連付けられるように変更されました。

  • %type ディレクティブの変更:

    -%type	<lint>	lbrace import_here
    +%type	<i>	lbrace import_here
    

    lbraceimport_here という非終端記号(または型)のセマンティック値も、同様に %unionlint フィールドから i フィールドに関連付けられるように変更されました。

src/cmd/gc/lex.c の変更

このファイルはGoコンパイラの字句解析器のC言語ソースコードです。

  • yylval のフィールド参照の変更:
    -	yylval.lint = c;	// rathole to hold which asop
    +	yylval.i = c;	// rathole to hold which asop
    
    字句解析器が LASOP トークンを認識した際に、そのセマンティック値を yylval グローバル変数に格納する箇所です。%union のフィールド名が lint から i に変更されたため、yylval の参照も yylval.lint から yylval.i に更新されました。コメントの「rathole to hold which asop」は、このフィールドがどの代入演算子であるかを保持するための一時的な場所であることを示唆しています。

これらの変更は一貫しており、%union のフィールド名変更に伴う影響範囲全体をカバーしています。

関連リンク

参考にした情報源リンク