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

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

このコミットは、Go言語の標準ライブラリであるgo/docパッケージにおいて、nilを事前宣言された定数(predeclared constants)のリストに追加する変更です。これにより、go/docパッケージがGoのソースコードを解析し、ドキュメントを生成する際に、nilが正しく組み込みの定数として認識されるようになります。具体的には、go/doc/reader.go内のpredeclaredConstantsマップにnilが追加され、それに伴いgo/doc/example.go内のplayExample関数におけるnilの特別扱いが不要になったため、関連する条件式が簡略化されています。

コミット

commit ce6acefc5d9049762066cad6c7cac1378de4544e
Author: Andrew Gerrand <adg@golang.org>
Date:   Thu Oct 4 08:37:48 2012 +1000

    go/doc: add nil to list of predeclared constants
    
    R=gri
    CC=gobot, golang-dev
    https://golang.org/cl/6601054

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

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

元コミット内容

diff --git a/src/pkg/go/doc/example.go b/src/pkg/go/doc/example.go
index dc899351a6..79053b9a73 100644
--- a/src/pkg/go/doc/example.go
+++ b/src/pkg/go/doc/example.go
@@ -138,7 +138,7 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.BlockStmt {
 
 	// Remove predeclared identifiers from unresolved list.
 	for n := range unresolved {
-		if n == "nil" || predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
+		if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
 			delete(unresolved, n)
 		}
 	}
diff --git a/src/pkg/go/doc/reader.go b/src/pkg/go/doc/reader.go
index f0860391f6..de42d47d9f 100644
--- a/src/pkg/go/doc/reader.go
+++ b/src/pkg/go/doc/reader.go
@@ -751,7 +751,7 @@ func sortedFuncs(m methodSet, allMethods bool) []*Func {
 }
 
 // ----------------------------------------------------------------------------
