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

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

このコミットは、Goコンパイラの内部におけるエラー報告メカニズムの改善と、特定のバグの修正に焦点を当てています。特に、多倍長整数演算ライブラリ(mparith)におけるエラー報告を、一般的な警告(warn)から、より構文解析エラーに近いyyerrorへと変更しています。これにより、コンパイラが検出した問題の性質がより明確に伝えられるようになります。また、Go言語の初期の文法に関する2つのバグ(bug108.gobug125.go)が修正され、関連するテストケースがtest/bugsからtest/fixedbugsディレクトリに移動されています。

コミット

commit 5fbadf0bc375d63049c210b640dd6384b7ca890b
Author: Russ Cox <rsc@golang.org>
Date:   Thu Mar 12 19:57:30 2009 -0700

    warn -> yyerror in mparith.
    close two more bugs.
    
    R=ken
    OCL=26226
    CL=26226

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

https://github.com/golang/go/commit/5fbadf0bc375d63049c210b640dd6384b7ca890b

元コミット内容

warn -> yyerror in mparith.
close two more bugs.

R=ken
OCL=26226
CL=26226

変更の背景

このコミットが行われた2009年3月は、Go言語がまだ一般に公開される前の開発初期段階にあたります。この時期は、言語仕様の策定、コンパイラの安定化、そして基本的な機能の実装が活発に行われていました。

変更の背景には、以下の点が挙げられます。

  1. エラー報告の厳密化: コンパイラが検出するエラーの種類をより正確に分類し、ユーザー(開発者)に対して適切なフィードバックを提供する必要がありました。特に、多倍長整数演算におけるオーバーフローなどのエラーは、単なる警告ではなく、コンパイルを続行できない重大な問題として扱うべきであるという判断があったと考えられます。warnは通常、コンパイルを中断しない軽微な問題に対して使用されますが、yyerrorは構文解析エラーなど、コンパイルの続行が困難な場合に用いられます。この変更は、コンパイラの堅牢性とエラーメッセージの品質向上を目指したものです。
  2. 初期バグの修正: 開発初期には、言語の文法やコンパイラの挙動に関する多くのバグが存在します。このコミットでは、特にGo言語のimport文の構文解析と、シフト演算における定数オーバーフローに関する2つの具体的なバグが修正されました。これらのバグは、Go言語の基本的な機能に影響を与えるものであり、早期の修正が求められました。
  3. テストインフラの整備: バグが修正されたことに伴い、関連するテストケースがtest/bugsからtest/fixedbugsディレクトリに移動されました。これは、修正済みのバグに対する回帰テストを管理するための、より体系的なテストインフラの構築の一環と考えられます。

前提知識の解説

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

1. Goコンパイラの構造

Goコンパイラ(gc)は、Go言語のソースコードを機械語に変換するツールチェーンの一部です。その内部では、字句解析、構文解析、意味解析、最適化、コード生成といった複数のフェーズを経て処理が行われます。

  • go.y: このファイルは、Go言語の構文を定義するYacc(またはBison)の文法ファイルです。Yaccは、文法定義に基づいてパーサー(構文解析器)を生成するツールです。go.yは、Go言語のキーワード、演算子、文の構造などを定義し、ソースコードがGo言語の文法に準拠しているかをチェックします。
  • mparith*.c: これらのファイルは、Goコンパイラが内部で使用する多倍長整数(multiple-precision arithmetic)演算ライブラリの実装です。Go言語では、コンパイル時に定数式を評価する際に、非常に大きな数値や高精度な浮動小数点数を扱う必要があります。このライブラリは、標準のCPUレジスタに収まらないような大きな整数や、浮動小数点数の計算を正確に行うために使用されます。例えば、1 << 1025のようなシフト演算は、通常の整数型では表現できない巨大な数になるため、このような多倍長整数演算が必要になります。

2. warnyyerror

Goコンパイラの初期段階では、エラー報告のためにwarnyyerrorという2つの異なるメカニズムが使用されていました。

  • warn: これは一般的な警告メッセージを出力するための関数です。通常、warnが呼び出されてもコンパイル処理は続行されます。軽微な問題や、将来的に非推奨となる可能性のある構文などに対して使用されます。
  • yyerror: これはYacc/Bisonによって生成されたパーサーが構文エラーを検出した際に呼び出される標準的なエラー報告関数です。yyerrorが呼び出されると、通常はコンパイル処理が中断され、エラーメッセージが表示されます。これは、ソースコードが言語の文法規則に違反していることを示す、より重大なエラーです。

