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

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

このコミットは、Goコンパイラ (cmd/gc) において、自己参照型の配列型に関するテストを追加するものです。具体的には、構造体内で自身のフィールドを参照して配列のサイズを決定しようとする不正なケースを検出し、コンパイルエラーを発生させることを目的としています。

コミット

commit e45f5cd5f17c88f979adcbebaa3bead9b0d7cc1f
Author: Chris Manghane <cmang@golang.org>
Date:   Mon Mar 17 18:30:02 2014 -0700

    cmd/gc: Add tests for self-referential array types.
    
    LGTM=gri, iant
    R=gri, iant
    CC=golang-codereviews
    https://golang.org/cl/77050045

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

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

元コミット内容

cmd/gc: Add tests for self-referential array types.

LGTM=gri, iant
R=gri, iant
CC=golang-codereviews
https://golang.org/cl/77050045

変更の背景

このコミットは、Go言語のIssue 7525「cmd/gc: self-referential array types」に関連しています。Go言語では、配列の長さはコンパイル時に定数として決定される必要があります。しかし、特定の状況下で、構造体のフィールドが自身の構造体内の別のフィールドを参照して配列の長さを定義しようとする、いわゆる「自己参照型配列」が意図せず作成される可能性がありました。このような自己参照は、コンパイル時の型チェックやサイズ計算を不可能にするため、コンパイラがこれを適切に検出し、エラーとして報告する必要がありました。

このコミット以前は、Goコンパイラがこのような不正な自己参照を常に適切に検出できていなかった可能性があります。そのため、コンパイラの堅牢性を高め、開発者がGoの型システムにおける制約をより明確に理解できるようにするために、この種の不正なコードパターンを明示的にテストし、エラーとして扱うための変更が導入されました。

前提知識の解説

Go言語の配列

Go言語における配列は、同じ型の要素を固定長で連続して格納するデータ構造です。配列の長さは型の一部であり、コンパイル時に決定される定数である必要があります。例えば、[5]int は5つの整数を格納する配列型であり、[10]int とは異なる型として扱われます。

unsafe パッケージ

unsafe パッケージは、Go言語の型安全性とメモリ安全性の保証を意図的にバイパスするための機能を提供します。通常、Go言語ではポインタ演算や型変換は厳しく制限されていますが、unsafe パッケージを使用することで、低レベルのメモリ操作が可能になります。

  • unsafe.Sizeof(x): 式 x のオペランドが占めるバイト数を返します。これはコンパイル時に評価される定数です。
  • unsafe.Offsetof(x.f): 構造体 x のフィールド f のオフセット(構造体の先頭からのバイト単位の距離)を返します。これもコンパイル時に評価される定数です。
  • unsafe.Alignof(x): 式 x のオペランドのメモリ配置アライメントを返します。これもコンパイル時に評価される定数です。

これらの関数は、通常、低レベルのシステムプログラミングや、特定のメモリレイアウトを必要とする場合にのみ使用されます。

lencap

  • len(s): スライス、配列、文字列、マップ、チャネルの長さを返します。配列に対して使用した場合、その配列の固定長を返します。
  • cap(s): スライス、配列、チャネルの容量を返します。配列に対して使用した場合、その配列の固定長を返します。

配列の長さはコンパイル時に定数である必要があるため、lencap を配列の長さの定義に用いる場合、その引数もコンパイル時にその長さが確定している必要があります。

自己参照型 (Self-referential types)

自己参照型とは、型定義の中で自分自身を参照する構造を持つ型のことです。ポインタを使ったリンクリストやツリー構造などでよく見られます。しかし、Goの配列の長さの定義においては、コンパイル時に値が確定する必要があるため、自己参照によってその値が確定できない場合は不正となります。

技術的詳細

このコミットで追加されたテストケース test/fixedbugs/issue7525.go は、Goコンパイラが自己参照型の配列定義を正しくエラーとして検出することを確認します。具体的には、以下のような構造体 x が定義されています。

