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

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

このコミットは、Go言語の公式仕様書(doc/go_spec.html)における「式ステートメント(Expression statements)」および「unsafe.Offsetof関数」に関する記述を明確化するものです。特に、特定の組み込み関数がステートメントとして使用できないこと、およびunsafe.Offsetofが括弧で囲まれたセレクタを引数として受け入れることを明記しています。これは、既存のコンパイラの挙動(gcとgccgoの両方)を文書化したものであり、言語の動作自体を変更するものではありません。

コミット

commit 8c058b32d1838e67d67277564b6cb9b0bccd1e75
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Sep 18 11:25:53 2012 -0700

    spec: clarify expression statements
    
    Function and method calls are valid expression statements,
    but calling certain built-in functions is not permitted.
    Enumerate the built-ins.
    
    Also: unsafe.Offsetof permits parenthesized selectors as
    arguments.
    
    This is simply documenting existing compiler behavior
    (both gc and gccgo agree).
    
    R=r, rsc, iant, ken
    CC=golang-dev
    https://golang.org/cl/6498138

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

https://github.com/golang/go/commit/8c058b32d1838e67d67277564b6cb9b0bccd1e75

元コミット内容

このコミットの元の内容は以下の通りです。

  • 式ステートメントに関する記述を明確化する。
  • 関数呼び出しやメソッド呼び出しは有効な式ステートメントであるが、特定の組み込み関数を呼び出すことは許可されていない。これらの組み込み関数を列挙する。
  • unsafe.Offsetofが括弧で囲まれたセレクタを引数として許可することを明記する。
  • これらの変更は、既存のコンパイラ(gcとgccgoの両方)の挙動を単に文書化したものであり、コンパイラの動作自体を変更するものではない。

変更の背景

Go言語の仕様書は、言語の正確な挙動を定義する最も重要なドキュメントです。このコミットは、以下の2つの主要な側面において、既存のコンパイラの挙動と仕様書の記述との間にあった潜在的な曖昧さや不整合を解消することを目的としています。

  1. 式ステートメントとしての組み込み関数の使用: Go言語では、関数呼び出しやメソッド呼び出しは、その結果が使用されない場合でもステートメントとして記述できます(例: fmt.Println("Hello"))。しかし、一部の組み込み関数(例: len, cap, makeなど)は、値を生成するだけで副作用を持たないため、単独でステートメントとして使用することは意味がありません。コンパイラは歴史的にこれらの使用を禁止していましたが、仕様書にはその制限が明示されていませんでした。このコミットは、このコンパイラの挙動を仕様書に明記することで、開発者がより正確な言語ルールを理解できるようにします。これにより、コンパイラのエラーメッセージがより理解しやすくなり、予期せぬ挙動を避けることができます。

  2. unsafe.Offsetofの引数: unsafe.Offsetofは、構造体のフィールドのオフセットをバイト単位で取得するための特殊な関数です。この関数は、引数としてセレクタ(例: s.f)を取ります。コンパイラは、このセレクタが括弧で囲まれていても(例: (s.f))正しく処理していましたが、仕様書にはその柔軟性が明示されていませんでした。このコミットは、この既存の挙動を仕様書に反映させることで、ドキュメントと実装の整合性を高め、開発者がunsafe.Offsetofをより柔軟に使用できることを明確にします。

要するに、このコミットはGo言語の「仕様」を「実装」に合わせることで、言語の定義をより堅牢で明確なものにすることを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語の概念について基本的な知識が必要です。

1. 式ステートメント (Expression statements)

Go言語では、式(Expression)は値を生成するコードの断片です。例えば、a + bは式であり、5という値や"hello"という文字列も式です。 ステートメント(Statement)は、何らかのアクションを実行するコードの断片です。例えば、変数宣言(var x int)、代入(x = 10)、if文、forループなどがステートメントです。

「式ステートメント」とは、値を生成する式が、その値を使用せずに単独でステートメントとして記述される場合を指します。Go言語では、主に以下の種類の式が式ステートメントとして使用できます。

  • 関数呼び出し (Function calls): fmt.Println("Hello") のように、関数の戻り値が使用されない場合でも、関数呼び出し自体がステートメントとして機能します。
  • メソッド呼び出し (Method calls): file.Close() のように、メソッドの戻り値が使用されない場合でも、メソッド呼び出し自体がステートメントとして機能します。
  • 受信操作 (Receive operations): チャネルからの値の受信操作(例: <-ch)も、その受信した値が変数に代入されない場合でもステートメントとして使用できます。これは、チャネルからの受信がチャネルの状態を変更する副作用を持つためです。

しかし、すべての式が式ステートメントとして有効なわけではありません。例えば、1 + 2; のような単なる算術式は、Goでは有効なステートメントではありません。なぜなら、それは副作用を持たず、生成された値も使用されないため、意味がないからです。

2. 組み込み関数 (Built-in functions)