このコミットでは、mparithライブラリ内で発生する特定の条件(例: オーバーフロー)が、単なる警告ではなく、コンパイルを続行すべきではない構文上の問題として扱われるべきであるという判断から、warnからyyerrorへの変更が行われました。

3. Go言語のimport

Go言語のimport文は、他のパッケージの機能を利用するために使用されます。初期のGo言語の文法は現在とは異なり、開発の過程で変更が加えられていました。特に、import文のリストにおけるセミコロンの扱いは、このコミットで修正されたバグの一つです。

4. シフト演算と定数オーバーフロー

Go言語では、定数式はコンパイル時に評価されます。シフト演算(<<, >>)も定数式として評価されることがあります。例えば、1 << 1025のような非常に大きなシフト量は、結果が通常の整数型では表現できないほど巨大な数になるため、オーバーフローが発生します。Goコンパイラは、このような定数式のオーバーフローを検出し、エラーとして報告する必要があります。

技術的詳細

このコミットの技術的な変更点は多岐にわたりますが、主要なものは以下の通りです。

1. warnからyyerrorへの変更

src/cmd/gc/mparith1.c, src/cmd/gc/mparith2.c, src/cmd/gc/mparith3.cの各ファイルにおいて、多倍長整数演算に関連するエラー条件で呼び出されていたwarn関数がyyerror関数に置き換えられました。

具体的な変更箇所は以下の通りです。

  • mparith1.c: mpatof(文字列から浮動小数点数への変換)およびmpatov(文字列から整数への変換)におけるオーバーフロー検出時のエラー報告。
  • mparith2.c:
    • mpcmp(比較)におけるオーバーフロー。
    • mpaddfixfix(整数加算)におけるオーバーフロー。
    • mpmulfixfix(整数乗算)およびmpmulfract(浮動小数点数乗算)におけるオーバーフロー。
    • mporfixfix, mpandfixfix, mpandnotfixfix, mpxorfixfix(ビット演算)におけるオーバーフロー。
    • mplshfixfix(左シフト)およびmprshfixfix(右シフト)におけるオーバーフロー、および「愚かなシフト量」(stupid shift)の検出。
    • mpgetfix(多倍長整数から固定長整数への変換)におけるオーバーフロー。
    • mpdivmodfixfix(整数除算と剰余)におけるオーバーフロー。
  • mparith3.c:
    • mpdivfltflt(浮動小数点数除算)におけるゼロ除算。
    • mpgetflt(多倍長浮動小数点数から固定長浮動小数点数への変換)におけるオーバーフローおよび正規化(norm)の問題。

この変更により、これらのエラーはコンパイル時に即座に報告され、コンパイルが中断されるようになりました。これは、コンパイラがより厳密にエラーを扱い、開発者が早期に問題を特定できるようにするための重要な改善です。

2. go.yにおける文法変更

src/cmd/gc/go.yファイルには、Go言語の文法定義に関する2つの重要な変更が含まれています。

  • LPACKAGE { warn("package is gone"); } xfndclの削除: Go言語の初期には、packageキーワードが現在の位置とは異なる文脈で使用される可能性があったか、あるいはその使用法が変更されたことを示す警告が発せられていたようです。この行の削除は、packageキーワードの文法的な曖昧さが解消されたか、あるいはその特定の文脈での使用が完全に廃止されたことを意味します。これにより、パーサーの複雑性が軽減され、文法がより明確になりました。
  • import_stmt_list_r osemi import_stmtからimport_stmt_list_r ';' import_stmtへの変更: これは、Go言語のimport文のリストにおけるセミコロンの扱いに関する変更です。osemiは「optional semicolon」(オプションのセミコロン)を意味すると考えられます。この変更は、import文のリストの各エントリが明示的にセミコロンで区切られることを要求するように文法が変更されたことを示唆しています。Go言語の文法では、通常、文の終わりにセミコロンは不要ですが、複数の文を1行に記述する場合や、特定の文脈ではセミコロンが必要になることがあります。この変更は、import文の解析をより堅牢にするためのものです。

3. バグ修正とテストケースの移動