var x struct {
	a [unsafe.Sizeof(x.a)]int // ERROR "array bound|invalid array"
	b [unsafe.Offsetof(x.b)]int // ERROR "array bound|invalid array"
	c [unsafe.Alignof(x.c)]int // ERROR "array bound|invalid array"
	d [len(x.d)]int // ERROR "array bound|invalid array"
	e [cap(x.e)]int // ERROR "array bound|invalid array"
}

この構造体 x の各フィールド (a, b, c, d, e) は配列として定義されており、その配列の長さは、同じ構造体 x の別のフィールド(または自分自身)を参照する式によって決定しようとしています。

  • x.a のサイズを x.a 自身のサイズで定義しようとする。
  • x.b のサイズを x.b 自身のオフセットで定義しようとする。
  • x.c のサイズを x.c 自身のアライメントで定義しようとする。
  • x.d のサイズを x.d 自身の len で定義しようとする。
  • x.e のサイズを x.e 自身の cap で定義しようとする。

これらのケースはすべて、配列の長さがコンパイル時に定数として確定できない「自己参照」の状況を作り出しています。例えば、unsafe.Sizeof(x.a) を評価するためには x.a の型が完全に決定されている必要がありますが、x.a の型は unsafe.Sizeof(x.a) の結果に依存しているため、循環参照が発生します。

Goコンパイラは、このような循環参照を検出し、"array bound" または "invalid array" というエラーメッセージを出力するように設計されています。このテストは、コンパイラがこれらの不正なパターンを確実に捕捉できることを保証します。

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

このコミットでは、既存のGoコンパイラのコードベースに変更を加えるのではなく、新しいテストファイル test/fixedbugs/issue7525.go を追加しています。

--- /dev/null
+++ b/test/fixedbugs/issue7525.go
@@ -0,0 +1,19 @@
+// errorcheck
+
+// Copyright 2014 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.
+
+// Issue 7525: self-referential array types.
+
+package main
+
+import "unsafe"
+
+var x struct {
+	a [unsafe.Sizeof(x.a)]int // ERROR "array bound|invalid array"
+	b [unsafe.Offsetof(x.b)]int // ERROR "array bound|invalid array"
+	c [unsafe.Alignof(x.c)]int // ERROR "array bound|invalid array"
+	d [len(x.d)]int // ERROR "array bound|invalid array"
+	e [cap(x.e)]int // ERROR "array bound|invalid array"
+}

コアとなるコードの解説

追加された test/fixedbugs/issue7525.go ファイルは、Goのテストスイートの一部として機能します。ファイルの先頭にある // errorcheck ディレクティブは、このファイルがコンパイル時に特定のエラーメッセージを生成することを期待するテストであることを示しています。

各配列定義の行の末尾には // ERROR "array bound|invalid array" というコメントが付加されています。これは、Goのテストツールがこの行で指定された正規表現("array bound|invalid array")に一致するエラーメッセージが出力されることを期待していることを意味します。もしコンパイラがこれらの不正な自己参照を検出できず、エラーを出力しなかった場合、または異なるエラーメッセージを出力した場合、テストは失敗します。

このテストファイルは、Goコンパイラが以下のシナリオで正しくエラーを報告することを確認します。

  1. unsafe.Sizeof を使用して、自身のフィールドのサイズを参照する。
  2. unsafe.Offsetof を使用して、自身のフィールドのオフセットを参照する。
  3. unsafe.Alignof を使用して、自身のフィールドのアライメントを参照する。
  4. len を使用して、自身のフィールドの長さを参照する。
  5. cap を使用して、自身のフィールドの容量を参照する。

これらのテストケースは、Goコンパイラの型チェックロジックが、配列の長さがコンパイル時に定数として解決可能であるという制約を厳密に適用していることを保証します。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード
  • Go言語のIssueトラッカー
  • unsafe パッケージのドキュメント
  • Go言語の配列に関するドキュメント
  • len および cap 関数のドキュメント
  • Go言語のコンパイラ (cmd/gc) に関する一般的な情報