-// Predeclared identifiers (minus "nil")
+// Predeclared identifiers
 
 var predeclaredTypes = map[string]bool{
 	"bool":       true,
@@ -795,7 +795,8 @@ var predeclaredFuncs = map[string]bool{
 }
 
 var predeclaredConstants = map[string]bool{
+	"false": true,
 	"iota":  true,
+	"nil":   true,
 	"true":  true,
-	"false": true,
 }

変更の背景

Go言語のgo/docパッケージは、Goのソースコードを解析し、その構造やコメントからドキュメントを生成する役割を担っています。このプロセスにおいて、Go言語に組み込まれている「事前宣言された識別子」(predeclared identifiers)を正確に認識することが重要です。これには、組み込み型(int, stringなど)、組み込み関数(len, makeなど)、そして組み込み定数(true, false, iotaなど)が含まれます。

nilはGo言語において非常に重要な概念であり、ポインタ、チャネル、関数、インターフェース、マップ、スライスといった参照型のゼロ値を示すために使用されます。しかし、このコミット以前のgo/docパッケージでは、nilが事前宣言された定数として明示的にリストアップされていませんでした。

この認識の欠如は、go/docがコード例(examples)などを解析する際に問題を引き起こす可能性がありました。具体的には、nilがコード内で使用された場合に、それが未解決の識別子(unresolved identifier)として扱われ、ドキュメント生成のロジックに不整合が生じる恐れがありました。例えば、go/doc/example.go内のplayExample関数では、未解決の識別子のリストから事前宣言された識別子を除外する処理が行われていましたが、nilは特別にn == "nil"という条件で個別に処理されていました。これは、nilpredeclaredConstantsマップに含まれていなかったためです。

このコミットは、nilを正式に事前宣言された定数としてgo/docパッケージに認識させることで、この不整合を解消し、コード解析とドキュメント生成のロジックをより堅牢かつ一貫性のあるものにすることを目的としています。これにより、nilの扱いが他の事前宣言された定数(true, false, iotaなど)と同様になり、コードの重複や特別なケースの処理を削減できます。

前提知識の解説

Go言語のnil

Go言語におけるnilは、特定の型の「ゼロ値」を表す事前宣言された識別子です。これは、初期化されていない、または値が割り当てられていない参照型(ポインタ、スライス、マップ、チャネル、インターフェース、関数)のデフォルト値として機能します。nilはキーワードではなく、事前宣言された識別子であり、その型は文脈によって決定されます。例えば、var p *int = nilの場合、nil*int型として扱われます。nilは、これらの参照型が有効な値を指していない状態を示すために不可欠です。

Go言語の「事前宣言された識別子 (predeclared identifiers)」

Go言語には、言語仕様によってあらかじめ定義され、特別な意味を持つ識別子が存在します。これらは、ユーザーが定義する変数や関数とは異なり、プログラムのどこでも特別な宣言なしに使用できます。主なカテゴリは以下の通りです。

  • 型 (Types): bool, byte, complex64, complex128, error, float32, float64, int, int8, int16, int32, int64, rune, string, uint, uint8, uint16, uint32, uint64, uintptr
  • 定数 (Constants): true, false, iota, nil
  • 関数 (Functions): append, cap, close, complex, copy, delete, imag, len, make, new, panic, print, println, real, recover

これらの識別子は、Goプログラムの基本的な構成要素であり、コンパイラによって特別に扱われます。

go/docパッケージ

go/docパッケージは、Go言語の標準ライブラリの一部であり、Goのソースコードからドキュメントを生成するためのツールを提供します。このパッケージは、go/ast(抽象構文木)、go/parser(パーサー)、go/token(トークン)といった他のgo/*パッケージと連携して動作します。

go/docパッケージの主な機能は以下の通りです。

  1. ソースコードの解析: Goのソースファイルを読み込み、go/astパッケージを使用して抽象構文木(AST)を構築します。
  2. ドキュメントの抽出: ASTを走査し、パッケージ、型、関数、変数、定数、メソッドなどの宣言に関する情報と、それらに付随するコメント(ドキュメンテーションコメント)を抽出します。
  3. コード例の処理: Example関数として記述されたコード例を識別し、それらをドキュメントに含めるための処理を行います。これには、コード例の実行可能性のチェックや、出力のキャプチャなどが含まれる場合があります。
  4. 識別子の解決: ソースコード内の識別子(変数名、関数名など)が、組み込みのものか、パッケージ内で定義されたものか、あるいは外部パッケージからインポートされたものかを解決します。この解決プロセスにおいて、事前宣言された識別子のリストが参照されます。

go/docパッケージは、go docコマンドやgodocツール(Go 1.11以降はgo docに統合)の基盤となっており、Goの公式ドキュメントサイト(pkg.go.devなど)のコンテンツ生成にも利用されています。

AST (Abstract Syntax Tree)

AST(抽象構文木)は、プログラミング言語のソースコードの抽象的な構文構造をツリー形式で表現したデータ構造です。コンパイラやインタープリタがソースコードを解析する際に中間表現として生成されます。

Go言語では、go/astパッケージがASTの定義と操作を提供します。go/parserパッケージはソースコードを解析してASTを生成します。go/docパッケージは、このASTを利用してソースコードの意味内容を理解し、ドキュメントに必要な情報を抽出します。ASTは、コードの構造(関数定義、変数宣言、制御フローなど)をプログラム的に操作・分析するための強力な手段となります。

技術的詳細

このコミットの技術的詳細は、go/docパッケージがGo言語の事前宣言された識別子、特にnilをどのように認識し、処理するかという点に集約されます。

  1. src/pkg/go/doc/reader.goにおけるpredeclaredConstantsマップの更新:

    • reader.goファイルには、Go言語の事前宣言された型、関数、定数をそれぞれ管理するためのマップ(predeclaredTypes, predeclaredFuncs, predeclaredConstants)が定義されています。
    • このコミット以前は、predeclaredConstantsマップには"iota", "true", "false"が含まれていましたが、"nil"は含まれていませんでした。
    • 変更後、"nil": true,predeclaredConstantsマップに追加されました。これにより、go/docパッケージの内部ロジックにおいて、nilが他の組み込み定数と同様に扱われるようになります。
    • これに伴い、マップの上のコメントが// Predeclared identifiers (minus "nil")から// Predeclared identifiersに変更され、nilが特別扱いされなくなったことが示されています。
  2. src/pkg/go/doc/example.goにおけるplayExample関数のロジック変更:

    • playExample関数は、Goのコード例を処理し、そのASTを操作してドキュメントに適した形式に変換する役割を担っています。
    • この関数内には、unresolvedというマップがあり、これはまだ解決されていない識別子(つまり、Goの組み込み識別子でも、現在のスコープで定義された識別子でもないもの)を追跡するために使用されます。
    • コミット前のコードでは、unresolvedリストを走査し、各識別子nが事前宣言された型、定数、関数であるかどうかをチェックしていました。この際、nilについてはn == "nil" ||という特別な条件で個別にチェックされていました。これは、nilpredeclaredConstantsマップに含まれていなかったため、このマップによる一般的なチェックでは捕捉できなかったからです。
    • コミット後、reader.gonilpredeclaredConstantsに追加されたため、example.goplayExample関数内のif条件からn == "nil" ||という特別なチェックが削除されました。これにより、nilpredeclaredConstants[n]という一般的なチェックによって適切に処理されるようになり、コードが簡潔化され、一貫性が向上しました。

この変更は、go/docパッケージの内部的な整合性を高め、Go言語のセマンティクス(意味論)をより正確に反映させるものです。nilが事前宣言された定数として明示的に扱われることで、ドキュメント生成プロセスにおける潜在的なエラーや不正確さが排除されます。

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

src/pkg/go/doc/example.go

 // Remove predeclared identifiers from unresolved list.
 for n := range unresolved {
-	if n == "nil" || predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
+	if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
 		delete(unresolved, n)
 	}
 }

src/pkg/go/doc/reader.go

 // ----------------------------------------------------------------------------
-// Predeclared identifiers (minus "nil")
+// Predeclared identifiers
 
 var predeclaredTypes = map[string]bool{
 	"bool":       true,
@@ -795,7 +795,8 @@ var predeclaredFuncs = map[string]bool{
 }
 
 var predeclaredConstants = map[string]bool{
+	"false": true,
 	"iota":  true,
+	"nil":   true,
 	"true":  true,
-	"false": true,
 }

コアとなるコードの解説

src/pkg/go/doc/example.goの変更

この変更は、playExample関数内で行われています。この関数は、Goのコード例(Example関数)の抽象構文木(AST)を処理する際に、未解決の識別子(unresolvedマップに格納されているもの)を特定し、それらがGo言語の組み込み識別子(事前宣言された型、定数、関数)であれば、unresolvedリストから削除するロジックを含んでいます。

変更前は、if文の条件式にn == "nil" ||という部分がありました。これは、「もし識別子nが文字列"nil"と等しいか、またはpredeclaredTypespredeclaredConstantspredeclaredFuncsのいずれかのマップに存在すれば」という意味です。このn == "nil"という特別なチェックは、nilpredeclaredConstantsマップに明示的に含まれていなかったために必要でした。

変更後は、n == "nil" ||が削除されました。これは、src/pkg/go/doc/reader.gonilpredeclaredConstantsマップに追加されたため、predeclaredConstants[n]という一般的なチェックでnilがカバーされるようになったからです。この変更により、コードがより簡潔になり、nilの扱いが他の事前宣言された定数と統一されました。

src/pkg/go/doc/reader.goの変更

この変更は、predeclaredConstantsというグローバル変数マップに対して行われています。このマップは、Go言語の事前宣言された定数を文字列キーとして保持し、その存在をtrueで示すmap[string]bool型です。

変更前は、このマップには"iota", "true", "false"が含まれていましたが、"nil"は含まれていませんでした。そのため、go/docパッケージの他の部分でnilを事前宣言された定数として認識させるためには、個別のロジック(例: example.goでのn == "nil"チェック)が必要でした。

変更後、"nil": true,predeclaredConstantsマップに追加されました。これにより、go/docパッケージ全体でnilが正式に事前宣言された定数として扱われるようになります。この変更は、Go言語のセマンティクスをgo/docパッケージの内部モデルにより正確に反映させるための重要なステップです。

また、マップの上のコメントが// Predeclared identifiers (minus "nil")から// Predeclared identifiersに変更されています。これは、もはやnilが事前宣言された識別子リストから除外される特別なケースではなくなったことを明確に示しています。

これらの変更は相互に関連しており、reader.goでのpredeclaredConstantsマップの更新が、example.goでのコード簡略化を可能にしています。これにより、go/docパッケージのコードベース全体でnilの扱いが一貫し、保守性が向上しています。

関連リンク

参考にした情報源リンク

  • 特になし(Go言語の知識とコミット内容から直接解説を生成しました)