[インデックス 15311] ファイルの概要
このコミットは、Go言語の型チェッカー(go/types
パッケージ)における代入操作の挙動を改善するものです。具体的には、代入文の右辺(RHS: Right-Hand Side)の式を評価する際に、左辺(LHS: Left-Hand Side)の変数の型をヒントとして利用するように変更しています。これにより、より正確な型推論とエラー検出が可能になります。
コミット
commit 1fe8fdf7085f52f4060a06e31ae3033163a20394
Author: Andrew Wilkins <axwalk@gmail.com>
Date: Tue Feb 19 09:20:56 2013 -0800
go/types: Use left-hand side's type as hint for right-hand
side expression evaluation in assignment operations.
R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/7349046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1fe8fdf7085f52f4060a06e31ae3033163a20394
元コミット内容
go/types
: 代入操作において、右辺の式評価のために左辺の型をヒントとして使用する。
変更の背景
Go言語の型システムでは、代入操作において右辺の式の型が左辺の変数の型と互換性があるかどうかが検証されます。しかし、特定の状況下では、右辺の式の型が曖昧であったり、文脈に依存して解釈が変わる場合があります。例えば、型なし定数(untyped constant)や、多値代入における関数呼び出しなどがこれに該当します。
このコミット以前は、型チェッカーが右辺の式を評価する際に、左辺の変数の型を考慮せずに独立して評価していました。このアプローチでは、右辺の式が型なしの値である場合、その値がデフォルトの型に推論されてしまい、結果として左辺の型との不一致が生じたり、本来は許容されるべき代入がエラーとして報告されたりする可能性がありました。
この変更の背景には、型チェッカーの精度向上と、より直感的な型推論の実現があります。代入の文脈において、左辺の型は右辺の式がどのような型を持つべきかという強力なヒントを提供します。このヒントを利用することで、型チェッカーは右辺の式をより正確に型付けし、開発者が意図する挙動に近づけることができます。特に、数値型やインターフェース型など、複数の型に適合しうる値の代入において、このヒントは重要となります。
前提知識の解説
Go言語の型システムと型推論
Go言語は静的型付け言語であり、変数は宣言時に型を持ちます。しかし、Goには強力な型推論機能があり、明示的な型宣言なしに変数を初期化できる場合があります(例: x := 10
)。この場合、コンパイラは初期値から変数の型を推論します。
型なし定数 (Untyped Constants)
Go言語には「型なし定数」という概念があります。これは、数値リテラル(例: 10
, 3.14
)や文字列リテラルなど、特定の型にバインドされていない定数のことです。型なし定数は、使用される文脈(代入先の変数の型や、演算の相手の型など)に応じて適切な型に「変換」されます。例えば、var i int = 10
の場合、10
は型なしの整数定数ですが、int
型に変換されてi
に代入されます。
代入操作 (Assignment Operations)
Go言語における代入操作は、=
演算子を用いて行われます。
LHS = RHS
ここで、LHS
は左辺の変数、RHS
は右辺の式です。Goの仕様では、RHS
の型がLHS
の型に代入可能である必要があります。これには、型が完全に一致する場合だけでなく、型なし定数が適切な型に変換される場合や、インターフェース型への代入などが含まれます。
go/types
パッケージ
go/types
パッケージは、Go言語のコンパイラやツールがGoのソースコードの型チェックを行うために使用するライブラリです。このパッケージは、抽象構文木(AST)を解析し、各式の型を決定し、型エラーを検出する役割を担っています。型チェッカーは、プログラムのセマンティクスを理解し、Go言語の型規則に違反していないかを検証します。
技術的詳細
このコミットの技術的な核心は、go/types
パッケージ内の型チェッカーが代入文を処理する際のcheck.expr
関数の呼び出し方にあります。
変更前は、代入文の右辺(RHS)の式を評価する際に、check.expr
関数はnil
をヒントとして渡していました。これは、右辺の式が独立して型推論されることを意味します。例えば、var u64 uint64; u64 += 1<<u64
のようなコードがあった場合、1<<u64
の部分が評価される際に、u64
がuint64
型であるという情報が1
の型推論に直接的に利用されませんでした。1
は型なしの整数定数として扱われ、その結果、シフト演算の右オペランドがuint
型に推論されるなど、意図しない型推論が発生する可能性がありました。
変更後は、check.expr
関数に左辺(LHS)の変数の型(x.typ
)がヒントとして渡されるようになりました。
// 変更前
// check.expr(&y, s.Rhs[0], nil, -1)
// 変更後
// check.expr(&y, s.Rhs[0], x.typ, -1)
このx.typ
というヒントは、check.expr
関数が右辺の式を評価する際に、その式が最終的にx.typ
に適合するような型を持つべきであるという情報を提供します。これにより、特に型なし定数を含む式や、多重代入における関数呼び出しの結果など、文脈に依存して型が決定されるべきケースにおいて、より正確な型推論が可能になります。
例えば、var i int = 1.0
のような代入があった場合、1.0
は型なし浮動小数点数定数です。ヒントがない場合、1.0
はデフォルトでfloat64
型に推論されるかもしれません。しかし、左辺のi
がint
型であるというヒントがあれば、型チェッカーは1.0
をint
型に変換可能かどうかを考慮し、変換可能であれば代入を許可し、そうでなければエラーを報告します。この場合、1.0
はint
型に変換可能(1
になる)なので、代入は成功します。
この変更は、Go言語の型システムの堅牢性を高め、開発者が遭遇する可能性のある型関連の予期せぬ挙動を減少させることに貢献します。
コアとなるコードの変更箇所
このコミットの主要な変更は、src/pkg/go/types/stmt.go
ファイルの func (check *checker) stmt(s ast.Stmt)
関数内にあります。
--- a/src/pkg/go/types/stmt.go
+++ b/src/pkg/go/types/stmt.go
@@ -403,11 +403,13 @@ func (check *checker) stmt(s ast.Stmt) {
return
}
var x, y operand
+ // The lhs operand's type doesn't need a hint (from the rhs operand),
+ // because it must be a fully typed variable in this case.
check.expr(&x, s.Lhs[0], nil, -1)
if x.mode == invalid {
return
}
- check.expr(&y, s.Rhs[0], nil, -1)
+ check.expr(&y, s.Rhs[0], x.typ, -1)
if y.mode == invalid {
return
}
また、テストケースとして src/pkg/go/types/testdata/stmt0.src
に以下の行が追加されています。
--- a/src/pkg/go/types/testdata/stmt0.src
+++ b/src/pkg/go/types/testdata/stmt0.src
@@ -29,6 +29,9 @@ func _() {
s += "bar"
s += 1 /* ERROR "cannot convert.*string" */
+\
+\tvar u64 uint64\
+\tu64 += 1<<u64
}
func _incdecs() {
そして、src/pkg/exp/gotype/gotype_test.go
では、コメントアウトされていたいくつかの標準ライブラリパッケージのテストが有効化されています。これは、今回の型チェッカーの変更が既存のコードベースに悪影響を与えないことを確認するため、より広範なテストを実行できるようにしたものです。
コアとなるコードの解説
src/pkg/go/types/stmt.go
の変更箇所は、代入文(ast.AssignStmt
)を処理するロジックの一部です。
check.expr(&x, s.Lhs[0], nil, -1)
:
この行は、代入文の左辺(s.Lhs[0]
)の式を評価しています。左辺は通常、既に型が確定している変数であるため、評価時に外部からの型ヒント(nil
)は不要です。評価結果はx
オペランドに格納されます。
check.expr(&y, s.Rhs[0], x.typ, -1)
:
この行が今回の変更の核心です。代入文の右辺(s.Rhs[0]
)の式を評価する際に、第三引数にx.typ
(左辺の型)を渡しています。
&y
: 右辺の式の評価結果が格納されるオペランド。s.Rhs[0]
: 評価対象となる右辺の抽象構文木ノード。x.typ
: 左辺の型。これが右辺の式評価のための「ヒント」として機能します。-1
: オーバーロードされた関数のインデックス(このコンテキストでは重要ではない)。
この変更により、check.expr
関数は右辺の式を評価する際に、左辺の型を考慮に入れることができます。例えば、右辺が型なし定数である場合、その定数が左辺の型に変換可能であれば、その型に推論されるようになります。これにより、より柔軟かつ正確な型チェックが可能となり、Go言語の型推論の挙動がより直感的になります。
src/pkg/go/types/testdata/stmt0.src
に追加されたテストケース u64 += 1<<u64
は、この変更がもたらす具体的な改善を示しています。1<<u64
のようなシフト演算では、シフトされる値(1
)の型がシフト量(u64
)の型によって影響を受ける可能性があります。左辺のu64
がuint64
型であるというヒントがなければ、1
がデフォルトのint
型に推論され、結果として型エラーや意図しない挙動を引き起こす可能性がありました。この変更により、1
がuint64
型に適合するように推論され、正しい型チェックが行われるようになります。
関連リンク
- Go言語の仕様: https://go.dev/ref/spec
- Go言語の型システムに関する議論: https://go.dev/blog/go-type-inference (型推論に関するブログ記事)
- Go言語の型チェッカー(
go/types
パッケージ)のドキュメント: https://pkg.go.dev/go/types
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(特に
go/types
パッケージ) - Go言語のIssue Tracker (関連するバグ報告や議論)
- Go言語のコードレビューシステム (Gerrit) の変更履歴 (CL 7349046)```markdown
[インデックス 15311] ファイルの概要
このコミットは、Go言語の型チェッカー(go/types
パッケージ)における代入操作の挙動を改善するものです。具体的には、代入文の右辺(RHS: Right-Hand Side)の式を評価する際に、左辺(LHS: Left-Hand Side)の変数の型をヒントとして利用するように変更しています。これにより、より正確な型推論とエラー検出が可能になります。
コミット
commit 1fe8fdf7085f52f4060a06e31ae3033163a20394
Author: Andrew Wilkins <axwalk@gmail.com>
Date: Tue Feb 19 09:20:56 2013 -0800
go/types: Use left-hand side's type as hint for right-hand
side expression evaluation in assignment operations.
R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/7349046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1fe8fdf7085f52f4060a06e31ae3033163a20394
元コミット内容
go/types
: 代入操作において、右辺の式評価のために左辺の型をヒントとして使用する。
変更の背景
Go言語の型システムでは、代入操作において右辺の式の型が左辺の変数の型と互換性があるかどうかが検証されます。しかし、特定の状況下では、右辺の式の型が曖昧であったり、文脈に依存して解釈が変わる場合があります。例えば、型なし定数(untyped constant)や、多値代入における関数呼び出しなどがこれに該当します。
このコミット以前は、型チェッカーが右辺の式を評価する際に、左辺の変数の型を考慮せずに独立して評価していました。このアプローチでは、右辺の式が型なしの値である場合、その値がデフォルトの型に推論されてしまい、結果として左辺の型との不一致が生じたり、本来は許容されるべき代入がエラーとして報告されたりする可能性がありました。
この変更の背景には、型チェッカーの精度向上と、より直感的な型推論の実現があります。代入の文脈において、左辺の型は右辺の式がどのような型を持つべきかという強力なヒントを提供します。このヒントを利用することで、型チェッカーは右辺の式をより正確に型付けし、開発者が意図する挙動に近づけることができます。特に、数値型やインターフェース型など、複数の型に適合しうる値の代入において、このヒントは重要となります。
前提知識の解説
Go言語の型システムと型推論
Go言語は静的型付け言語であり、変数は宣言時に型を持ちます。しかし、Goには強力な型推論機能があり、明示的な型宣言なしに変数を初期化できる場合があります(例: x := 10
)。この場合、コンパイラは初期値から変数の型を推論します。
型なし定数 (Untyped Constants)
Go言語には「型なし定数」という概念があります。これは、数値リテラル(例: 10
, 3.14
)や文字列リテラルなど、特定の型にバインドされていない定数のことです。型なし定数は、使用される文脈(代入先の変数の型や、演算の相手の型など)に応じて適切な型に「変換」されます。例えば、var i int = 10
の場合、10
は型なしの整数定数ですが、int
型に変換されてi
に代入されます。
代入操作 (Assignment Operations)
Go言語における代入操作は、=
演算子を用いて行われます。
LHS = RHS
ここで、LHS
は左辺の変数、RHS
は右辺の式です。Goの仕様では、RHS
の型がLHS
の型に代入可能である必要があります。これには、型が完全に一致する場合だけでなく、型なし定数が適切な型に変換される場合や、インターフェース型への代入などが含まれます。
go/types
パッケージ
go/types
パッケージは、Go言語のコンパイラやツールがGoのソースコードの型チェックを行うために使用するライブラリです。このパッケージは、抽象構文木(AST)を解析し、各式の型を決定し、型エラーを検出する役割を担っています。型チェッカーは、プログラムのセマンティクスを理解し、Go言語の型規則に違反していないかを検証します。
技術的詳細
このコミットの技術的な核心は、go/types
パッケージ内の型チェッカーが代入文を処理する際のcheck.expr
関数の呼び出し方にあります。
変更前は、代入文の右辺(RHS)の式を評価する際に、check.expr
関数はnil
をヒントとして渡していました。これは、右辺の式が独立して型推論されることを意味します。例えば、var u64 uint64; u64 += 1<<u64
のようなコードがあった場合、1<<u64
の部分が評価される際に、u64
がuint64
型であるという情報が1
の型推論に直接的に利用されませんでした。1
は型なしの整数定数として扱われ、その結果、シフト演算の右オペランドがuint
型に推論されるなど、意図しない型推論が発生する可能性がありました。
変更後は、check.expr
関数に左辺(LHS)の変数の型(x.typ
)がヒントとして渡されるようになりました。
// 変更前
// check.expr(&y, s.Rhs[0], nil, -1)
// 変更後
// check.expr(&y, s.Rhs[0], x.typ, -1)
このx.typ
というヒントは、check.expr
関数が右辺の式を評価する際に、その式が最終的にx.typ
に適合するような型を持つべきであるという情報を提供します。これにより、特に型なし定数を含む式や、多重代入における関数呼び出しの結果など、文脈に依存して型が決定されるべきケースにおいて、より正確な型推論が可能になります。
例えば、var i int = 1.0
のような代入があった場合、1.0
は型なし浮動小数点数定数です。ヒントがない場合、1.0
はデフォルトでfloat64
型に推論されるかもしれません。しかし、左辺のi
がint
型であるというヒントがあれば、型チェッカーは1.0
をint
型に変換可能かどうかを考慮し、変換可能であれば代入を許可し、そうでなければエラーを報告します。この場合、1.0
はint
型に変換可能(1
になる)なので、代入は成功します。
この変更は、Go言語の型システムの堅牢性を高め、開発者が遭遇する可能性のある型関連の予期せぬ挙動を減少させることに貢献します。
コアとなるコードの変更箇所
このコミットの主要な変更は、src/pkg/go/types/stmt.go
ファイルの func (check *checker) stmt(s ast.Stmt)
関数内にあります。
--- a/src/pkg/go/types/stmt.go
+++ b/src/pkg/go/types/stmt.go
@@ -403,11 +403,13 @@ func (check *checker) stmt(s ast.Stmt) {
return
}
var x, y operand
+ // The lhs operand's type doesn't need a hint (from the rhs operand),
+ // because it must be a fully typed variable in this case.
check.expr(&x, s.Lhs[0], nil, -1)
if x.mode == invalid {
return
}
- check.expr(&y, s.Rhs[0], nil, -1)
+ check.expr(&y, s.Rhs[0], x.typ, -1)
if y.mode == invalid {
return
}
また、テストケースとして src/pkg/go/types/testdata/stmt0.src
に以下の行が追加されています。
--- a/src/pkg/go/types/testdata/stmt0.src
+++ b/src/pkg/go/types/testdata/stmt0.src
@@ -29,6 +29,9 @@ func _() {
s += "bar"
s += 1 /* ERROR "cannot convert.*string" */
+\
+\tvar u64 uint64\
+\tu64 += 1<<u64
}
func _incdecs() {
そして、src/pkg/exp/gotype/gotype_test.go
では、コメントアウトされていたいくつかの標準ライブラリパッケージのテストが有効化されています。これは、今回の型チェッカーの変更が既存のコードベースに悪影響を与えないことを確認するため、より広範なテストを実行できるようにしたものです。
コアとなるコードの解説
src/pkg/go/types/stmt.go
の変更箇所は、代入文(ast.AssignStmt
)を処理するロジックの一部です。
check.expr(&x, s.Lhs[0], nil, -1)
:
この行は、代入文の左辺(s.Lhs[0]
)の式を評価しています。左辺は通常、既に型が確定している変数であるため、評価時に外部からの型ヒント(nil
)は不要です。評価結果はx
オペランドに格納されます。
check.expr(&y, s.Rhs[0], x.typ, -1)
:
この行が今回の変更の核心です。代入文の右辺(s.Rhs[0]
)の式を評価する際に、第三引数にx.typ
(左辺の型)を渡しています。
&y
: 右辺の式の評価結果が格納されるオペランド。s.Rhs[0]
: 評価対象となる右辺の抽象構文木ノード。x.typ
: 左辺の型。これが右辺の式評価のための「ヒント」として機能します。-1
: オーバーロードされた関数のインデックス(このコンテキストでは重要ではない)。
この変更により、check.expr
関数は右辺の式を評価する際に、左辺の型を考慮に入れることができます。例えば、右辺が型なし定数である場合、その定数が左辺の型に変換可能であれば、その型に推論されるようになります。これにより、より柔軟かつ正確な型チェックが可能となり、Go言語の型推論の挙動がより直感的になります。
src/pkg/go/types/testdata/stmt0.src
に追加されたテストケース u64 += 1<<u64
は、この変更がもたらす具体的な改善を示しています。1<<u64
のようなシフト演算では、シフトされる値(1
)の型がシフト量(u64
)の型によって影響を受ける可能性があります。左辺のu64
がuint64
型であるというヒントがなければ、1
がデフォルトのint
型に推論され、結果として型エラーや意図しない挙動を引き起こす可能性がありました。この変更により、1
がuint64
型に適合するように推論され、正しい型チェックが行われるようになります。
関連リンク
- Go言語の仕様: https://go.dev/ref/spec
- Go言語の型システムに関する議論: https://go.dev/blog/go-type-inference (型推論に関するブログ記事)
- Go言語の型チェッカー(
go/types
パッケージ)のドキュメント: https://pkg.go.dev/go/types
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(特に
go/types
パッケージ) - Go言語のIssue Tracker (関連するバグ報告や議論)
- Go言語のコードレビューシステム (Gerrit) の変更履歴 (CL 7349046)