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

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

このコミットは、Go言語のコンパイラ(gc)における型変換、型アサーション、および複合リテラルの構文解析と内部表現に関する重要な変更を導入しています。具体的には、T{x}(複合リテラル)、T(x)(型変換)、x.(T)(型アサーション)の間の厳密な区別を強化し、それぞれの操作に対応する新しい内部ノードタイプを導入することで、コンパイラの堅牢性と正確性を向上させています。特に、OCONVDOTOCONVPARENという汎用的なノードを廃止し、より意味論的に明確なODOTTYPEOCOMPOSを導入することで、コンパイラがこれらの構文をより正確に処理できるようにしています。

コミット

commit 49cc649e5925e3917b7102665364c5568b1959c4
Author: Russ Cox <rsc@golang.org>
Date:   Tue Mar 3 08:41:02 2009 -0800

    back to T{x}, stricter handling of T(x) vs x.(T)
    
    R=ken
    DELTA=131  (60 added, 41 deleted, 30 changed)
    OCL=25617
    CL=25633

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

https://github.com/golang/go/commit/49cc649e5925e3917b7102665364c5568b1959c4

元コミット内容

back to T{x}, stricter handling of T(x) vs x.(T)

R=ken
DELTA=131  (60 added, 41 deleted, 30 changed)
OCL=25617
CL=25633

変更の背景

Go言語の初期開発段階において、型変換、型アサーション、および複合リテラルの構文は進化の途上にありました。このコミット以前は、T(x)のような構文が型変換と複合リテラルの両方に使用されるなど、曖昧さが存在していました。特に、T(x)x.(T)、そしてT{x}の間の意味論的な区別がコンパイラ内部で十分に厳密に扱われていなかった可能性があります。

