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

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

このコミットは、Go言語の抽象構文木(AST)とパーサーに関する変更です。具体的には、Go 1.2で導入された3インデックススライス式(a[low:high:max])を正確に表現し、型チェックを可能にするための基盤を構築しています。

コミット

commit 20db0f428a28a529146b5016b97061f2b13c54d4
Author: Robert Griesemer <gri@golang.org>
Date:   Tue Sep 24 16:35:35 2013 -0700

    go/ast: add Slice3 field to SliceExpr
    
    If Slice3 is set, the expression is
    a 3-index slice expression (2 colons).
    Required for type-checking.
    
    Backward-compatible API extension.
    
    R=r, rsc
    CC=golang-dev
    https://golang.org/cl/13826050

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

https://github.com/golang/go/commit/20db0f428a28a529146b5016b97061f2b13c54d4

元コミット内容

go/astパッケージのSliceExpr構造体にSlice3フィールドを追加し、go/parserパッケージで3インデックススライス式が検出された場合にこのフィールドをtrueに設定するように変更しました。これにより、2つのコロン(:)を含むスライス式(例: a[low:high:max])をAST上で正確に表現できるようになり、後続の型チェック処理で利用されます。この変更は後方互換性のあるAPI拡張です。

変更の背景

Go言語では、スライスは配列の一部を参照するための強力なデータ構造です。初期のGo言語では、スライス式はa[low:high]のように2つのインデックス(開始と終了)を持つ形式が一般的でした。しかし、スライスの「容量(capacity)」を明示的に指定して新しいスライスを作成する、あるいは既存のスライスから容量を制限した新しいスライスを作成するニーズがありました。

Go 1.2で、このニーズに応えるために3インデックススライス式(a[low:high:max])が導入されました。ここでmaxは新しいスライスの容量を決定します。この機能が言語仕様に追加されるにあたり、コンパイラがこの新しい構文を正しく解析し、ASTに表現し、そして型チェックを行うための変更が必要となりました。

このコミットは、そのための第一歩として、ASTが3インデックススライス式の存在を認識できるようにするためのものです。ASTはコンパイラのフロントエンドがソースコードを解析して生成する中間表現であり、その後の型チェック、最適化、コード生成といったフェーズで利用されます。したがって、新しい言語機能が導入される際には、まずASTがその機能を表現できるようになる必要があります。

前提知識の解説

Go言語のスライス

Go言語のスライスは、基盤となる配列への参照です。スライスは「長さ(length)」と「容量(capacity)」という2つの重要なプロパティを持ちます。

  • 長さ (length): スライスに含まれる要素の数。len(s)で取得できます。
  • 容量 (capacity): スライスの基盤となる配列の、スライスの開始位置から数えた要素の総数。cap(s)で取得できます。

通常のスライス式 s[low:high] は、lowからhigh-1までの要素を含む新しいスライスを作成します。この新しいスライスの長さは high - low で、容量は cap(s) - low です。

3インデックススライス式 (s[low:high:max])

Go 1.2で導入された3インデックススライス式 s[low:high:max] は、新しいスライスの容量を明示的に指定することを可能にします。

  • low: 新しいスライスの開始インデックス。
  • high: 新しいスライスの終了インデックス(high-1までが含まれる)。
  • max: 新しいスライスの容量。新しいスライスの容量は max - low となります。

このmaxインデックスは、元のスライスまたは配列の容量の範囲内である必要があり、high以上である必要があります。この機能は、特にスライスを関数に渡す際に、そのスライスがアクセスできる基盤配列の範囲を制限したい場合や、append操作によって基盤配列が再割り当てされるのを防ぎたい場合に有用です。

抽象構文木 (AST)

抽象構文木(Abstract Syntax Tree, AST)は、ソースコードの抽象的な構文構造を木構造で表現したものです。コンパイラの字句解析器(lexer)と構文解析器(parser)によって生成されます。ASTは、ソースコードの具体的な構文(括弧やセミコロンなど)を省略し、プログラムの論理的な構造に焦点を当てます。

Go言語のコンパイラでは、go/astパッケージがASTのノード型を定義しており、go/parserパッケージがソースコードを解析してASTを構築します。コンパイラの各フェーズ(型チェック、最適化など)は、このASTを走査して処理を進めます。

go/astパッケージとSliceExpr構造体

go/astパッケージは、Go言語のソースコードのASTを表現するための型を提供します。スライス式はast.SliceExpr構造体で表現されます。この構造体は、スライスされる元の式(X)、角括弧の位置(Lbrack, Rbrack)、そしてlowhighmaxの各インデックスを表すフィールド(Low, High, Max)を持ちます。

go/parserパッケージ

go/parserパッケージは、Go言語のソースコードを解析し、go/astパッケージで定義されたASTを構築する機能を提供します。ソースコードの文字列やファイルパスを受け取り、対応するASTのルートノード(通常は*ast.File)を返します。

技術的詳細

