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

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

このコミットは、Goコンパイラのソースコードの一部である src/cmd/gc/go.y ファイルに対する変更です。go.y はGo言語の構文解析器の定義ファイルであり、この変更はGoのパッケージインポートに関する特定のケースを処理するために行われました。具体的には、package main をインポートしようとした場合に、より明確なエラーメッセージを生成するようにコンパイラの動作を修正しています。

コミット

commit 39436f2a747bc1c64f50c8be46cd9cf801576ba8
Author: Russ Cox <rsc@golang.org>
Date:   Thu Apr 2 21:46:19 2009 -0700

    special case check for this situation
    
            ; cat >http.go
            package main
            import "http"  // intended the library, not this file
            ^D
            ; 6g http.go
            ; 6g http.go
            http.go:4: export/package mismatch: init
            ;
    
    new error:
    
            http.6:7 http.go:3: cannot import package main
    
    R=ken
    OCL=27053
    CL=27053

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

https://github.com/golang/go/commit/39436f2a747bc1c64f50c8be46cd9cf801576ba8

元コミット内容

special case check for this situation

        ; cat >http.go
        package main
        import "http"  // intended the library, not this file
        ^D
        ; 6g http.go
        ; 6g http.go
        http.go:4: export/package mismatch: init
        ;

new error:

        http.6:7 http.go:3: cannot import package main

R=ken
OCL=27053
CL=27053

変更の背景

このコミットは、Goコンパイラが特定の状況で誤解を招くエラーメッセージを出す問題を解決するために導入されました。具体的には、ユーザーが http.go という名前のファイルを作成し、そのファイル内で package main を宣言しつつ、Go標準ライブラリの http パッケージをインポートしようとした場合に問題が発生していました。

当時のGoコンパイラ(6g)は、ファイル名とインポートパスの解決において、特定の内部ロジックやバグにより、import "http" を現在の package main である http.go 自体をインポートしようと誤解釈してしまうことがありました。これにより、http.go:4: export/package mismatch: init といった、ユーザーにとっては理解しにくいエラーメッセージが表示されていました。

このコミットの目的は、このような曖昧な状況を解消し、package main をインポートしようとすること自体が不正であることを明確に伝える、より分かりやすいエラーメッセージ (cannot import package main) を提供することです。これにより、開発者は問題の原因を迅速に特定できるようになります。

前提知識の解説

Goのパッケージシステム

Go言語は、コードを整理し、再利用するための強力なパッケージシステムを持っています。

  • package main: Goにおいて特別な意味を持つパッケージです。これは実行可能なプログラムのエントリポイントとして機能し、main 関数を含みます。package main は、他のGoパッケージからインポートされることを意図していません。Goプログラムは、main パッケージから実行を開始します。
  • その他のパッケージ: main 以外のパッケージ(例: package http, package fmt)は、再利用可能なコードの集合体として設計されています。これらのパッケージは、他のGoプログラムやライブラリからインポートして利用することができます。

Goのimport文

Goの import 文は、他のパッケージの機能(関数、変数、型など)を現在のファイルで利用可能にするために使用されます。 例: import "fmt" は、標準ライブラリの fmt パッケージをインポートし、fmt.Println() のようにその機能を利用できるようにします。

Goコンパイラ (6g)

このコミットが作成された2009年当時、Go言語のコンパイラは、ターゲットアーキテクチャに基づいて命名されていました。

  • 6g: amd64 (64-bit x86) アーキテクチャ向けのコンパイラでした。
  • 8g: 386 (32-bit x86) アーキテクチャ向け。
  • 5g: arm アーキテクチャ向け。

Go 1.5以降、これらのアーキテクチャ固有のコンパイラ名は非推奨となり、現在は統一された compile ツールが使用されています。現代のGo開発では、通常 go build コマンドを使用し、コンパイルプロセスは自動的に処理されます。

Yacc/Bisonとgo.y

  • Yacc (Yet Another Compiler Compiler) / Bison: これらは、形式文法に基づいて構文解析器(パーサー)を生成するためのツールです。プログラミング言語のコンパイラやインタープリタの構築によく使用されます。
  • go.y: Goコンパイラのソースコード内で、Go言語の構文規則を定義しているYacc/Bisonの文法ファイルです。このファイルは、Goのソースコードがどのように解析され、抽象構文木(AST)が構築されるかを記述しています。コンパイラは go.y の定義に基づいて、Goのコードが文法的に正しいかどうかを検証します。