test/bugs/bug108.gotest/bugs/bug125.goの2つのテストファイルがtest/fixedbugsディレクトリに移動されました。これは、これらのバグが修正され、回帰テストとして維持されるべきであることを示しています。

  • bug108.go: このテストは、v := 1 << 1025;というコード行を含んでいます。これは、1を1025ビット左にシフトするという操作です。この結果は非常に大きな数となり、通常の整数型では表現できません。元のテストでは// ERROR "overflow"というコメントがあり、オーバーフローエラーが期待されていました。このコミットでは、期待されるエラーメッセージが// ERROR "overflow|stupid shift"に変更されました。これは、コンパイラが単にオーバーフローを報告するだけでなく、「愚かなシフト量」という、より具体的な診断メッセージを出力するようになったことを示しています。これは、開発者にとって問題の原因を特定しやすくするための改善です。
  • bug125.go: このテストは、import ( OS "os" // should require semicolon here; this is no different from other decls IO "io" // ERROR "missing" )というコードを含んでいます。元のテストでは、IO "io"の行で// ERROR "missing"というエラーが期待されていました。これは、OS "os"の後にセミコロンがないことによる構文エラーを示唆しています。このコミットでは、期待されるエラーメッセージが// ERROR "missing|syntax"に変更されました。これは、エラーが単に「不足している」だけでなく、より広範な「構文エラー」として認識されるようになったことを示しています。これは、go.yにおけるimport文の文法変更と密接に関連しています。

test/golden.outファイルからは、これらのバグに関連する古いエラー出力が削除されています。これは、修正によってこれらのテストが期待通りに動作し、エラーが正しく報告されるようになったことを確認するためです。

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

src/cmd/gc/go.y

--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -215,12 +215,6 @@ xdcl:
 		autoexport($1->nname->sym);
 		$$ = N;
 	}
-|	LPACKAGE { warn("package is gone"); } xfndcl
-	{
-		if($3 != N && $3->nname != N)
-			packagesym($3->nname->sym);
-		$$ = N;
-	}
 |	';'
 	{
 		$$ = N;
@@ -1660,7 +1654,7 @@ exprsym3_list_r:
 
 import_stmt_list_r:
 	import_stmt
-|	import_stmt_list_r osemi import_stmt
+|	import_stmt_list_r ';' import_stmt
 
 hidden_import_list_r:
 |	hidden_import_list_r hidden_import

src/cmd/gc/mparith1.c

--- a/src/cmd/gc/mparith1.c
+++ b/src/cmd/gc/mparith1.c
@@ -308,7 +308,7 @@ out:
 	return;
 
 bad:
-	warn("set ovf in mpatof");
+	yyerror("set ovf in mpatof");
 	mpmovecflt(a, 0.0);
 }
 
@@ -396,7 +396,7 @@ out:
 	return;
 
 bad:
-	warn("set ovf in mpatov: %s", as);
+	yyerror("set ovf in mpatov: %s", as);
 	mpmovecfix(a, 0);
 }

src/cmd/gc/mparith2.c

--- a/src/cmd/gc/mparith2.c
+++ b/src/cmd/gc/mparith2.c
@@ -119,7 +119,7 @@ mpcmp(Mpint *a, Mpint *b)
 	int i;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in cmp");
+		yyerror("ovf in cmp");
 		return 0;
 	}
 
@@ -194,7 +194,7 @@ mpaddfixfix(Mpint *a, Mpint *b)
 	long x, *a1, *b1;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mpaddxx");
+		yyerror("ovf in mpaddxx");
 		a->ovf = 1;
 		return;
 	}
@@ -217,7 +217,7 @@ mpaddfixfix(Mpint *a, Mpint *b)
 	}
 	a->ovf = c;
 	if(a->ovf)
-		warn("set ovf in mpaddxx");
+		yyerror("set ovf in mpaddxx");
 
 	return;
 
@@ -264,7 +264,7 @@ mpmulfixfix(Mpint *a, Mpint *b)
 	Mpint s, q;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mpmulfixfix");
+		yyerror("ovf in mpmulfixfix");
 		a->ovf = 1;
 		return;
 	}
@@ -297,7 +297,7 @@ mpmulfixfix(Mpint *a, Mpint *b)
 	q.neg = a->neg ^ b->neg;
 	mpmovefixfix(a, &q);
 	if(a->ovf)
-		warn("set ovf in mpmulfixfix");
+		yyerror("set ovf in mpmulfixfix");
 }
 
 void
@@ -309,7 +309,7 @@ mpmulfract(Mpint *a, Mpint *b)
 	Mpint s, q;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mpmulflt");