Go言語には、言語自体に組み込まれている特別な関数がいくつかあります。これらはimportすることなく直接使用できます。主な組み込み関数には以下のようなものがあります。

  • append: スライスに要素を追加する。
  • cap: スライス、マップ、チャネルの容量を返す。
  • complex: 浮動小数点数から複素数を作成する。
  • imag: 複素数の虚数部を返す。
  • len: スライス、文字列、マップ、チャネルの長さを返す。
  • make: スライス、マップ、チャネルを初期化して作成する。
  • new: 型のゼロ値へのポインタを割り当てる。
  • real: 複素数の実数部を返す。
  • panic: ランタイムパニックを引き起こす。
  • recover: パニックから回復する。
  • print, println: 低レベルの出力関数(デバッグ用)。
  • delete: マップから要素を削除する。
  • close: チャネルを閉じる。

これらの組み込み関数の中には、paniccloseのように副作用を持つものもありますが、lencapのように純粋に値を計算して返すだけのものもあります。後者のグループは、単独でステートメントとして使用しても意味がないため、Goコンパイラによって禁止されています。

3. unsafeパッケージとunsafe.Offsetof

unsafeパッケージは、Go言語の型安全性とメモリ安全性の保証を意図的にバイパスするための機能を提供します。このパッケージは、非常に特殊なケース(例えば、C言語との相互運用、高性能なデータ構造の実装など)でのみ使用されるべきであり、通常のGoプログラミングでは避けるべきです。

unsafeパッケージが提供する主要な関数の一つがunsafe.Offsetofです。

  • unsafe.Offsetof(x.f): この関数は、構造体xのフィールドfが、構造体の先頭から何バイト目に位置するか(オフセット)を返します。これは、メモリレイアウトを直接操作する必要がある場合に有用です。 例:
    package main
    
    import (
    	"fmt"
    	"unsafe"
    )
    
    type S struct {
    	A int32
    	B bool
    	C float64
    }
    
    func main() {
    	s := S{}
    	fmt.Println("Offset of A:", unsafe.Offsetof(s.A)) // 0
    	fmt.Println("Offset of B:", unsafe.Offsetof(s.B)) // 4 (on 64-bit systems, due to alignment)
    	fmt.Println("Offset of C:", unsafe.Offsetof(s.C)) // 8 (on 64-bit systems, due to alignment)
    }
    
    unsafe.Offsetofの引数は「セレクタ」(x.fのようなフィールドアクセス)である必要があります。このコミットは、このセレクタが括弧で囲まれていても(例: (s.A))有効であることを明確にしています。

4. セレクタ (Selectors)

Go言語において、セレクタは構造体のフィールドやインターフェースのメソッドにアクセスするために使用される構文です。例えば、object.fieldobject.method()のような形式です。

  • s.f: 構造体sのフィールドfにアクセスするセレクタ。
  • obj.Method(): オブジェクトobjのメソッドMethodを呼び出すセレクタ。

このコミットでは、unsafe.Offsetofの引数として、s.fだけでなく(s.f)のように括弧で囲まれたセレクタも有効であることが明記されました。これは、式の評価順序を明確にするために括弧を使用する一般的なプログラミングの慣習に沿ったものです。

技術的詳細

このコミットは、Go言語の仕様書(doc/go_spec.html)を更新することで、言語の挙動に関する2つの重要な側面を明確にしています。

1. 式ステートメントとしての組み込み関数の制限

Go言語の仕様では、関数呼び出し、メソッド呼び出し、およびチャネルからの受信操作は、その結果が使用されない場合でもステートメントとして記述できるとされています。しかし、このコミット以前は、特定の組み込み関数がこのルールから除外されることが明示されていませんでした。

このコミットによって、以下の組み込み関数が式ステートメントとして許可されないことが明確にされました。

  • append
  • cap
  • complex
  • imag
  • len
  • make
  • new
  • real
  • unsafe.Alignof
  • unsafe.Offsetof
  • unsafe.Sizeof

これらの関数は、値を計算して返すだけで、プログラムの状態に直接的な副作用(例えば、変数の変更、I/O操作、パニックの発生など)をもたらしません。したがって、これらの関数呼び出しの結果が使用されない場合、そのステートメントは意味がなく、コンパイラはエラーと見なします。

例: len("foo")3 という値を返しますが、この値がどこにも代入されたり使用されたりしない場合、len("foo") を単独のステートメントとして記述することはGoでは許可されません。これは、C言語における 1 + 2; のようなステートメントが意味を持たないのと同様の理由です。

この変更は、Goコンパイラ(特にgcとgccgo)が既に実施していた挙動を仕様書に反映させたものです。これにより、仕様書とコンパイラの実装との間の整合性が向上し、開発者は言語のルールをより正確に理解できるようになります。

2. unsafe.Offsetofの引数における括弧付きセレクタの許可

unsafe.Offsetof関数は、構造体のフィールドのオフセットをバイト単位で取得するために使用されます。この関数の引数は、構造体のフィールドへのセレクタ(例: s.f)である必要があります。

このコミットは、unsafe.Offsetofの引数として、括弧で囲まれたセレクタも有効であることを明確にしました。

