[インデックス 15217] ファイルの概要
このコミットは、Go言語の型チェッカー(go/types
パッケージ)における組み込み関数print
およびprintln
の挙動を変更し、これらの関数が0個以上の引数を受け入れるように修正するものです。以前は、これらの関数は少なくとも1つの引数を必要としていました。この変更により、引数なしでprint()
やprintln()
を呼び出すことが可能になり、例えば単に改行を出力したい場合などに便利になります。
コミット
commit 27970af5c9a90e1b50026b4ff003064ff9d04920
Author: Robert Griesemer <gri@golang.org>
Date: Tue Feb 12 19:40:20 2013 -0800
go/types: print, println accept 0 or more arguments
R=adonovan
CC=golang-dev
https://golang.org/cl/7304089
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/27970af5c9a90e1b50026b4ff003064ff9d04920
元コミット内容
go/types: print, println accept 0 or more arguments
このコミットは、Go言語の型システムにおいて、組み込み関数であるprint
とprintln
が0個以上の引数を受け入れるように変更します。
変更の背景
Go言語の初期のバージョンでは、print
とprintln
はデバッグや簡単な出力のために提供される組み込み関数であり、少なくとも1つの引数を必要としていました。しかし、他の多くの言語の類似機能(例えばC言語のprintf("\n")
やPythonのprint()
)と同様に、引数なしで呼び出して単に改行を出力したり、特定の値を伴わないデバッグメッセージを出力したりするユースケースが存在します。
このコミットは、このような一般的な使用パターンに対応し、print
とprintln
の柔軟性を高めることを目的としています。これにより、Go言語のユーザーはより直感的にこれらの関数を使用できるようになります。また、型チェッカーがこれらの呼び出しを正しく処理できるように、内部的な定義とチェックロジックが更新されています。
前提知識の解説
Go言語の組み込み関数 (builtins
)
Go言語には、言語仕様の一部として定義され、特別な意味を持ついくつかの組み込み関数があります。これらはimport
することなく直接使用できます。make
, new
, len
, cap
, append
, panic
, print
, println
などがその例です。これらの関数はコンパイラによって特別に扱われ、その型チェックやコード生成は通常の関数とは異なるロジックで行われます。
go/types
パッケージ
go/types
パッケージは、Go言語のコンパイラツールチェーンの一部であり、Goプログラムの型チェックを行うためのライブラリです。このパッケージは、ソースコードを解析し、各式の型を決定し、型の一貫性や正当性を検証します。例えば、関数呼び出しの引数の数や型が、その関数の定義と一致しているかなどをチェックします。
ast
(Abstract Syntax Tree)
Goコンパイラは、ソースコードを解析する際に、その構造を抽象構文木(AST)として表現します。ast.CallExpr
は、AST内で関数呼び出しを表すノードです。型チェッカーは、このASTを走査し、各ノードに対応する型情報を付与したり、型エラーを検出したりします。
operand
構造体
go/types
パッケージ内部で使用されるoperand
構造体は、式の結果を表すために用いられます。これには、式のモード(例: variable
, value
, novalue
, invalid
など)と型情報が含まれます。
novalue
: 式が値を生成しないことを示します(例:panic
やprint
のような副作用を持つ関数)。invalid
: 式が型エラーを含んでいることを示します。
技術的詳細
このコミットは、主に以下の3つのファイルにわたる変更を通じて、print
とprintln
の引数処理を修正しています。
-
src/pkg/go/types/universe.go
:- このファイルは、Go言語の「ユニバースブロック」に存在する、事前に宣言された(組み込みの)関数や型を定義しています。
predeclaredFunctions
という配列内で、_Print
と_Println
の定義が更新されています。- 変更前:
{_Print, "print", 1, true, true}
および{_Println, "println", 1, true, true}
- 変更後:
{_Print, "print", 0, true, true}
および{_Println, "println", 0, true, true}
- ここで変更された
1
から0
への数値は、その関数が受け入れる最小引数(minArgs
)を示しています。これにより、型チェッカーはprint
とprintln
が0個の引数でも有効であると認識するようになります。
-
src/pkg/go/types/builtins.go
:- このファイルには、組み込み関数の呼び出しを型チェックするためのロジックが含まれています。
- 変更点1:
builtin
メソッド内のswitch id
文において、_Print
と_Println
が_Make, _New, _Trace
と同じケースに追加されました。これは、これらの関数が引数の数に関してより柔軟な扱いを受けることを示唆しています。 - 変更点2:
_Panic, _Print, _Println
を処理するcase
ブロックが修正されました。_Panic
の処理が分離され、x.mode = novalue
が直接設定されるようになりました。これは、panic
が常に値を返さないことを明示しています。_Print
と_Println
の引数処理ループがfor _, arg := range args[1:]
からfor _, arg := range args
に変更されました。- 元のコードでは
args[1:]
(スライス)を使用していたため、最初の引数(args[0]
)はスキップされ、関数が少なくとも1つの引数を持つことを前提としていました。 - 新しいコードでは
args
全体をイテレートするため、引数が0個の場合でもループが正しく動作し、引数がない呼び出しが有効になります。
- 元のコードでは
- ループ内に
if x.mode == invalid { goto Error }
というチェックが追加されました。これは、print
やprintln
の引数の中に型エラーがある場合、そのエラーを適切に伝播させるためのものです。
-
src/pkg/go/types/testdata/builtins.src
:- このファイルは、
go/types
パッケージの組み込み関数に対するテストケースを含んでいます。 - 新しいテストケースが追加され、
_print()
と_println()
が引数なしで呼び出されるシナリオが検証されています。 - また、
_panic()
が引数なしや複数引数で呼び出された場合にエラーとなることを確認するテストも追加されており、panic
の引数制約(常に1つの引数)が維持されていることを示しています。
- このファイルは、
これらの変更により、Goコンパイラの型チェッカーは、print()
やprintln()
のような引数なしの呼び出しを有効な構文として認識し、コンパイルエラーを発生させなくなります。
コアとなるコードの変更箇所
src/pkg/go/types/builtins.go
--- a/src/pkg/go/types/builtins.go
+++ b/src/pkg/go/types/builtins.go
@@ -41,7 +41,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
if n > 0 {
arg0 = args[0]
switch id {
- case _Make, _New, _Trace:
+ case _Make, _New, _Print, _Println, _Trace:
// respective cases below do the work
default:
// argument must be an expression
@@ -301,9 +301,15 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
x.mode = variable
x.typ = &Pointer{Base: resultTyp}
- case _Panic, _Print, _Println:
- for _, arg := range args[1:] {
+ case _Panic:
+ x.mode = novalue
+
+ case _Print, _Println:
+ for _, arg := range args {
check.expr(x, arg, nil, -1)
+ if x.mode == invalid {
+ goto Error
+ }
}
x.mode = novalue
src/pkg/go/types/testdata/builtins.src
--- a/src/pkg/go/types/testdata/builtins.src
+++ b/src/pkg/go/types/testdata/builtins.src
@@ -214,6 +214,32 @@ func _new() {
new /* ERROR "not used" */ (int)
}
+func _panic() {
+ panic /* ERROR "arguments" */ ()
+ panic /* ERROR "arguments" */ (1, 2)
+ panic(0)
+ panic("foo")
+ panic(false)
+}
+
+func _print() {
+ print()
+ print(1)
+ print(1, 2)
+ print("foo")
+ print(2.718281828)
+ print(false)
+}
+
+func _println() {
+ println()
+ println(1)
+ println(1, 2)
+ println("foo")
+ println(2.718281828)
+ println(false)
+}
+
func _real() {
var f32 float32
var f64 float64
src/pkg/go/types/universe.go
--- a/src/pkg/go/types/universe.go
+++ b/src/pkg/go/types/universe.go
@@ -73,8 +73,8 @@ var predeclaredFunctions = [...]*builtin{
{_Make, "make", 1, true, false},
{_New, "new", 1, false, false},
{_Panic, "panic", 1, false, true},
- {_Print, "print", 1, true, true},
- {_Println, "println", 1, true, true},
+ {_Print, "print", 0, true, true},
+ {_Println, "println", 0, true, true},
{_Real, "real", 1, false, false},
{_Recover, "recover", 0, false, true},
コアとなるコードの解説
src/pkg/go/types/builtins.go
の変更
-
case _Make, _New, _Print, _Println, _Trace:
の追加:- これは、
print
とprintln
が、make
,new
,trace
と同様に、引数の数や型に関して特別な処理を必要とする組み込み関数として認識されるようにするための変更です。これにより、これらの関数が可変引数を受け入れる可能性が示唆されます。
- これは、
-
case _Panic:
の分離とcase _Print, _Println:
の変更:- 以前は
_Panic, _Print, _Println
が同じcase
で処理されていましたが、_Panic
が分離されました。これは、panic
が常に1つの引数を必要とするのに対し、print
とprintln
は引数の数が可変になるため、処理を分ける必要があったためです。 _Print
と_Println
のループがfor _, arg := range args[1:]
からfor _, arg := range args
に変更された点が最も重要です。args[1:]
は、引数リストの2番目以降の要素を処理することを意味していました。つまり、少なくとも1つの引数(args[0]
)が存在することを前提としていました。args
は、引数リスト全体を処理することを意味します。これにより、args
が空(引数が0個)の場合でもループが正しく終了し、エラーなく処理が続行されるようになります。
if x.mode == invalid { goto Error }
の追加は、print
やprintln
に渡された引数自体が型エラーを含んでいる場合に、そのエラーを適切に伝播させるための堅牢性向上です。
- 以前は
src/pkg/go/types/testdata/builtins.src
の変更
- このファイルは、
go/types
パッケージのテストデータとして機能します。 _print()
と_println()
の引数なしの呼び出しが追加され、これがエラーにならないことを確認しています。_print(1)
,_print(1, 2)
,_print("foo")
などの様々な引数パターンもテストされており、これらの関数が引き続き単一または複数の引数を受け入れることを確認しています。_panic()
のテストケースも更新され、引数なしや複数引数でのpanic
呼び出しがコンパイルエラーになることを明示的にテストしています。これにより、panic
の引数制約が維持されていることが確認されます。
src/pkg/go/types/universe.go
の変更
- このファイルは、Go言語の組み込み関数に関するメタデータを提供します。
_Print
と_Println
の定義における3番目のフィールド(minArgs
)が1
から0
に変更されました。- この
minArgs
フィールドは、その組み込み関数が受け入れる最小の引数の数を型チェッカーに伝えます。0
に設定することで、print
とprintln
が引数なしで呼び出されても型エラーにならないことが保証されます。
これらの変更が連携することで、Go言語の型チェッカーはprint()
やprintln()
のような引数なしの呼び出しを正しく処理し、コンパイルエラーを発生させなくなります。
関連リンク
- Go言語の組み込み関数に関する公式ドキュメント: https://go.dev/ref/spec#Predeclared_functions
- Go言語の
go/types
パッケージに関するドキュメント: https://pkg.go.dev/go/types
参考にした情報源リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のコードレビューシステム (Gerrit): https://go.dev/cl/7304089 (コミットメッセージに記載されているCLリンク)
- Go言語の
print
とprintln
に関する議論(Stack Overflowなど): - Go言語の型チェッカーの仕組みに関する一般的な情報源(書籍やブログ記事など)