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

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

このコミットは、Go言語の実験的な型チェッカーである exp/gotype パッケージに、より多くのテストを追加するものです。これにより、gotype がGo標準ライブラリの大部分を処理できるようになることを目指しています。

コミット

commit e93bdd998c1c4e46dac4b21d2dca251e6016d3f2
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Dec 6 09:23:13 2012 -0800

    exp/gotype: added many more tests
    
    gotype can now handle much of the standard library.
    
    - marked packages which have type checker issues
    - this CL depends on CL 6846131
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/6850130

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

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

元コミット内容

exp/gotype: added many more tests

gotype は現在、標準ライブラリの大部分を処理できます。

  • 型チェッカーの問題があるパッケージをマークしました
  • この変更リスト (CL) は CL 6846131 に依存します

変更の背景

Go言語の進化において、型システムは言語の健全性と堅牢性を保証する上で極めて重要な要素です。exp/gotype は、Go言語の型チェックメカニズムを実験的に探求し、改善するためのパッケージでした。このコミットが行われた2012年12月時点では、Go言語はまだ比較的新しく、そのツールチェイン、特に型チェッカーは継続的に開発・改善されていました。

このコミットの主な背景は、exp/gotype のカバレッジと堅牢性を向上させることにありました。具体的には、Go標準ライブラリの広範なパッケージに対して gotype をテストすることで、その型チェック能力を検証し、既存の課題(例えば、特定の言語機能のサポート不足や、複雑な型構造の処理における問題)を特定することが目的でした。これにより、gotype がより実用的な型チェッカーとして機能するための基盤を固めることが意図されています。

また、コミットメッセージにある「このCLはCL 6846131に依存します」という記述は、この変更が先行する別の変更(おそらく gotype 自体の機能改善やバグ修正)の上に成り立っていることを示唆しており、開発が段階的に進められていることを示しています。

前提知識の解説

Go言語の型システム

Go言語は静的型付け言語であり、プログラムのコンパイル時に型チェックが行われます。これにより、多くの一般的なプログラミングエラーを早期に発見し、実行時の安全性を高めることができます。Goの型システムは、構造体、インターフェース、ポインタ、スライス、マップなど、多様なデータ型をサポートしています。型チェックは、変数への値の代入、関数の引数と戻り値の型の一致、演算子の適用可能性などを検証します。

go/build パッケージ

go/build パッケージは、Goのソースコードパッケージを検索、解析、およびビルドするための情報を提供します。このパッケージは、Goのビルドシステムがどのように動作するかを理解し、Goのソースファイルやパッケージに関する情報をプログラム的に取得するために使用されます。例えば、特定のディレクトリにあるGoソースファイルからパッケージ情報を抽出し、そのパッケージが依存する他のパッケージを解決する能力を持っています。これは、Goのツール(コンパイラ、リンカ、goimportsgofmt など)が内部的に利用する重要なコンポーネントです。

exp/gotype パッケージ

exp/gotype は、Go言語の実験的な型チェッカーです。Goの標準ライブラリには、go/types パッケージという公式の型チェッカーが存在しますが、exp/gotype はその前身、あるいは異なるアプローチを試みるための実験的な実装であったと考えられます。型チェッカーの主な役割は、Goのソースコードを解析し、言語仕様に準拠した型付けが行われているかを検証することです。これには、変数の型推論、関数のシグネチャの一致、インターフェースの実装チェックなどが含まれます。実験的なパッケージであるため、当時の gotype はまだ開発途上にあり、標準ライブラリのすべてのケースを完全に処理できるわけではありませんでした。

CL (Change List)

Go言語の開発では、変更は「Change List (CL)」としてGerrit(コードレビューシステム)に提出されます。各CLは一意の番号を持ち、関連する一連の変更を含みます。コミットメッセージに「CL 6846131に依存します」とあるのは、このコミットがその先行するCLによって導入された機能や修正を前提としていることを意味します。

技術的詳細

このコミットの技術的な核心は、exp/gotype のテストハーネスを大幅に拡張し、Go標準ライブラリの広範なパッケージに対する型チェックの網羅性を高めた点にあります。

runTest 関数の変更