+		yyerror("ovf in mpmulflt");
 		a->ovf = 1;
 		return;
 	}
@@ -336,7 +336,7 @@ mpmulfract(Mpint *a, Mpint *b)
 	q.neg = a->neg ^ b->neg;
 	mpmovefixfix(a, &q);
 	if(a->ovf)
-		warn("set ovf in mpmulflt");
+		yyerror("set ovf in mpmulflt");
 }
 
 void
@@ -346,7 +346,7 @@ mporfixfix(Mpint *a, Mpint *b)
 	long x, *a1, *b1;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mporfixfix");
+		yyerror("ovf in mporfixfix");
 		mpmovecfix(a, 0);
 		a->ovf = 1;
 		return;
@@ -380,7 +380,7 @@ mpandfixfix(Mpint *a, Mpint *b)
 	long x, *a1, *b1;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mpandfixfix");
+		yyerror("ovf in mpandfixfix");
 		mpmovecfix(a, 0);
 		a->ovf = 1;
 		return;
@@ -414,7 +414,7 @@ mpandnotfixfix(Mpint *a, Mpint *b)
 	long x, *a1, *b1;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mpandnotfixfix");
+		yyerror("ovf in mpandnotfixfix");
 		mpmovecfix(a, 0);
 		a->ovf = 1;
 		return;
@@ -448,7 +448,7 @@ mpxorfixfix(Mpint *a, Mpint *b)
 	long x, *a1, *b1;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mporfixfix");
+		yyerror("ovf in mporfixfix");
 		mpmovecfix(a, 0);
 		a->ovf = 1;
 		return;
@@ -481,14 +481,14 @@ mplshfixfix(Mpint *a, Mpint *b)
 	vlong s;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mporfixfix");
+		yyerror("ovf in mporfixfix");
 		mpmovecfix(a, 0);
 		a->ovf = 1;
 		return;
 	}
 	s = mpgetfix(b);
 	if(s < 0 || s >= Mpprec*Mpscale) {
-		warn("stupid shift: %lld", s);
+		yyerror("stupid shift: %lld", s);
 		mpmovecfix(a, 0);
 		return;
 	}
@@ -502,14 +502,14 @@ mprshfixfix(Mpint *a, Mpint *b)
 	vlong s;
 
 	if(a->ovf || b->ovf) {
-		warn("ovf in mprshfixfix");
+		yyerror("ovf in mprshfixfix");
 		mpmovecfix(a, 0);
 		a->ovf = 1;
 		return;
 	}
 	s = mpgetfix(b);
 	if(s < 0 || s >= Mpprec*Mpscale) {
-		warn("stupid shift: %lld", s);
+		yyerror("stupid shift: %lld", s);
 		if(a->neg)
 			mpmovecfix(a, -1);
 		else
@@ -532,7 +532,7 @@ mpgetfix(Mpint *a)
 	vlong v;
 
 	if(a->ovf) {
-		warn("ovf in mpgetfix");
+		yyerror("ovf in mpgetfix");
 		return 0;
 	}
 
@@ -588,7 +588,7 @@ mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d)
 	if(i >= Mpprec*Mpscale) {
 		q->ovf = 1;
 		r->ovf = 1;
-		warn("set ovf in mpdivmodfixfix");
+		yyerror("set ovf in mpdivmodfixfix");
 		return;
 	}

src/cmd/gc/mparith3.c

--- a/src/cmd/gc/mparith3.c
+++ b/src/cmd/gc/mparith3.c
@@ -134,7 +134,7 @@ mpdivfltflt(Mpflt *a, Mpflt *b)
 		a->exp = 0;
 		a->val.neg = 0;
 		a->val.ovf = 1;