このコミットの技術的詳細は、Go言語のコンパイラが新しい言語構文をどのように内部的に処理するかを示しています。

  1. ASTの拡張: src/pkg/go/ast/ast.go にある SliceExpr 構造体に Slice3 bool フィールドが追加されました。

    type (
        // ...
        SliceExpr struct {
            X      Expr      // expression being sliced
            Lbrack token.Pos // position of "["
            Low    Expr      // begin of slice range; or nil
            High   Expr      // end of slice range; or nil
            Max    Expr      // maximum capacity of slice; or nil
            Slice3 bool      // true if 3-index slice (2 colons present)
            Rbrack token.Pos // position of "]"
        }
        // ...
    )
    

    このSlice3フィールドは、スライス式が2つのコロン(:)を持つ3インデックス形式であるかどうかを示すブール値です。これにより、ASTの構造自体が、2インデックススライス(a[low:high])と3インデックススライス(a[low:high:max])を区別できるようになります。これは、後続の型チェックフェーズで、スライスの長さと容量の計算ロジックを適切に適用するために不可欠です。

  2. パーサーの変更: src/pkg/go/parser/parser.go にある parseIndexOrSlice 関数が変更されました。この関数は、配列、スライス、または文字列のインデックスアクセスまたはスライス式を解析する役割を担っています。 変更前:

    // ...
    if ncolons > 0 {
        // slice expression
        return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Rbrack: rbrack}
    }
    // ...
    

    変更後:

    // ...
    if ncolons > 0 {
        // slice expression
        return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: ncolons == 2, Rbrack: rbrack}
    }
    // ...
    

    ここでncolonsは、解析中に検出されたコロンの数を表します。

    • ncolons == 1 の場合: a[low:high] のような2インデックススライス式。
    • ncolons == 2 の場合: a[low:high:max] のような3インデックススライス式。 変更後のコードでは、Slice3フィールドがncolons == 2の場合にtrueに設定されるようになりました。これにより、パーサーがソースコードから3インデックススライス式を認識し、その情報をASTに正確に埋め込むことができるようになります。

この変更は、Goコンパイラのフロントエンドにおける重要なステップであり、新しい言語機能のサポートを可能にするための基盤となります。ASTにこの情報が追加されることで、型チェッカーはmaxインデックスが有効であるか、highmaxの関係が正しいか、そして結果として得られるスライスの容量が適切に計算されるかといった検証を行うことができます。

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

src/pkg/go/ast/ast.go

SliceExpr構造体にSlice3 boolフィールドが追加されました。

--- a/src/pkg/go/ast/ast.go
+++ b/src/pkg/go/ast/ast.go
@@ -298,6 +298,7 @@ type (
 		Low    Expr      // begin of slice range; or nil
 		High   Expr      // end of slice range; or nil
 		Max    Expr      // maximum capacity of slice; or nil
+		Slice3 bool      // true if 3-index slice (2 colons present)
 		Rbrack token.Pos // position of "]"
 	}
 

src/pkg/go/parser/parser.go

parseIndexOrSlice関数内でSliceExprを生成する際に、Slice3フィールドにncolons == 2の結果が設定されるようになりました。

--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -1187,7 +1187,7 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
 
 	if ncolons > 0 {
 		// slice expression
-		return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Rbrack: rbrack}
+		return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: ncolons == 2, Rbrack: rbrack}
 	}
 
 	return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}

コアとなるコードの解説

src/pkg/go/ast/ast.go の変更

SliceExpr構造体は、Go言語のスライス式(例: a[low:high]a[low:high:max])をAST上で表現するためのものです。このコミットで追加されたSlice3 boolフィールドは、そのスライス式が3つのインデックス(つまり2つのコロン)を持つ形式であるかどうかを明示的に示すフラグです。

  • trueの場合: a[low:high:max] の形式。
  • falseの場合: a[low:high] の形式(またはa[:high]a[low:]a[:]など、maxが指定されていない形式)。

このフラグは、ASTを走査する後続のコンパイラフェーズ(特に型チェッカー)が、スライスの長さと容量を計算する際に、どのルールを適用すべきかを判断するために使用されます。例えば、3インデックススライスの場合、結果のスライスの容量はmax - lowとして計算されますが、2インデックススライスではcap(original_slice) - lowとなります。このSlice3フィールドがなければ、型チェッカーはMaxフィールドが存在するかどうかだけで判断することになり、それはMaxnilでない場合にのみ機能しますが、Maxnilでない場合でも2インデックススライスである可能性を考慮できません。Slice3フラグは、この曖昧さを解消し、より堅牢な型チェックを可能にします。

src/pkg/go/parser/parser.go の変更

parseIndexOrSlice関数は、Goのパーサーの中核部分であり、角括弧[]で囲まれた式(インデックスアクセスやスライス式)を解析します。この関数は、コロンの数(ncolons)を数えることで、それがインデックスアクセスなのか、2インデックススライスなのか、3インデックススライスなのかを判別します。

変更前のコードでは、ncolons > 0(つまりスライス式である)場合に、一律にSliceExprを生成していました。しかし、Slice3フィールドが追加されたことで、パーサーはスライス式の種類をより詳細に区別してASTに反映させる必要があります。

変更後のコードでは、Slice3: ncolons == 2という条件が追加されました。

  • ncolons == 1 の場合(例: a[low:high]): Slice3falseになります。
  • ncolons == 2 の場合(例: a[low:high:max]): Slice3trueになります。

これにより、パーサーはソースコードの構文を正確にASTにマッピングし、3インデックススライス式の存在をASTを通じてコンパイラの他の部分に伝えることができるようになります。これは、Go 1.2で導入された3インデックススライス機能が、言語の構文解析から型チェック、そして最終的なコード生成まで、コンパイラ全体で正しく処理されるための基盤を築く重要な変更です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (go.dev)
  • Go言語のソースコード (github.com/golang/go)
  • Go言語のコミット履歴
  • Go言語のIssueトラッカー (golang.org/issue)
  • Go言語のメーリングリスト (golang-dev)
  • 各種Go言語に関する技術ブログや解説記事 (3インデックススライスに関するもの)