以前の runTest 関数は、pathpkg(パッケージ名)を直接引数として受け取っていました。しかし、変更後には path のみを受け取るように簡略化されました。この変更の背後には、テスト対象が単一のGoファイルであるか、あるいはパッケージディレクトリ全体であるかを runTest 内部で動的に判断し、適切な処理を行うという設計思想があります。

  • 単一ファイルの場合: path.go で終わる場合、それは単一のGoファイルとして扱われます。filepath.Join(runtime.GOROOT(), "src/pkg", path) を用いてファイルの絶対パスを構築し、filepath.Split でディレクトリとファイル名を分離します。パッケージ名はファイル名から .go 拡張子を除いたものとして推測されます。その後、processFiles 関数が呼び出され、指定されたファイルに対して型チェックが実行されます。
  • パッケージディレクトリの場合: path.go で終わらない場合、それはパッケージディレクトリとして扱われます。ここで重要なのは、go/build.Import 関数が導入されたことです。build.Import(path, "", 0) を呼び出すことで、指定されたパスのGoパッケージに関する詳細な情報(パッケージ名、ディレクトリ、含まれるGoファイルのリストなど)を取得します。これにより、gotype はGoのビルドシステムがパッケージをどのように解決するかを模倣し、より正確なパッケージの型チェックが可能になります。取得したパッケージ情報から、pkg.GoFiles を用いてパッケージ内のすべてのGoファイルの絶対パスを構築し、processFiles に渡して型チェックを実行します。

この go/build パッケージの利用は、gotype がGoの公式なパッケージ解決ロジックに準拠し、より現実的なシナリオでの型チェックを可能にするための重要なステップです。TODO(gri) gotype should use the build package instead というコメントは、このテストの変更が、将来的に gotype 自体が go/build パッケージを直接利用するようにリファクタリングされることを見越したものであることを示唆しています。

テスト対象パッケージの拡張