-		warn("mpdivfltflt divide by zero");
+		yyerror("mpdivfltflt divide by zero");
 		return;
 	}
 	if(sa == 0) {
@@ -165,14 +165,14 @@ mpgetflt(Mpflt *a)
 	double f;
 
 	if(a->val.ovf)
-		warn("mpgetflt ovf");
+		yyerror("mpgetflt ovf");
 
 	s = sigfig(a);
 	if(s == 0)
 		return 0;
 
 	if(s != Mpnorm) {
-		warn("mpgetflt norm");
+		yyerror("mpgetflt norm");
 		mpnorm(a);
 	}

test/bugs/bug108.go (rename to test/fixedbugs/bug108.go)

--- a/test/bugs/bug108.go
+++ b/test/fixedbugs/bug108.go
@@ -6,5 +6,5 @@
 
 package main
 func f() {
-	v := 1 << 1025;		// ERROR "overflow"
+	v := 1 << 1025;		// ERROR "overflow|stupid shift"
 }

test/bugs/bug125.go (rename to test/fixedbugs/bug125.go)

--- a/test/bugs/bug125.go
+++ b/test/fixedbugs/bug125.go
@@ -8,7 +8,7 @@ package main
 
 import (
 	OS "os"  // should require semicolon here; this is no different from other decls
-	IO "io"  // ERROR "missing"
+	IO "io"  // ERROR "missing|syntax"
 )
 
 func main() {

コアとなるコードの解説

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

  • LPACKAGE関連の削除: LPACKAGE { warn("package is gone"); } xfndclという行は、Go言語の初期の文法解析において、packageキーワードが特定の文脈で出現した場合に「package is gone」という警告を発していた部分です。この行が削除されたということは、その特定の文脈でのpackageキーワードの扱いに変更があり、もはやその警告が不要になったことを意味します。これは、Go言語の文法がより安定し、packageキーワードの役割が明確になったことを示唆しています。
  • import_stmt_list_rの変更: import_stmt_list_r osemi import_stmtからimport_stmt_list_r ';' import_stmtへの変更は、import文のリストにおける区切り文字の扱いを明確にしています。osemiは「optional semicolon」(オプションのセミコロン)を意味するYaccの記法である可能性が高いです。この変更により、import文の各エントリ間には明示的にセミコロン(;)が必要となるように文法が変更されたことを示しています。これは、パーサーの曖昧さを減らし、文法解析をより堅牢にするための典型的な変更です。

src/cmd/gc/mparith*.cの変更

これらのファイルにおけるwarnからyyerrorへの変更は、多倍長整数演算ライブラリmparith内で発生するエラーの重要度を引き上げるものです。

  • warnyyerrorの役割:
    • warnは、コンパイルを中断しない軽微な問題や、情報提供のためのメッセージに使用されます。
    • yyerrorは、構文解析エラーなど、コンパイルの続行が不可能な重大な問題に対して使用され、通常はコンパイルプロセスを停止させます。
  • 変更の意図: mparithライブラリ内で検出されるオーバーフロー、ゼロ除算、不正なシフト量などのエラーは、単なる警告ではなく、プログラムのセマンティクスに重大な影響を与えるものです。これらのエラーがwarnとして扱われると、コンパイルは成功するものの、実行時に予期せぬ結果を引き起こす可能性があります。yyerrorに変更することで、コンパイラはこれらの問題をコンパイル時に厳密に検出し、開発者に修正を促すようになります。これにより、Goプログラムの信頼性と安全性が向上します。特に、stupid shift(愚かなシフト量)というメッセージは、シフト量が非常に大きすぎて意味をなさない場合など、特定の不正な操作をより具体的に指摘するために導入されたと考えられます。

test/bugsからtest/fixedbugsへの移動とテスト内容の変更

  • ディレクトリ移動: test/bugsは、まだ修正されていない既知のバグに対するテストケースを格納する場所であったと考えられます。test/fixedbugsは、修正されたバグに対する回帰テストを格納する場所です。この移動は、bug108.gobug125.goで示される問題が解決されたことを意味します。
  • bug108.goの変更: v := 1 << 1025;というコードに対する期待されるエラーメッセージが"overflow"から"overflow|stupid shift"に変更されました。これは、mparith2.cmplshfixfix関数におけるwarn("stupid shift: %lld", s);yyerror("stupid shift: %lld", s);に変更されたことと直接関連しています。コンパイラが、単なる数値のオーバーフローだけでなく、シフト量が非現実的に大きいという、より具体的な診断を提供するようになったことを示しています。
  • bug125.goの変更: import文の構文エラーに対する期待されるエラーメッセージが"missing"から"missing|syntax"に変更されました。これは、go.yにおけるimport文の文法変更と関連しており、コンパイラがより一般的な「構文エラー」として問題を認識するようになったことを示しています。これは、エラーメッセージの汎用性と正確性を向上させるための変更です。

これらの変更は、Goコンパイラの初期開発段階における、エラー報告の精度向上、文法解析の堅牢化、およびバグ修正の体系化に向けた重要なステップを示しています。

関連リンク

参考にした情報源リンク