[インデックス 1145] ファイルの概要
このコミットは、Go言語の初期段階におけるnew
組み込み関数の引数処理に関するバグ修正を目的としています。具体的には、new
関数がスライス型に対して不適切な数の引数(本来make
関数が受け取るべき長さや容量の引数)を受け入れてしまう問題を修正するためのテストケースを追加しています。
コミット
- コミットハッシュ:
7692a93173cb8555acf4d24433224d48f531bf5a
- 作者: Robert Griesemer gri@golang.org
- コミット日時: Mon Nov 17 16:46:56 2008 -0800
- コミットメッセージ:
- new() accepts too many arguments R=r OCL=19413 CL=19413
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7692a93173cb8555acf4d24433224d48f531bf5a
元コミット内容
commit 7692a93173cb8555acf4d24433224d48f531bf5a
Author: Robert Griesemer <gri@golang.org>
Date: Mon Nov 17 16:46:56 2008 -0800
- new() accepts too many arguments
R=r
OCL=19413
CL=19413
---
test/bugs/bug122.go | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/test/bugs/bug122.go b/test/bugs/bug122.go
new file mode 100644
index 0000000000..da58944b77
--- /dev/null
+++ b/test/bugs/bug122.go
@@ -0,0 +1,11 @@
+// errchk $G $D/$F.go
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main() {
+\ta := new([]int, 10, 20, 30, 40); // should allow at most 2 sizes
+}\n
変更の背景
このコミットが行われた2008年当時、Go言語はまだ開発の初期段階にありました。new
組み込み関数は、特定の型のためのメモリを割り当て、そのゼロ値へのポインタを返す役割を担っています。しかし、スライス型に対してnew
関数が呼び出された際に、本来make
関数が受け取るべき引数(スライスの長さや容量)を誤って受け入れてしまうというバグが存在していました。
このバグは、コンパイラがnew
関数の引数検証を適切に行っていなかったことに起因します。new([]int, 10, 20, 30, 40)
のような不正な呼び出しがコンパイル時にエラーとして検出されず、予期せぬ動作やクラッシュを引き起こす可能性がありました。このコミットは、このような不正なnew
関数の使用をコンパイル時に確実に検出するための回帰テストを追加することで、この問題を修正しようとしています。
前提知識の解説
Go言語には、メモリを割り当てるための主要な組み込み関数としてnew
とmake
の2つがあります。これらは異なる目的で使用され、特にスライス、マップ、チャネルといった参照型においてはその違いが重要です。
-
new(Type)
:- 任意の型
Type
のゼロ値を格納するのに十分なメモリを割り当て、その型へのポインタ(*Type
)を返します。 - 割り当てられたメモリは、その型のゼロ値で初期化されます(例: 数値型は0、文字列は""、ブール値はfalse、ポインタはnil)。
- スライス型に対して
new([]T)
と呼び出すと、それはスライスのヘッダ(基底配列へのポインタ、長さ、容量)のためのメモリを割り当てるだけであり、基底配列自体は割り当てられません。結果として得られるスライスはnil
スライスと同様に長さ0、容量0となります。
- 任意の型
-
make(Type, size, capacity)
:- スライス、マップ、チャネルといった組み込みの参照型のみに使用されます。
- これらの型を初期化し、使用可能な状態にします。
- スライスの場合:
make([]T, length, capacity)
のように使用します。length
はスライスの初期要素数、capacity
は基底配列の総容量を指定します。capacity
は省略可能で、その場合はlength
と同じ値になります。make
は指定された長さと容量を持つ基底配列を割り当て、その配列を参照するスライスヘッダを返します。
このコミットの文脈では、new([]int, 10, 20, 30, 40)
という呼び出しが問題となっています。new
関数はスライスの長さや容量を指定する引数を受け取るべきではありません。これらの引数はmake
関数に特有のものです。このバグは、Goコンパイラがnew
とmake
の引数規則を厳密に区別していなかったことを示しています。
技術的詳細
このコミットの技術的詳細は、Goコンパイラの型チェックと組み込み関数の引数検証の厳密性に関するものです。
Go言語の設計哲学の一つに「早期エラー検出」があります。これは、開発プロセスの早い段階(コンパイル時)で可能な限り多くのエラーを検出することで、ランタイムエラーや予期せぬバグの発生を防ぐというものです。
new
関数とmake
関数は、そのセマンティクスが大きく異なります。
new(T)
は、T
型の変数を宣言し、そのアドレスを取得するのと概念的に似ています。例えば、var x T; p := &x
とp := new(T)
は、p
がT
型のゼロ値へのポインタであるという点で似ています。make(T, ...)
は、スライス、マップ、チャネルといった特殊なデータ構造を「構築」するために使用されます。これらの型は、単にメモリを割り当てるだけでなく、内部的なデータ構造(例えば、スライスの基底配列、マップのハッシュテーブル、チャネルのバッファ)を適切に初期化する必要があります。
問題となっていたのは、コンパイラがnew
関数に対して、make
関数にのみ有効な引数(スライスの長さや容量)が渡された場合に、それを不正な呼び出しとして認識しなかった点です。new([]int, 10, 20, 30, 40)
というコードは、new
の定義から逸脱しており、コンパイル時にエラーとなるべきです。このコミットは、この特定のケースを捕捉するためのテストを追加することで、コンパイラの引数検証ロジックが強化されたことを示唆しています。
test/bugs/bug122.go
ファイルの先頭にある// errchk $G $D/$F.go
というコメントは、Goのテストフレームワークにおける特別なディレクティブです。これは、そのファイルがコンパイルされる際に、指定されたコンパイラ($G
はGoコンパイラを指す)がエラーを発生させることを期待していることを示します。もしエラーが発生しなければ、テストは失敗します。これにより、コンパイラが将来的にこの種の不正なnew
呼び出しを誤って許可しないことが保証されます。
コアとなるコードの変更箇所
このコミットで追加された唯一のファイルは、test/bugs/bug122.go
です。
--- /dev/null
+++ b/test/bugs/bug122.go
@@ -0,0 +1,11 @@
+// errchk $G $D/$F.go
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main() {
+\ta := new([]int, 10, 20, 30, 40); // should allow at most 2 sizes
+}\n
コアとなるコードの解説
追加されたtest/bugs/bug122.go
は、Goコンパイラのバグをテストするための非常に簡潔なプログラムです。
-
// errchk $G $D/$F.go
:- これはGoのテストシステムに対する指示であり、このファイルがコンパイルされる際にコンパイラ(
$G
)がエラーを出すことを期待していることを示します。もしエラーが出なければ、テストは失敗します。これは、このコミットが修正しようとしているバグが、コンパイル時に検出されるべきエラーであることを明確にしています。
- これはGoのテストシステムに対する指示であり、このファイルがコンパイルされる際にコンパイラ(
-
package main
とfunc main() {}
:- これはGoの実行可能なプログラムの標準的な構造です。
-
a := new([]int, 10, 20, 30, 40); // should allow at most 2 sizes
:- この行がテストの核心です。
new
関数は、スライス型[]int
に対して呼び出されていますが、それに続いて10, 20, 30, 40
という4つの引数が渡されています。 - コメント
// should allow at most 2 sizes
が示すように、スライスを初期化する際に長さと容量を指定できるのはmake
関数であり、その場合でも最大2つの引数(長さと容量)しか取りません。new
関数は型引数のみを受け取るべきであり、このような追加の引数は不正です。 - このテストの目的は、コンパイラがこの不正な
new
呼び出しを検出し、コンパイルエラーを発生させることを確認することです。これにより、new
関数の引数検証が正しく機能していることが保証されます。
- この行がテストの核心です。
このテストケースの追加により、Goコンパイラはnew
関数の誤用を早期に検出し、開発者がより堅牢なコードを書くのを助けることができるようになりました。
関連リンク
- Go言語の公式ドキュメント(
new
とmake
について):https://go.dev/doc/effective_go#allocation_new - Go言語の公式ドキュメント(スライスについて):https://go.dev/blog/go-slices-usage-and-internals
参考にした情報源リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/7692a93173cb8555acf4d24433224d48f531bf5a
- Go言語の
new
とmake
に関する一般的な知識 - Go言語のテストにおける
errchk
ディレクティブに関する知識