tests 変数は、以前は少数の go/ パッケージ(go/ast, go/build など)のみを対象としていましたが、このコミットにより、Go標準ライブラリの非常に多くのパッケージがテスト対象として追加されました。これには、archive/zip, bufio, bytes, compress/*, crypto/*, encoding/*, fmt, image/*, io, log, math, net/http, regexp, runtime, sort, strings, testing, text/*, unicode/* など、多岐にわたるパッケージが含まれます。

注目すべきは、追加されたパッケージの多くがコメントアウトされており、その理由が具体的に記されている点です。例えば:

  • // "archive/tar", // investigate (調査が必要)
  • // "encoding/binary", // complex() doesn't work yet (complex 型の処理がまだ不完全)
  • // "go/doc", // variadic parameters don't work yet fully (可変長引数の処理がまだ不完全)
  • // "net", // depends on C files (C言語のファイルに依存しているため、Goの型チェッカーだけでは完結しない)
  • // "sync", // platform-specific files (プラットフォーム固有のファイルが含まれるため、一般的なテストが難しい)

これらのコメントは、当時の exp/gotype が直面していた具体的な技術的課題を浮き彫りにしています。型チェッカーがGo言語のすべての機能を完全にサポートするためには、complex 型、可変長引数、Cgo(C言語との連携)、プラットフォーム固有のコードなど、様々な複雑なケースを正確に処理する必要があることが示されています。

testdata/test1.go の変更

testdata/test1.go のパッケージ名が package p から package test1 に変更されました。これは、テストデータの識別をより明確にするための軽微な変更であり、テストの構造や命名規則の改善の一環と考えられます。

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

src/pkg/exp/gotype/gotype_test.go

--- a/src/pkg/exp/gotype/gotype_test.go
+++ b/src/pkg/exp/gotype/gotype_test.go
@@ -5,20 +5,38 @@
 package main
 
 import (
+	"go/build"
 	"path/filepath"
 	"runtime"
+	"strings"
 	"testing"
 )
 
-func runTest(t *testing.T, path, pkg string) {
+func runTest(t *testing.T, path string) {
 	exitCode = 0
-	*pkgName = pkg
-	*recursive = false
+
+	*recursive = false
+	if suffix := ".go"; strings.HasSuffix(path, suffix) {
+		// single file
+		path = filepath.Join(runtime.GOROOT(), "src/pkg", path)
+		path, file := filepath.Split(path)
+		*pkgName = file[:len(file)-len(suffix)]
+		processFiles([]string{path}, true)
+	} else {
+		// package directory
+		// TODO(gri) gotype should use the build package instead
+		pkg, err := build.Import(path, "", 0)
+		if err != nil {
+			t.Errorf("build.Import error for path = %s: %s", path, err)
+			return
+		}
+		// TODO(gri) there ought to be a more direct way using the build package...
+		files := make([]string, len(pkg.GoFiles))
+		for i, file := range pkg.GoFiles {
+			files[i] = filepath.Join(pkg.Dir, file)
+		}
+		*pkgName = pkg.Name
+		processFiles(files, true)
+	}
 
-	if pkg == "" {
-		processFiles([]string{path}, true)
-	} else {
-		processDirectory(path)
-	}
-
 	if exitCode != 0 {
@@ -26,26 +44,167 @@ func runTest(t *testing.T, path, pkg string) {
 	}
 }
 
-var tests = []struct {
-	path string
-	pkg  string
-}{
+var tests = []string{
 	// individual files
-	{"testdata/test1.go", ""},
+	"exp/gotype/testdata/test1.go",
 
 	// directories
-	{filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"},
-	{filepath.Join(runtime.GOROOT(), "src/pkg/go/build"), "build"},
-	{filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"},
-	{filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"},
-	{filepath.Join(runtime.GOROOT(), "src/pkg/go/printer"), "printer"},
-	{filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"},
-	{filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "token"},
-	{filepath.Join(runtime.GOROOT(), "src/pkg/exp/types"), "types"},
+	// Note: packages that don't typecheck yet are commented out
+	// "archive/tar", // investigate
+	"archive/zip",
+
+	"bufio",
+	"bytes",
+
+	"compress/bzip2",
+	"compress/flate",
+	"compress/gzip",
+	"compress/lzw",
+	"compress/zlib",
+
+	"container/heap",
+	"container/list",
+	"container/ring",
+
+	"crypto",
+	"crypto/aes",
+	"crypto/cipher",
+	"crypto/des",
+	"crypto/dsa",
+	"crypto/ecdsa",
+	"crypto/elliptic",
+	"crypto/hmac",
+	"crypto/md5",
+	"crypto/rand",
+	"crypto/rc4",
+	"crypto/rsa",
+	"crypto/sha1",
+	"crypto/sha256",
+	"crypto/sha512",
+	"crypto/subtle",
+	"crypto/tls",
+	// "crypto/x509", // investigate
+	"crypto/x509/pkix",
+
+	"database/sql",
+	"database/sql/driver",
+
+	"debug/dwarf",
+	"debug/elf",
+	"debug/gosym",
+	"debug/macho",
+	"debug/pe",
+
+	"encoding/ascii85",
+	"encoding/asn1",
+	"encoding/base32",
+	"encoding/base64",
+	// "encoding/binary", // complex() doesn't work yet
+	"encoding/csv",
+	// "encoding/gob", // complex() doesn't work yet
+	"encoding/hex",
+	"encoding/json",
+	"encoding/pem",
+	"encoding/xml",
+
+	"errors",
+	"expvar",
+	"flag",
+	"fmt",
+
+	"exp/types",
+	"exp/gotype",
+
+	"go/ast",
+	"go/build",
+	// "go/doc", // variadic parameters don't work yet fully
+	"go/format",
+	"go/parser",
+	"go/printer",
+	"go/scanner",
+	"go/token",
+
+	"hash/adler32",
+	// "hash/crc32", // investigate
+	"hash/crc64",
+	"hash/fnv",
+
+	"image",
+	"image/color",
+	"image/draw",
+	"image/gif",
+	"image/jpeg",
+	"image/png",
+
+	"index/suffixarray",
+
+	"io",
+	// "io/ioutil", // investigate
+
+	"log",
+	"log/syslog",
+
+	"math",
+	// "math/big", // investigate
+	// "math/cmplx", // complex doesn't work yet
+	"math/rand",
+
+	"mime",
+	"mime/multipart",
+
+	// "net", // depends on C files
+	"net/http",
+	"net/http/cgi",
+	// "net/http/fcgi", // investigate
+	"net/http/httptest",
+	"net/http/httputil",
+	// "net/http/pprof", // investigate
+	"net/mail",
+	// "net/rpc", // investigate
+	"net/rpc/jsonrpc",
+	"net/smtp",
+	"net/textproto",
+	"net/url",
+
+	// "path", // variadic parameters don't work yet fully
+	// "path/filepath", // investigate
+
+	// "reflect", // investigate
+
+	"regexp",
+	"regexp/syntax",
+
+	"runtime",
+	// "runtime/cgo", // import "C"
+	"runtime/debug",
+	"runtime/pprof",
+
+	"sort",
+	// "strconv", // investigate
+	"strings",
+
+	// "sync", // platform-specific files
+	// "sync/atomic", // platform-specific files
+
+	// "syscall", // platform-specific files
+
+	"testing",
+	"testing/iotest",
+	"testing/quick",
+
+	"text/scanner",
+	"text/tabwriter",
+	// "text/template", // variadic parameters don't work yet fully
+	// "text/template/parse", // variadic parameters don't work yet fully
+
+	// "time", // platform-specific files
+	"unicode",
+	"unicode/utf16",
+	"unicode/utf8",
+}
+
+func Test(t *testing.T) {
 	for _, test := range tests {
-		runTest(t, test.path, test.pkg)
+		runTest(t, test)
 	}
 }

src/pkg/exp/gotype/testdata/test1.go

--- a/src/pkg/exp/gotype/testdata/test1.go
+++ b/src/pkg/exp/gotype/testdata/test1.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package p
+package test1
 
 func _() {
 	// the scope of a local type declaration starts immediately after the type name

コアとなるコードの解説

gotype_test.go の変更点

  1. runTest 関数のシグネチャ変更と内部ロジックの強化:

    • 変更前: func runTest(t *testing.T, path, pkg string)
    • 変更後: func runTest(t *testing.T, path string)
    • この変更により、runTest はテスト対象のパス(ファイルまたはディレクトリ)のみを受け取るようになりました。
    • 内部では、strings.HasSuffix(path, suffix) を使用して、パスが .go で終わるかどうかをチェックし、単一のGoファイルかパッケージディレクトリかを判別します。
    • 単一ファイルの場合: filepath.Join(runtime.GOROOT(), "src/pkg", path) でGoのソースツリー内の絶対パスを構築し、filepath.Split でファイル名とディレクトリを分離します。*pkgName グローバル変数にファイル名から拡張子を除いたものを設定し、processFiles 関数を呼び出して型チェックを実行します。
    • パッケージディレクトリの場合: go/build パッケージの build.Import 関数を導入しました。これは、Goのビルドシステムがパッケージをどのように解決するかを模倣し、指定されたパスのパッケージに関する情報(パッケージ名、ディレクトリ、含まれるGoファイルのリストなど)を取得するために使用されます。これにより、gotype はより正確なパッケージの型チェックが可能になります。取得した pkg.GoFiles をループして、各Goファイルの絶対パスを構築し、processFiles に渡して型チェックを実行します。
    • TODO(gri) gotype should use the build package instead というコメントは、このテストの変更が、将来的に gotype 自体が go/build パッケージを直接利用するようにリファクタリングされることを見越したものであることを示唆しています。
  2. tests 変数の構造変更とテスト対象の拡張:

    • 変更前: var tests = []struct { path string; pkg string }
    • 変更後: var tests = []string
    • runTest 関数のシグネチャ変更に合わせて、tests スライスは単にテスト対象のパッケージパス(またはファイルパス)の文字列のリストになりました。
    • 最も顕著な変更は、Go標準ライブラリの非常に多くのパッケージがこのリストに追加されたことです。これにより、exp/gotype の型チェック能力が広範なコードベースに対して検証されるようになりました。
    • コメントアウトされたパッケージとその理由(例: complex() がまだ動作しない、可変長引数が不完全、Cファイルに依存、プラットフォーム固有のファイルなど)は、当時の gotype が直面していた具体的な課題を示しています。

testdata/test1.go の変更点

  • パッケージ名が package p から package test1 に変更されました。これは、テストデータの識別をより明確にするための軽微な変更です。

これらの変更は、exp/gotype がGo標準ライブラリの大部分を型チェックできるようになるための重要なステップであり、型チェッカーの堅牢性とカバレッジを向上させることを目的としています。特に go/build パッケージの利用は、Goの公式なパッケージ解決ロジックとの整合性を高める上で重要です。

関連リンク

参考にした情報源リンク