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

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

このコミットは、Go言語のコンパイラが定数(const)を関数であるかのように呼び出す不正なコードを正しく拒否するように修正するものです。具体的には、定数を関数呼び出しの構文(例: F())で参照した場合に、コンパイラがエラーを発生させるように変更されました。これにより、Go言語の型システムとセマンティクスがより厳密に適用されるようになります。また、この変更に伴い、コンパイラのテスト結果を記録する golden.out ファイルも更新されています。

コミット

commit d8ecead73d5c2862df08ed601c65682978e4a6cd
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Nov 18 06:25:21 2008 -0800

    The compiler should reject calling a const as though it were a
    function.  Also update golden.out.
    
    R=r
    DELTA=18  (18 added, 0 deleted, 0 changed)
    OCL=19433
    CL=19448

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

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

元コミット内容

The compiler should reject calling a const as though it were a
function.  Also update golden.out.

変更の背景

このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の初期開発段階でした。当時のGoコンパイラ(6gなど)は、言語仕様が固まりつつある中で、様々なエッジケースや不正な構文に対する挙動がまだ完全に洗練されていませんでした。

この特定のバグは、プログラマが誤って定数を関数のように呼び出そうとした場合に、コンパイラがそれをエラーとして検出できず、不正なコードをコンパイルしてしまう可能性があったことを示しています。Go言語は静的型付け言語であり、型安全性とコードの明確性を重視しています。定数はリテラル値や定数式に名前を付けたものであり、関数のように実行可能なエンティティではありません。したがって、このような誤用は言語のセマンティクスに反し、予期せぬ動作やバグを引き起こす原因となります。

このコミットは、Goコンパイラの堅牢性を高め、言語仕様に厳密に準拠した挙動を保証するために導入されました。開発の初期段階において、このような基本的な型チェックの厳密化は、将来的な言語の安定性と信頼性を築く上で非常に重要でした。

前提知識の解説