コミットメッセージにある「back to T{x}」は、以前の変更でT{x}形式の複合リテラルが一時的に廃止され、T(x)のような形式に統合されていた可能性を示唆しています。しかし、この統合は構文の曖昧さを生み、特にif x == []int { true } { true }のようなコードにおいて、{が複合リテラルの開始なのか、それともif文のブロック開始なのかを区別する際に、シフト/リデュースコンフリクト(shift/reduce conflict)を引き起こす問題がありました。

このコミットの目的は、これらの構文の曖昧さを解消し、コンパイラがより正確にコードを解析できるようにすることです。具体的には、以下の点を改善しています。

  1. 構文の明確化: 複合リテラルにはT{x}形式を再度採用し、型変換にはT(x)、型アサーションにはx.(T)を厳密に適用することで、構文レベルでの曖昧さを排除します。
  2. 内部ノードの分離: コンパイラの抽象構文木(AST)において、これらの異なる操作を表現するための専用のノードタイプ(ODOTTYPEOCOMPOS)を導入し、OCONVDOTOCONVPARENといった汎用的なノードを廃止します。これにより、コンパイラのセマンティック分析フェーズで、各操作をより正確に識別し、処理できるようになります。
  3. シフト/リデュースコンフリクトの解決: go.y(Yacc/Bisonの文法定義ファイル)において、if文の条件式と複合リテラルの{}の間のコンフリクトを解決するための優先順位ルール(%prec Condition)を導入しています。

これらの変更により、Goコンパイラはより堅牢になり、開発者はより明確な構文でコードを記述できるようになりました。

前提知識の解説

このコミットを理解するためには、Go言語における以下の概念と、コンパイラの基本的な動作に関する知識が必要です。

1. Go言語の構文要素

  • 複合リテラル (Composite Literals): Go言語では、構造体、配列、スライス、マップなどの複合型を初期化するために複合リテラルを使用します。初期のGoでは、Type{value1, value2}のような形式が一般的でした。例えば、Point{1, 2}Point構造体のインスタンスを作成します。このコミットでは、T{x}形式が複合リテラルの標準として再確立されています。

  • 型変換 (Type Conversions): Goは静的型付け言語であり、異なる型間で値を変換するには明示的な型変換が必要です。構文はType(expression)です。例えば、float64(i)は整数ifloat64型に変換します。

  • 型アサーション (Type Assertions): 型アサーションは、インターフェース型の値が特定の具象型を保持しているかどうかをチェックし、その具象型の値を取り出すために使用されます。構文はvalue.(Type)です。例えば、myVar.(string)myVarstring型を保持しているかをアサートします。安全なアサーションのためには、result, ok := value.(Type)という「カンマOKイディオム」がよく使われます。

2. コンパイラの基本的な動作

  • 字句解析 (Lexical Analysis): ソースコードをトークン(単語)のストリームに変換するフェーズ。
  • 構文解析 (Syntax Analysis / Parsing): トークンのストリームを解析し、言語の文法規則に従って抽象構文木(AST)を構築するフェーズ。Yacc/Bisonのようなパーサジェネレータが使用されることが多いです。
  • 抽象構文木 (Abstract Syntax Tree - AST): ソースコードの構造を木構造で表現したもの。コンパイラの後のフェーズ(セマンティック分析、コード生成など)で利用されます。
  • セマンティック分析 (Semantic Analysis): ASTを走査し、型チェック、名前解決、意味論的なエラー検出などを行うフェーズ。
  • 中間表現 (Intermediate Representation - IR): コンパイラ内部でコードを表現するための形式。ASTのノードは、このIRの操作に対応することが多いです。
  • Yacc/Bison: LALRパーサジェネレータ。文法規則(.yファイル)からC言語のパーサコードを生成します。
    • %type: 非終端記号のセマンティック値の型を宣言します。
    • %left, %right, %nonassoc: 演算子の結合性と優先順位を定義します。
    • %prec: 特定の規則に、別のトークンや非終端記号の優先順位を強制的に適用します。これはシフト/リデュースコンフリクトを解決するためによく使用されます。
    • シフト/リデュースコンフリクト (Shift/Reduce Conflict): パーサが次のアクションとして「シフト」(入力トークンをスタックに積む)と「リデュース」(スタック上の記号を文法規則に従って非終端記号に置き換える)のどちらを行うべきか決定できない状況。

3. Goコンパイラ (gc) の内部構造(2009年頃)

Go言語の初期のコンパイラは、gc(Go Compiler)と呼ばれ、C言語で実装されていました。

  • go.h: コンパイラ全体で共有されるヘッダファイル。ASTノードのタイプ(enumで定義されるONODEなど)やその他の定数が含まれます。
  • go.y: Go言語の文法定義ファイル。Yacc/Bisonによって解析され、Goソースコードの構文解析ロジックが生成されます。
  • subr.c: 補助関数やユーティリティ関数が含まれるファイル。ASTノードのタイプ名を文字列に変換するopnames配列などもここに定義されます。
  • walk.c: ASTを走査し、セマンティック分析や最適化、中間コード生成などを行う「ウォーカー」のロジックが含まれるファイル。各ASTノードタイプに対応する処理が記述されています。

このコミットは、これらのファイルにわたる変更を通じて、Go言語の構文解析とセマンティック分析の精度を高めています。

技術的詳細

このコミットの技術的詳細は、主にGoコンパイラのフロントエンドにおける構文解析と抽象構文木(AST)の構造変更に集約されます。

1. ASTノードタイプの変更 (src/cmd/gc/go.h, src/cmd/gc/subr.c)

以前は、型アサーション(x.(T))と複合リテラル(T(x)またはT{x})を表現するために、OCONVDOTOCONVPARENという汎用的なASTノードタイプが使用されていました。このコミットでは、これらを廃止し、より意味論的に特化した新しいノードタイプを導入しています。

  • ODOTTYPE (Old: OCONVDOT):

    • go.hOCONVDOTが削除され、ODOTTYPEが追加されました。
    • subr.copnames配列でも同様にOCONVDOTが削除され、ODOTTYPEが追加されています。
    • これは、x.(T)のような型アサーションを表現するためのノードです。名前が示す通り、ドット(.)と型(TYPE)を含む構文に対応します。
  • OCOMPOS (Old: OCONVPAREN):

    • go.hOCONVPARENが削除され、OCOMPOSが追加されました。
    • subr.copnames配列でも同様にOCONVPARENが削除され、OCOMPOSが追加されています。
    • これは、T{x}のような複合リテラルを表現するためのノードです。COMPOSは「複合」を意味し、構造体、配列、マップなどの複合型の初期化に使用されるリテラルを指します。

これらの変更により、コンパイラはASTの段階で、型アサーションと複合リテラルを明確に区別できるようになり、後続のセマンティック分析やコード生成フェーズでの処理が簡素化され、正確性が向上します。

2. 構文解析規則の変更とコンフリクト解決 (src/cmd/gc/go.y)

go.yファイルでは、Go言語の文法規則が定義されています。このコミットでは、特にif文の条件式と複合リテラルの構文に関する曖昧さを解消するための変更が行われています。

  • for_headerif_header の変更:

    • 以前はfor_headerif_headerの条件部分にosimple_stmt(単純なステートメント)が直接許可されていました。
    • このコミットでは、osimple_stmtの代わりに新しい非終端記号conditionが導入されました。
    • conditionosimple_stmtをラップし、%prec Conditionという優先順位ルールが適用されています。
  • condition 非終端記号と %prec Condition:

    • condition: osimple_stmt %prec Condition
    • この新しい規則は、if x == []int { true } { true }のような入力があった場合に発生するシフト/リデュースコンフリクトを解決するために導入されました。
    • %prec Conditionは、condition規則にConditionというトークンの優先順位を強制的に適用します。これにより、パーサは{を見たときに、それが複合リテラルの開始なのか、if文のブロック開始なのかを判断する際に、Conditionの優先順位を考慮するようになります。具体的には、{if文の条件式の一部として解釈されることを防ぎ、複合リテラルとして解釈されるためには括弧が必要であるというルール(if x == ([]int { true }) { true }のように)を強制します。
  • pexpr (Primary Expression) の変更:

    • 型アサーションの構文: pexpr '.' '(' type ')' の規則が、$$ = nod(OCONVDOT, $1, N); から $$ = nod(ODOTTYPE, $1, N); に変更されました。これは、前述のASTノードタイプの変更に対応しています。
    • 型変換と複合リテラルの構文: 以前は convtype '(' braced_keyexpr_list ')'convtype '{' braced_keyexpr_list '}' の両方が OCONVPAREN ノードを生成していました。 このコミットでは、
      • convtype '(' expr ')'OCONV ノードを生成するように変更されました。これは一般的な型変換(例: int(f))に対応します。
      • convtype '{' braced_keyexpr_list '}'OCOMPOS ノードを生成するように変更されました。これは複合リテラル(例: []int{1, 2, 3})に対応します。
      • これにより、T(x)T{x}がそれぞれ異なるASTノードにマッピングされ、コンパイラ内部での区別が明確になります。

3. ASTウォーカーの変更 (src/cmd/gc/walk.c)

walk.cファイルには、ASTを走査してセマンティック分析や中間コード生成を行うロジックが含まれています。新しいASTノードタイプが導入されたため、それらを適切に処理するようにwalk関数内のswitch文が更新されています。

  • OCONVDOT から ODOTTYPE への変更:

    • case OCONVDOT:case ODOTTYPE: に変更されました。
    • walkconv関数内でも、if(op == OCONVDOT) のチェックが if(n->op == ODOTTYPE) に変更され、インターフェースアサーションのロジックがODOTTYPEノードに紐付けられました。
  • OCONVPAREN の削除と OCOMPOS の追加:

    • OCONVPARENに関連する処理が削除され、代わりに新しい case OCOMPOS: ブロックが追加されました。
    • OCOMPOSノードの処理では、その型(構造体、配列、マップ)に応じて、structlitarraylitmaplitといった対応するリテラル処理関数が呼び出されるようになりました。これにより、複合リテラルの初期化ロジックがOCOMPOSノードに集約され、より明確になりました。
  • OADDR (アドレス取得) 処理の変更:

    • &Point(1, 2)のような構文(これは複合リテラルのアドレス取得)の処理が変更されました。
    • 以前は n->left->op == OCONVPAREN でチェックされていましたが、これが n->left->op == OCOMPOS に変更されました。
    • これにより、複合リテラルのアドレス取得がOCOMPOSノードに正しくマッピングされ、newによるアロケーションと初期化のロジックが適用されるようになりました。

これらの変更は、Go言語の構文がより厳密に定義され、コンパイラがその意味をより正確に解釈できるようにするための基盤を築きました。特に、構文の曖昧さによるパーサのコンフリクトを解決し、コンパイラの内部表現をよりセマンティックにすることで、将来の機能拡張や最適化の余地を広げています。

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

src/cmd/gc/go.h

--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -327,9 +327,9 @@ enum
 	ONOT, OCOM, OPLUS, OMINUS, OSEND, ORECV,
 	OLITERAL, OREGISTER, OINDREG,
 	OKEY, OPARAM,
+	OCOMPOS,
 	OCONV,
-	OCONVDOT,
-	OCONVPAREN,
+	ODOTTYPE,
 	OBAD,
 
 	OEXTEND,	// 6g internal

src/cmd/gc/go.y

--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -89,6 +89,13 @@
 %left			'+' '-' '|' '^'
 %left			'*' '/' '%' '&' LLSH LRSH
 
+/*
+ * resolve { vs condition in favor of condition
+ */
+%left			'{'
+%left			Condition
+
+
 %%\n file:
 	package import_there imports oxdcl_list
 	{
@@ -562,7 +569,7 @@ for_header:
 		$$->ntest = $3;
 		$$->nincr = $5;
 	}
-|	osimple_stmt
+|	condition
 	{
 		// normal test
 		$$ = nod(OFOR, N, N);
@@ -591,15 +598,30 @@ for_stmt:
 		$$ = $2;
 	}
 
+/*
+ * using cond instead of osimple_stmt creates
+ * a shift/reduce conflict on an input like
+ *
+ *	if x == []int { true } { true }
+ *
+ * at the first {, giving us an opportunity
+ * to resolve it by reduce, which implements
+ * the rule about { } inside if conditions
+ * needing parens.
+ */
+condition:
+	osimple_stmt	%prec Condition
+
+
 if_header:
-	osimple_stmt
+	condition
 	{
 		// test
 		$$ = nod(OIF, N, N);
 		$$->ninit = N;
 		$$->ntest = $1;
 	}
-|	osimple_stmt ';' osimple_stmt
+|	osimple_stmt ';' condition
 	{
 		// init ; test
 		$$ = nod(OIF, N, N);
@@ -791,7 +813,7 @@ pexpr:
 	}
 |	pexpr '.' '(' type ')'
 	{
-		$$ = nod(OCONVDOT, $1, N);
+		$$ = nod(ODOTTYPE, $1, N);
 		$$->type = $4;
 	}
 |	pexpr '[' expr ']'
@@ -841,24 +863,22 @@ pexpr:
 		$$ = nod(OMAKE, $5, N);
 		$$->type = $3;
 	}
-|	convtype '(' braced_keyexpr_list ')'
+|	convtype '(' expr ')'
 	{
-		// typed literal
+		// conversion
 		$$ = rev($3);
 		if($$ == N)
 			$$ = nod(OEMPTY, N, N);
-		$$ = nod(OCONVPAREN, $$, N);
+		$$ = nod(OCONV, $$, N);
 		$$->type = $1;
 	}
 |	convtype '{' braced_keyexpr_list '}'
 	{
-		if(!debug['{'])
-			warn("braces should now be parens");
-		// composite literal
+		// composite expression
 		$$ = rev($3);
 		if($$ == N)
 			$$ = nod(OEMPTY, N, N);
-		$$ = nod(OCONVPAREN, $$, N);
+		$$ = nod(OCOMPOS, $$, N);
 		$$->type = $1;
 	}
 |	fnliteral

src/cmd/gc/subr.c

--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -634,9 +634,9 @@ opnames[] =
 	[OXCASE]	= "XCASE",
 	[OCMP]		= "CMP",
 	[OFALL]		= "FALL",
+	[OCOMPOS]	= "COMPOS",
+	[ODOTTYPE]		= "DOTTYPE",
 	[OCONV]	= "CONV",
-	[OCONVDOT]		= "CONVDOT",
-	[OCONVPAREN]	= "CONVPAREN",
 	[OCOM]		= "COM",
 	[OCONST]	= "CONST",
 	[OCONTINUE]	= "CONTINUE",

src/cmd/gc/walk.c

--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -510,7 +510,7 @@ loop:
 			}
 			break;
 
-		case OCONVDOT:
+		case ODOTTYPE:
 			if(cl == 2 && cr == 1) {
 				// a,b = i.(T)
 				walktype(r->left, Erv);
@@ -593,13 +593,41 @@ loop:
 		goto ret;
 
 	case OCONV:
-	case OCONVDOT:
-	case OCONVPAREN:
+	case ODOTTYPE:
 		if(top != Erv)
 			goto nottop;
 		walkconv(n);
 		goto ret;
 
+	case OCOMPOS:
+		t = n->type;
+		if(t == T)
+			goto ret;
+		l = n->left;
+		if(l == N)
+			goto ret;
+		walktype(l, Erv);
+	
+		// structure literal
+		if(t->etype == TSTRUCT) {
+			indir(n, structlit(n, N));
+			goto ret;
+		}
+
+		// array literal
+		if(t->etype == TARRAY) {
+			indir(n, arraylit(n, N));
+			goto ret;
+		}
+
+		// map literal
+		if(t->etype == TMAP) {
+			indir(n, maplit(n, N));
+			goto ret;
+		}
+		yyerror("invalid type for composite literal: %T", t);
+		goto ret;
+
 	case ORETURN:
 		if(top != Etop)
 			goto nottop;
@@ -887,11 +915,7 @@ loop:
 	case OADDR:
 		if(top != Erv)
 			goto nottop;
-		if(n->left->op == OCONVPAREN && n->left->type != T)
-		switch(n->left->type->etype) {
-		case TSTRUCT:
-		case TARRAY:
-		case TMAP:
+		if(n->left->op == OCOMPOS && n->left->type != T) {
 			// turn &Point(1, 2) or &[]int(1, 2) or &[...]int(1, 2) into allocation.
 			// initialize with
 			//	nvar := new(*Point);
@@ -920,13 +944,14 @@ loop:
 				maplit(n->left, nstar);
 				break;
 			default:
-				fatal("addr lit %T", n->left->type);
+				goto badlit;
 			}
 
 			indir(n, nvar);
 			goto ret;
 		}
 
+	badlit:
 		if(istype(n->left->type, TFUNC) && n->left->class == PFUNC) {
 			if(!n->diag) {
 				n->diag = 1;
@@ -1130,10 +1155,12 @@ walkbool(Node *n)
 			yyerror("IF and FOR require a boolean type");
 }
 
+\n+\n void
 walkconv(Node *n)
 {
-\tint et, op;\n+\tint et;\n 	Type *t;\n 	Node *l;\n \n@@ -1145,17 +1172,20 @@ walkconv(Node *n)\n 		return;\n 	walktype(l, Erv);\n \n-\tswitch(t->etype) {\n-\tcase TSTRUCT:\n-\tcase TMAP:\n-\tcase TARRAY:\n-\t\tbreak;\n-\tdefault:\n-\t\tconvlit1(l, t, 1);\n+\tconvlit1(l, t, 1);\n+\n+\t// if using .(T), interface assertion.\n+\tif(n->op == ODOTTYPE) {\n+\t\t// interface conversion\n+\t\tet = ifaceas(n->type, l->type, 1);\n+\t\tif(et != Inone) {\n+\t\t\tindir(n, ifaceop(n->type, l, et));\n+\t\t\treturn;\n+\t\t}\n+\t\tgoto bad;\n \t}\n \n-\top = n->op;\n-\tn->op = OCONV;\t// generic conversion\n+\t// otherwise, conversion.\n \n 	// nil conversion\n 	if(eqtype(t, l->type, 0)) {

コアとなるコードの解説

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

  • OCONVDOTOCONVPAREN の削除: これらは以前、型アサーションと複合リテラルの両方をカバーする汎用的なASTノードタイプでした。しかし、その汎用性ゆえに意味論的な曖昧さやコンパイラ内部での処理の複雑さを引き起こしていました。
  • OCOMPOSODOTTYPE の追加:
    • OCOMPOS: 複合リテラル(例: []int{1, 2, 3}struct{a int}{1})を明示的に表現するための新しいノードタイプです。COMPOSは「複合」を意味し、複合型の初期化に特化しています。
    • ODOTTYPE: 型アサーション(例: x.(int))を明示的に表現するための新しいノードタイプです。DOTTYPEは「ドットと型」を意味し、x.(T)の構文に直接対応します。 この変更により、ASTの段階で構文要素の意味がより明確になり、コンパイラのセマンティック分析フェーズでの処理が簡素化され、正確性が向上します。

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

  • 優先順位ルールの追加:

    %left			'{'
    %left			Condition
    

    これは、{Conditionif文などの条件式)の間の優先順位を定義しています。%leftは左結合性を示しますが、ここでは主に優先順位の宣言として機能します。Conditionの優先順位を{よりも高く設定することで、if x == []int { true } { true }のような曖昧な構文において、パーサが{を複合リテラルの開始ではなく、if文のブロック開始と誤解するのを防ぎます。これにより、複合リテラルが条件式内で使用される場合は、if x == ([]int { true }) { true }のように明示的に括弧で囲む必要があるというGoの文法規則が強制されます。

  • for_headerif_headerosimple_stmt から condition への変更: forループやif文のヘッダ部分で、以前は直接osimple_stmt(単純なステートメント)が許可されていましたが、これをconditionという新しい非終端記号に置き換えました。

    -|	osimple_stmt
    +|	condition
    

    そして、conditionは以下のように定義されます。

    condition:
    	osimple_stmt	%prec Condition
    

    この変更は、前述のシフト/リデュースコンフリクトを解決するためのものです。condition%prec Conditionを適用することで、if文の条件式が複合リテラルの{}と競合する際に、condition規則が優先されるようにパーサの動作を誘導します。

  • pexpr (Primary Expression) の変更:

    • 型アサーションのノード変更:
      -|		$$ = nod(OCONVDOT, $1, N);
      +|		$$ = nod(ODOTTYPE, $1, N);
      
      pexpr . ( type ) という構文(型アサーション)が、OCONVDOTノードではなく、新しく導入されたODOTTYPEノードを生成するように変更されました。これにより、型アサーションがASTで明確に識別されます。
    • 型変換と複合リテラルの分離:
      -|	convtype '(' braced_keyexpr_list ')'
      +|	convtype '(' expr ')'
       	{
      -|		// typed literal
      +|		// conversion
       		$$ = rev($3);
       		if($$ == N)
       			$$ = nod(OEMPTY, N, N);
      -|		$$ = nod(OCONVPAREN, $$, N);
      +|		$$ = nod(OCONV, $$, N);
       		$$->type = $1;
       	}
      -|	convtype '{' braced_keyexpr_list '}'
      +|	convtype '{' braced_keyexpr_list '}'
       	{
      -|		if(!debug['{'])
      -|			warn("braces should now be parens");
      -|		// composite literal
      +|		// composite expression
       		$$ = rev($3);
       		if($$ == N)
       			$$ = nod(OEMPTY, N, N);
      -|		$$ = nod(OCONVPAREN, $$, N);
      +|		$$ = nod(OCOMPOS, $$, N);
       		$$->type = $1;
       	}
      
      convtype ( ... ) の構文が、以前はOCONVPARENを生成していましたが、一般的な型変換(例: int(f))を表すOCONVノードを生成するように変更されました。 convtype { ... } の構文(複合リテラル)は、OCONVPARENではなく、新しく導入されたOCOMPOSノードを生成するように変更されました。 この分離により、T(x)T{x}という異なる構文が、それぞれ異なる意味を持つASTノードにマッピングされ、コンパイラがこれらを正確に区別して処理できるようになります。

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

  • opnames 配列の更新: opnames配列は、ASTノードのタイプに対応する文字列名を定義しています。OCONVDOTOCONVPARENが削除され、OCOMPOSODOTTYPEが追加されました。これは、デバッグやエラーメッセージの表示において、新しいノードタイプが正しく識別されるようにするための変更です。

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

  • walk 関数内の switch 文の更新:

    • case OCONVDOT:case ODOTTYPE: に変更されました。これにより、型アサーションの処理ロジックが新しいODOTTYPEノードに紐付けられます。
    • OCONVPAREN に関連する処理が削除され、代わりに新しい case OCOMPOS: ブロックが追加されました。このブロックでは、OCOMPOSノードの型(構造体、配列、マップ)に基づいて、structlitarraylitmaplitといった適切なリテラル初期化関数が呼び出されます。これは、複合リテラルの具体的な初期化処理をOCOMPOSノードに集約するものです。
  • OADDR (アドレス取得) 処理の変更:

    -|		if(n->left->op == OCONVPAREN && n->left->type != T)
    -|		switch(n->left->type->etype) {
    -|		case TSTRUCT:
    -|		case TARRAY:
    -|		case TMAP:
    +|		if(n->left->op == OCOMPOS && n->left->type != T) {
    

    &Point{1, 2}のような複合リテラルのアドレスを取得する構文の処理が変更されました。以前はOCONVPARENノードをチェックしていましたが、これがOCOMPOSノードをチェックするように変更されました。これにより、複合リテラルのアドレス取得が正しくOCOMPOSノードにマッピングされ、newによるアロケーションと初期化のロジックが適用されるようになります。

  • walkconv 関数の変更: walkconv関数は型変換の処理を担当します。

    -|	case OCONV:
    -|	case OCONVDOT:
    -|	case OCONVPAREN:
    +|	case OCONV:
    +|	case ODOTTYPE:
    

    switch文からOCONVDOTOCONVPARENが削除され、ODOTTYPEが追加されました。 また、if(n->op == ODOTTYPE)というチェックが追加され、ODOTTYPEノードの場合にインターフェースアサーションのロジック(ifaceas, ifaceop)が実行されるようになりました。これにより、型アサーションのセマンティックな処理がODOTTYPEノードに特化されます。

これらの変更は、Goコンパイラの内部で、異なる構文要素がより正確に、かつ意味論的に一貫した方法で表現され、処理されるようにするための重要なステップでした。これにより、コンパイラの堅牢性が向上し、Go言語の構文がより明確に定義されることにつながりました。

関連リンク

  • Go言語の初期の設計に関する議論やドキュメントは、Goプロジェクトの公式リポジトリやメーリングリストのアーカイブで確認できる場合があります。
  • Go言語の文法に関する公式ドキュメント: https://go.dev/ref/spec

参考にした情報源リンク