技術的詳細

このコミットの技術的な核心は、Goコンパイラの構文解析フェーズにおける import 文の処理にあります。go.y ファイルは、Go言語の文法を定義しており、コンパイラはこれに従ってソースコードを解析します。

問題となっていたのは、import "http" のような記述があった際に、コンパイラがインポートしようとしている "http" が、標準ライブラリの http パッケージなのか、それとも現在のファイル(例: http.go)が属する package main なのかを誤って判断してしまう可能性があった点です。特に、ファイル名がインポートしようとしているパッケージ名と一致する場合に、この曖昧さが発生しやすかったと考えられます。

package main は実行可能プログラムのエントリポイントであり、他のパッケージからインポートされるべきではありません。この設計原則に反するインポートが行われた場合、コンパイラはそれを検出し、適切なエラーを報告する必要があります。

このコミットでは、go.y 内の import_package ルールに明示的なチェックを追加することで、この問題を解決しています。これにより、コンパイラはインポートされるパッケージ名が "main" であるかどうかを直接確認し、もしそうであれば、より具体的で分かりやすいエラーメッセージを生成するようになります。この変更は、コンパイラの堅牢性を高め、開発者がGoのパッケージシステムのルールをより正確に理解するのに役立ちます。

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

--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -173,6 +173,9 @@ import_package:
 	{
 		pkgimportname = $2;
 
+		if(strcmp($2->name, "main") == 0)
+			yyerror("cannot import package main");
+
 		// if we are not remapping the package name
 		// then the imported package name is LPACK
 		if(pkgmyname == S)

コアとなるコードの解説

このコード変更は、src/cmd/gc/go.y ファイル内の import_package という構文規則のセクションに追加されました。

  • import_package: は、Go言語の import 文を解析するためのYacc/Bisonのルールの一部です。
  • { ... } ブロック内は、このルールがマッチしたときに実行されるC言語のコードです。
  • pkgimportname = $2;
    • $2 は、Yacc/Bisonの文法において、import_package ルールの2番目の要素(通常はインポートされるパッケージ名)を指します。この行は、インポートされるパッケージ名を pkgimportname 変数に代入しています。
  • if(strcmp($2->name, "main") == 0)
    • ここが今回の変更の核心です。
    • strcmp はC言語の標準ライブラリ関数で、2つの文字列を比較します。
    • $2->name は、インポートされるパッケージの名前(文字列)にアクセスしています。
    • strcmp($2->name, "main") == 0 は、「インポートしようとしているパッケージの名前が "main" と完全に一致するかどうか」をチェックしています。strcmp は文字列が一致する場合に 0 を返します。
  • yyerror("cannot import package main");
    • 上記の if 文の条件が真(つまり、package main をインポートしようとしている)の場合に実行されます。
    • yyerror はYacc/Bisonによって提供される関数で、コンパイルエラーメッセージを生成するために使用されます。
    • この行は、cannot import package main という明確なエラーメッセージを出力し、コンパイルプロセスを停止させます。

この変更により、コンパイラは import "main" のような不正なインポートを早期に検出し、開発者に対してより分かりやすいエラーメッセージを提供するようになりました。

関連リンク

  • Effective Go - Packages: Goのパッケージに関する公式ドキュメント。パッケージの設計原則と利用方法について説明されています。 https://go.dev/doc/effective_go#packages
  • Go Modules: 現代のGoにおけるパッケージ管理の標準的な方法。このコミットの時点では存在しませんでしたが、Goのパッケージエコシステムの進化を理解する上で重要です。 https://go.dev/blog/using-go-modules

参考にした情報源リンク

  • Web検索: "Go cannot import package main": package main がインポートできない理由と、Goのパッケージ構造に関する一般的な情報。
  • Web検索: "Go 6g compiler": 2009年当時のGoコンパイラの命名規則と、その後のGoツールチェーンの進化に関する情報。