[インデックス 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言語の型チェッカーの仕組みに関する一般的な情報源(書籍やブログ記事など)