変更前: 「関数 Offsetof は、任意の型の構造体フィールドを示すセレクタ(§セレクタ)を取り、そのフィールドの構造体のアドレスに対するオフセットをバイト単位で返します。」

変更後: 「関数 Offsetof は、(おそらく括弧で囲まれた) セレクタを取り、任意の型の構造体フィールドを示すもので、そのフィールドの構造体のアドレスに対するオフセットをバイト単位で返します。」

これは、unsafe.Offsetof((s.f)) のような記述が有効であることを明示しています。括弧は通常、式の評価順序を明確にするために使用されますが、この文脈ではセレクタ自体を括弧で囲むことが許可されることを示しています。これもまた、Goコンパイラが既にサポートしていた既存の挙動を仕様書に文書化したものです。

これらの変更は、Go言語の仕様をより厳密かつ包括的なものにし、開発者が言語の細部を正確に理解するのに役立ちます。

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

このコミットによる変更は、Go言語の公式仕様書である doc/go_spec.html ファイルに対して行われました。

具体的には、以下の2つのセクションが修正されています。

  1. Expression statements (式ステートメント) セクション:

    • 変更前:
      Function calls, method calls, and receive operations
      can appear in statement context. Such statements may be parenthesized.
      
    • 変更後:
      With the exception of specific built-in functions,
      function and method <a href="#Calls">calls</a> and
      <a href="#Receive_operator">receive operations</a>
      can appear in statement context. Such statements may be parenthesized.
      
    • さらに、以下の新しい段落が追加されました。
      <p>
      The following built-in functions are not permitted in statement context:
      </p>
      
      <pre>
      append cap complex imag len make new real
      unsafe.Alignof unsafe.Offsetof unsafe.Sizeof
      </pre>
      
    • そして、以下の不正な使用例が追加されました。
      len("foo")  // illegal if len is the built-in function
      
  2. unsafe.Offsetof の説明箇所:

    • 変更前:
      The function <code>Offsetof</code> takes a selector (§<a href="#Selectors">Selectors</a>) denoting a struct
      field of any type and returns the field offset in bytes relative to the
      
    • 変更後:
      The function <code>Offsetof</code> takes a (possibly parenthesized) <a href="#Selectors">selector</a>
      denoting a struct field of any type and returns the field offset in bytes relative to the
      

また、仕様書のバージョン日付も「Version of September 17, 2012」から「Version of September 18, 2012」に更新されています。

コアとなるコードの解説

1. 式ステートメントの明確化

変更の核心は、Go言語の式ステートメントに関するルールをより厳密に定義した点にあります。 以前の記述では、「関数呼び出し、メソッド呼び出し、および受信操作はステートメントコンテキストで出現できる」とされていました。これは一般的には正しいですが、lencapのような一部の組み込み関数は、値を返すだけで副作用を持たないため、単独のステートメントとして使用することは意味がありません。Goコンパイラは既にこのような使用をエラーとして扱っていましたが、仕様書にはその例外が明示されていませんでした。

新しい記述では、「特定の組み込み関数を除いて、関数およびメソッドの呼び出しと受信操作はステートメントコンテキストで出現できる」と修正され、さらに許可されない組み込み関数のリストが明示的に追加されました。

<p>
The following built-in functions are not permitted in statement context:
</p>

<pre>
append cap complex imag len make new real
unsafe.Alignof unsafe.Offsetof unsafe.Sizeof
</pre>

この変更により、開発者はどの組み込み関数が式ステートメントとして使用できないかを明確に理解できるようになります。これは、コンパイラのエラーメッセージがなぜ発生するのかを理解する上で非常に重要です。例えば、len("hello")という行だけを記述すると、コンパイラは「lenはステートメントとして使用できません」といったエラーを出すでしょう。この仕様の更新は、そのエラーの根拠を明確に示しています。

2. unsafe.Offsetofの引数における括弧付きセレクタの許可

unsafe.Offsetof関数の説明において、「セレクタ」という言葉に加えて「(おそらく括弧で囲まれた)」という表現が追加されました。

The function <code>Offsetof</code> takes a (possibly parenthesized) <a href="#Selectors">selector</a>
denoting a struct field of any type and returns the field offset in bytes relative to the

これは、unsafe.Offsetof(s.f)だけでなく、unsafe.Offsetof((s.f))のように、セレクタが括弧で囲まれていても有効であることを明示しています。Go言語の構文解析器は、このような括弧を正しく処理し、セレクタとして認識します。この変更は、既存のコンパイラの挙動を仕様書に反映させることで、ドキュメントと実装の間の整合性を高め、開発者がunsafe.Offsetofを使用する際の柔軟性を明確にしています。これは、特に複雑な式の中でunsafe.Offsetofを使用する場合に、開発者が括弧を使って意図を明確にできることを示唆しています。

これらの変更は、Go言語の仕様をより正確で、開発者にとって分かりやすいものにするための重要なステップです。

関連リンク

参考にした情報源リンク