Go言語の定数(const

Go言語において、constキーワードは定数を宣言するために使用されます。定数はコンパイル時に値が決定され、プログラムの実行中に変更されることはありません。数値、真偽値、文字列などの基本的な型を持つことができます。

例:

const PI = 3.14159
const MaxConnections = 100
const Greeting = "Hello, Go!"

定数は、その値が不変であるという特性から、関数のように呼び出すことはできません。関数は実行可能なコードブロックであり、引数を受け取り、値を返すことができますが、定数にはそのような実行の概念がありません。

Go言語の関数呼び出し

Go言語における関数呼び出しは、関数名に続けて括弧 () を記述し、その中に引数を渡す形式で行われます。

例:

func add(a, b int) int {
    return a + b
}

result := add(5, 3) // 関数呼び出し

関数呼び出しの構文は、その識別子が関数であることをコンパイラに示します。

コンパイラの役割

コンパイラは、人間が書いたソースコードをコンピュータが理解できる機械語に変換するプログラムです。この変換プロセスにおいて、コンパイラは以下の重要な役割を担います。

  1. 字句解析(Lexical Analysis): ソースコードをトークン(キーワード、識別子、演算子など)の並びに分解します。
  2. 構文解析(Syntax Analysis): トークンの並びが言語の文法規則に従っているかを確認し、抽象構文木(AST)を構築します。この段階で、不正な構文(例: 括弧の不一致、予約語の誤用)が検出されます。
  3. 意味解析(Semantic Analysis): 構文的に正しいコードが、意味的にも正しいかを確認します。これには、型チェック(例: 整数型変数に文字列を代入しようとしていないか)、変数宣言の確認、関数の引数と戻り値の型の整合性チェックなどが含まれます。今回のコミットで修正されたのは、この意味解析の段階でのエラー検出能力の向上にあたります。
  4. 中間コード生成: 抽象構文木から、より抽象度の低い中間表現を生成します。
  5. コード最適化: 中間コードを最適化し、実行効率を高めます。
  6. コード生成: 最終的な機械語コードを生成します。

このコミットは、意味解析の段階で、定数を関数として呼び出すという「意味的に不正な」操作を検出できるようにコンパイラのロジックを強化したものです。

golden.out ファイル

Go言語のコンパイラ開発において、golden.out のようなファイルは、コンパイラのテストスイートの一部として使用される「ゴールデンファイル」または「リファレンス出力」と呼ばれるものです。これらのファイルには、特定の入力コードに対してコンパイラが生成すべき期待される出力(エラーメッセージ、警告、生成されたコードの特定の側面など)が記述されています。

テスト実行時、コンパイラはテストケースのコードを処理し、その出力を生成します。この生成された出力が golden.out ファイルに記述された期待される出力と一致するかどうかを比較することで、コンパイラの挙動が正しいか、または意図しない変更がないかを確認します。

このコミットで golden.out が更新されたのは、test/bugs/bug123.go という新しいテストケースが追加され、そのテストケースが特定のコンパイルエラーを発生させることを期待しているためです。つまり、bug123.go をコンパイルした際に、コンパイラが「定数を関数として呼び出している」というエラーを正しく出力するようになったことを golden.out に記録したわけです。

技術的詳細

このコミットの技術的詳細を理解するためには、Goコンパイラの内部動作、特に型チェックとエラー報告のメカニズムに焦点を当てる必要があります。

Goコンパイラは、ソースコードを解析する際に、各識別子(変数名、関数名、定数名など)が何を表しているのかをシンボルテーブルで管理します。シンボルテーブルには、識別子の名前、その型、スコープ、そしてそれが変数なのか、関数なのか、定数なのかといった「種類」の情報が格納されています。

問題となっていたのは、コンパイラが F() のような構文を見たときに、F が定数であることを認識しつつも、その後の () を関数呼び出しの構文として処理してしまい、エラーを適切に報告できていなかった点です。

修正の核心は、コンパイラの意味解析フェーズにおいて、関数呼び出しの構文 () が適用される識別子が、実際に呼び出し可能なエンティティ(関数、メソッド、または関数型を持つ変数など)であるかを厳密にチェックするロジックが追加または強化されたことです。

具体的には、test/bugs/bug123.goF() のようなコードが解析される際、コンパイラは以下のステップを踏みます。

  1. F という識別子を解決し、それが const であることをシンボルテーブルから取得します。
  2. 次に () という関数呼び出しの構文が続くことを検出します。
  3. ここで、コンパイラは F の種類が「定数」であり、「関数」ではないことを確認します。
  4. この不一致を検出した時点で、コンパイラは「定数を関数として呼び出すことはできない」というエラーを生成し、コンパイルを停止します。

この修正により、コンパイラは言語のセマンティクスに反する操作を早期に検出し、開発者に明確なエラーメッセージを提供できるようになりました。これは、Go言語の「エラーは早期に、明確に報告されるべき」という設計哲学にも合致しています。

test/golden.out の更新は、この新しいエラー報告の挙動をテストスイートに組み込むことを意味します。BUG: errchk: command succeeded unexpectedly: 6g bugs/bug123.go という記述は、errchk ツールが 6g bugs/bug123.go のコンパイルが成功することを期待していなかった(つまり、エラーが発生することを期待していた)にもかかわらず、コンパイルが成功してしまった、という過去のバグの状態を示しています。このコミットによって、このバグが修正され、6g bugs/bug123.go は期待通りエラーを出すようになったため、golden.out にその新しい期待されるエラーメッセージが反映されることになります。

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

このコミットでは、主に2つのファイルが変更されています。

  1. test/bugs/bug123.go (新規追加)
  2. test/golden.out (更新)

test/bugs/bug123.go

// errchk $G $D/$F.go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main
const ( F = 1 )
func fn(i int) int {
  if i == F() {		// ERROR "function"
    return 0
  }
  return 1
}

このファイルは、定数を関数として呼び出す不正なケースをテストするために新規作成されました。 // errchk $G $D/$F.go は、このファイルがコンパイルエラーを発生させることを期待するテストディレクティブです。 const ( F = 1 ) で定数 F を宣言しています。 if i == F() { ... } の行で、定数 F を関数であるかのように F() と呼び出しています。 // ERROR "function" コメントは、この行で「function」という文字列を含むエラーメッセージがコンパイラから出力されることを期待していることを示しています。

test/golden.out

--- a/test/golden.out
+++ b/test/golden.out
@@ -157,6 +157,15 @@ Bad float64 const: 1e23+1 want 1.0000000000000001e+23 got 1e+23
 \twant exact: 100000000000000008388608
 \tgot exact:  99999999999999991611392
 
+=========== bugs/bug121.go
+BUG: compilation succeeds incorrectly
+
+=========== bugs/bug122.go
+BUG: compilation succeeds incorrectly
+
+=========== bugs/bug123.go
+BUG: errchk: command succeeded unexpectedly:  6g bugs/bug123.go
+
 =========== fixedbugs/bug016.go
 fixedbugs/bug016.go:7: overflow converting constant to uint
 \

このファイルは、コンパイラのテスト結果の期待値を記録するものです。 追加されたセクションは、bugs/bug123.go のテストケースに関するものです。 BUG: errchk: command succeeded unexpectedly: 6g bugs/bug123.go という行は、このコミットが適用される前の状態、つまり 6g bugs/bug123.go をコンパイルした際に、errchk ツールが期待するエラーが出ずにコンパイルが成功してしまったというバグの状況を記録しています。このコミットによってこのバグが修正され、6g bugs/bug123.go は期待通りエラーを出すようになったため、この golden.out のエントリは、その修正が正しく行われたことを検証するためのものとなります。

実際のコンパイラのコード変更は、このコミットの差分には含まれていませんが、このテストケースが追加されたことで、コンパイラ内部の型チェックロジックが修正されたことが示唆されます。Go言語の初期のコミットでは、関連するコード変更が別のコミット(OCL/CL番号で示される)で行われ、そのテストケースが後続のコミットで追加される、というワークフローが一般的でした。

コアとなるコードの解説

このコミット自体は、Goコンパイラのソースコード(src/cmd/6g など)に直接的な変更を加えていません。代わりに、新しいテストケース test/bugs/bug123.go を追加し、そのテストが期待する結果を test/golden.out に記録することで、コンパイラの修正が正しく行われたことを検証しています。

したがって、このコミットの「コアとなるコードの変更箇所」は、Goコンパイラ自体のコードではなく、その挙動を検証するためのテストコードとテスト結果の定義ファイルです。

test/bugs/bug123.goif i == F() { // ERROR "function" } の行が、このコミットの意図を最も明確に示しています。この行は、Goコンパイラが F() という構文を解析する際に、F が定数であることを認識し、それが関数ではないため、関数呼び出しの構文 () を適用することは不正であると判断し、エラーを発生させるべきであることを示唆しています。

このテストケースの追加は、コンパイラ開発における「テスト駆動開発」のようなアプローチを示しています。まず、期待されるエラー挙動を示すテストケースを作成し、そのテストが失敗することを確認します(つまり、バグが存在することを確認します)。次に、コンパイラのコードを修正してテストが成功するようにします(つまり、バグを修正します)。最後に、テストが成功したことを確認し、その結果を golden.out に記録します。

このコミットは、Go言語のコンパイラが、言語仕様の厳密な解釈に基づいて、不正な型使用を早期に検出する能力を向上させた重要なマイルストーンの一つと言えます。

関連リンク

参考にした情報源リンク

  • Go言語のGitHubリポジトリ: https://github.com/golang/go
  • Go言語の初期開発に関する情報(Goの歴史など)
  • コンパイラの設計に関する一般的な情報(字句解析、構文解析、意味解析など)
  • Go言語のテストフレームワークやテストディレクティブに関する情報(errchkなど)

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

このコミットは、Go言語のコンパイラが定数(const)を関数であるかのように呼び出す不正なコードを正しく拒否するように修正するものです。具体的には、定数を関数呼び出しの構文(例: F())で参照した場合に、コンパイラがエラーを発生させるように変更されました。これにより、Go言語の型システムとセマンティクスがより厳密に適用されるようになります。また、この変更に伴い、コンパイラのテスト結果を記録する golden.out ファイルも更新されています。

コミット

commit d8ecead73d5c2862df08ed601c65682978e4a6cd
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Nov 18 06:25:21 2008 -0800

    The compiler should reject calling a const as though it were a
    function.  Also update golden.out.
    
    R=r
    DELTA=18  (18 added, 0 deleted, 0 changed)
    OCL=19433
    CL=19448

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

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

元コミット内容

The compiler should reject calling a const as though it were a
function.  Also update golden.out.

変更の背景

このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の初期開発段階でした。当時のGoコンパイラ(6gなど)は、言語仕様が固まりつつある中で、様々なエッジケースや不正な構文に対する挙動がまだ完全に洗練されていませんでした。

この特定のバグは、プログラマが誤って定数を関数のように呼び出そうとした場合に、コンパイラがそれをエラーとして検出できず、不正なコードをコンパイルしてしまう可能性があったことを示しています。Go言語は静的型付け言語であり、型安全性とコードの明確性を重視しています。定数はリテラル値や定数式に名前を付けたものであり、関数のように実行可能なエンティティではありません。したがって、このような誤用は言語のセマンティクスに反し、予期せぬ動作やバグを引き起こす原因となります。

このコミットは、Goコンパイラの堅牢性を高め、言語仕様に厳密に準拠した挙動を保証するために導入されました。開発の初期段階において、このような基本的な型チェックの厳密化は、将来的な言語の安定性と信頼性を築く上で非常に重要でした。

前提知識の解説

Go言語の定数(const

Go言語において、constキーワードは定数を宣言するために使用されます。定数はコンパイル時に値が決定され、プログラムの実行中に変更されることはありません。数値、真偽値、文字列などの基本的な型を持つことができます。

例:

const PI = 3.14159
const MaxConnections = 100
const Greeting = "Hello, Go!"

定数は、その値が不変であるという特性から、関数のように呼び出すことはできません。関数は実行可能なコードブロックであり、引数を受け取り、値を返すことができますが、定数にはそのような実行の概念がありません。

Go言語の関数呼び出し

Go言語における関数呼び出しは、関数名に続けて括弧 () を記述し、その中に引数を渡す形式で行われます。

例:

func add(a, b int) int {
    return a + b
}

result := add(5, 3) // 関数呼び出し

関数呼び出しの構文は、その識別子が関数であることをコンパイラに示します。

コンパイラの役割

コンパイラは、人間が書いたソースコードをコンピュータが理解できる機械語に変換するプログラムです。この変換プロセスにおいて、コンパイラは以下の重要な役割を担います。

  1. 字句解析(Lexical Analysis): ソースコードをトークン(キーワード、識別子、演算子など)の並びに分解します。
  2. 構文解析(Syntax Analysis): トークンの並びが言語の文法規則に従っているかを確認し、抽象構文木(AST)を構築します。この段階で、不正な構文(例: 括弧の不一致、予約語の誤用)が検出されます。
  3. 意味解析(Semantic Analysis): 構文的に正しいコードが、意味的にも正しいかを確認します。これには、型チェック(例: 整数型変数に文字列を代入しようとしていないか)、変数宣言の確認、関数の引数と戻り値の型の整合性チェックなどが含まれます。今回のコミットで修正されたのは、この意味解析の段階でのエラー検出能力の向上にあたります。
  4. 中間コード生成: 抽象構文木から、より抽象度の低い中間表現を生成します。
  5. コード最適化: 中間コードを最適化し、実行効率を高めます。
  6. コード生成: 最終的な機械語コードを生成します。

このコミットは、意味解析の段階で、定数を関数として呼び出すという「意味的に不正な」操作を検出できるようにコンパイラのロジックを強化したものです。

golden.out ファイル

Go言語のコンパイラ開発において、golden.out のようなファイルは、コンパイラのテストスイートの一部として使用される「ゴールデンファイル」または「リファレンス出力」と呼ばれるものです。これらのファイルには、特定の入力コードに対してコンパイラが生成すべき期待される出力(エラーメッセージ、警告、生成されたコードの特定の側面など)が記述されています。

テスト実行時、コンパイラはテストケースのコードを処理し、その出力を生成します。この生成された出力が golden.out ファイルに記述された期待される出力と一致するかどうかを比較することで、コンパイラの挙動が正しいか、または意図しない変更がないかを確認します。

このコミットで golden.out が更新されたのは、test/bugs/bug123.go という新しいテストケースが追加され、そのテストケースが特定のコンパイルエラーを発生させることを期待しているためです。つまり、bug123.go をコンパイルした際に、コンパイラが「定数を関数として呼び出している」というエラーを正しく出力するようになったことを golden.out に記録したわけです。

技術的詳細

このコミットの技術的詳細を理解するためには、Goコンパイラの内部動作、特に型チェックとエラー報告のメカニズムに焦点を当てる必要があります。

Goコンパイラは、ソースコードを解析する際に、各識別子(変数名、関数名、定数名など)が何を表しているのかをシンボルテーブルで管理します。シンボルテーブルには、識別子の名前、その型、スコープ、そしてそれが変数なのか、関数なのか、定数なのかといった「種類」の情報が格納されています。

問題となっていたのは、コンパイラが F() のような構文を見たときに、F が定数であることを認識しつつも、その後の () を関数呼び出しの構文として処理してしまい、エラーを適切に報告できていなかった点です。

修正の核心は、コンパイラの意味解析フェーズにおいて、関数呼び出しの構文 () が適用される識別子が、実際に呼び出し可能なエンティティ(関数、メソッド、または関数型を持つ変数など)であるかを厳密にチェックするロジックが追加または強化されたことです。

具体的には、test/bugs/bug123.goF() のようなコードが解析される際、コンパイラは以下のステップを踏みます。

  1. F という識別子を解決し、それが const であることをシンボルテーブルから取得します。
  2. 次に () という関数呼び出しの構文が続くことを検出します。
  3. ここで、コンパイラは F の種類が「定数」であり、「関数」ではないことを確認します。
  4. この不一致を検出した時点で、コンパイラは「定数を関数として呼び出すことはできない」というエラーを生成し、コンパイルを停止します。

この修正により、コンパイラは言語のセマンティクスに反する操作を早期に検出し、開発者に明確なエラーメッセージを提供できるようになりました。これは、Go言語の「エラーは早期に、明確に報告されるべき」という設計哲学にも合致しています。

test/golden.out の更新は、この新しいエラー報告の挙動をテストスイートに組み込むことを意味します。BUG: errchk: command succeeded unexpectedly: 6g bugs/bug123.go という記述は、errchk ツールが 6g bugs/bug123.go のコンパイルが成功することを期待していなかった(つまり、エラーが発生することを期待していた)にもかかわらず、コンパイルが成功してしまった、という過去のバグの状態を示しています。このコミットによって、このバグが修正され、6g bugs/bug123.go は期待通りエラーを出すようになったため、golden.out にその新しい期待されるエラーメッセージが反映されることになります。

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

このコミットでは、主に2つのファイルが変更されています。

  1. test/bugs/bug123.go (新規追加)
  2. test/golden.out (更新)

test/bugs/bug123.go

// errchk $G $D/$F.go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main
const ( F = 1 )
func fn(i int) int {
  if i == F() {		// ERROR "function"
    return 0
  }
  return 1
}

このファイルは、定数を関数として呼び出す不正なケースをテストするために新規作成されました。 // errchk $G $D/$F.go は、このファイルがコンパイルエラーを発生させることを期待するテストディレクティブです。 const ( F = 1 ) で定数 F を宣言しています。 if i == F() { ... } の行で、定数 F を関数であるかのように F() と呼び出しています。 // ERROR "function" コメントは、この行で「function」という文字列を含むエラーメッセージがコンパイラから出力されることを期待していることを示しています。

test/golden.out

--- a/test/golden.out
+++ b/test/golden.out
@@ -157,6 +157,15 @@ Bad float64 const: 1e23+1 want 1.0000000000000001e+23 got 1e+23
 \twant exact: 100000000000000008388608
 \tgot exact:  99999999999999991611392
 
+=========== bugs/bug121.go
+BUG: compilation succeeds incorrectly
+
+=========== bugs/bug122.go
+BUG: compilation succeeds incorrectly
+
+=========== bugs/bug123.go
+BUG: errchk: command succeeded unexpectedly:  6g bugs/bug123.go
+
 =========== fixedbugs/bug016.go
 fixedbugs/bug016.go:7: overflow converting constant to uint
 \

このファイルは、コンパイラのテスト結果の期待値を記録するものです。 追加されたセクションは、bugs/bug123.go のテストケースに関するものです。 BUG: errchk: command succeeded unexpectedly: 6g bugs/bug123.go という行は、このコミットが適用される前の状態、つまり 6g bugs/bug123.go をコンパイルした際に、errchk ツールが期待するエラーが出ずにコンパイルが成功してしまったというバグの状況を記録しています。このコミットによってこのバグが修正され、6g bugs/bug123.go は期待通りエラーを出すようになったため、この golden.out のエントリは、その修正が正しく行われたことを検証するためのものとなります。

実際のコンパイラのコード変更は、このコミットの差分には含まれていませんが、このテストケースが追加されたことで、コンパイラ内部の型チェックロジックが修正されたことが示唆されます。Go言語の初期のコミットでは、関連するコード変更が別のコミット(OCL/CL番号で示される)で行われ、そのテストケースが後続のコミットで追加される、というワークフローが一般的でした。

コアとなるコードの解説

このコミット自体は、Goコンパイラのソースコード(src/cmd/6g など)に直接的な変更を加えていません。代わりに、新しいテストケース test/bugs/bug123.go を追加し、そのテストが期待する結果を test/golden.out に記録することで、コンパイラの修正が正しく行われたことを検証しています。

したがって、このコミットの「コアとなるコードの変更箇所」は、Goコンパイラ自体のコードではなく、その挙動を検証するためのテストコードとテスト結果の定義ファイルです。

test/bugs/bug123.goif i == F() { // ERROR "function" } の行が、このコミットの意図を最も明確に示しています。この行は、Goコンパイラが F() という構文を解析する際に、F が定数であることを認識し、それが関数ではないため、関数呼び出しの構文 () を適用することは不正であると判断し、エラーを発生させるべきであることを示唆しています。

このテストケースの追加は、コンパイラ開発における「テスト駆動開発」のようなアプローチを示しています。まず、期待されるエラー挙動を示すテストケースを作成し、そのテストが失敗することを確認します(つまり、バグが存在することを確認します)。次に、コンパイラのコードを修正してテストが成功するようにします(つまり、バグを修正します)。最後に、テストが成功したことを確認し、その結果を golden.out に記録します。

このコミットは、Go言語のコンパイラが、言語仕様の厳密な解釈に基づいて、不正な型使用を早期に検出する能力を向上させた重要なマイルストーンの一つと言えます。

関連リンク

参考にした情報源リンク

  • Go言語のGitHubリポジトリ: https://github.com/golang/go
  • Go言語の初期開発に関する情報(Goの歴史など)
  • コンパイラの設計に関する一般的な情報(字句解析、構文解析、意味解析など)
  • Go言語のテストフレームワークやテストディレクティブに関する情報(errchkなど)