[インデックス 11661] ファイルの概要
このコミットは、Go言語のcgo
ツールにおいて、致命的なエラー発生時に適切な行番号を出力するように改善するものです。これにより、cgo
のユーザーはエラーの発生源をより正確に特定できるようになります。具体的には、fatalf
関数および関連するヘルパー関数のシグネチャが統一され、エラーメッセージに行番号情報が付加されるようになりました。
コミット
commit 0d07600de3ec06204fb71906c136421dce69e206
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Mon Feb 6 20:38:54 2012 +0100
cgo: print line numbers in fatal errors when relevant.
Signatures of fatalf and error_ helpers have been matched for
consistency.
Fixes #1800.
R=rsc
CC=golang-dev, remy
https://golang.org/cl/5593049
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0d07600de3ec06204fb71906c136421dce69e206
元コミット内容
cgo: print line numbers in fatal errors when relevant.
Signatures of fatalf and error_ helpers have been matched for consistency.
Fixes #1800.
R=rsc
CC=golang-dev, remy
https://golang.org/cl/5593049
変更の背景
この変更の背景には、cgo
ツールがC言語のコードをGo言語のコードに変換する際に発生するエラーメッセージの改善要求がありました。以前のバージョンでは、cgo
が致命的なエラーに遭遇した場合、エラーメッセージには問題の種類は示されるものの、そのエラーがソースコードのどの位置(ファイル名と行番号)で発生したのかという情報が欠落していました。
特に、Goの型とCの型を変換する過程で発生する「型変換ループ」や「予期しない型」といったエラーは、デバッグを困難にしていました。開発者はエラーメッセージだけでは問題の根本原因を特定しにくく、手動で関連するコード箇所を探す必要がありました。
この問題はGoのIssue #1800として報告されており、エラーメッセージに行番号情報を含めることで、cgo
のデバッグ体験を大幅に向上させることが目的とされました。これにより、開発者はエラーメッセージから直接問題の箇所にジャンプできるようになり、開発効率が向上します。
前提知識の解説
このコミットを理解するためには、以下の概念について理解しておく必要があります。
- cgo: Go言語のプログラムからC言語のコードを呼び出すためのGoのツールです。
import "C"
という特殊なインポート宣言を使用することで、Cの関数やデータ構造をGoのコードから利用できるようになります。cgo
は、Cのヘッダーファイルを解析し、GoとCの間でデータをやり取りするための接着コード(binding code)を生成します。 - DWARF (Debugging With Attributed Record Formats): コンパイルされたプログラムのデバッグ情報を格納するための標準的なフォーマットです。
cgo
は、Cコンパイラが生成したDWARF情報からCの型定義を読み取り、それをGoの対応する型に変換します。このDWARF情報には、ソースコードのファイル名や行番号といった位置情報も含まれています。 token.Pos
: Go言語の標準ライブラリgo/token
パッケージで定義されている型です。これは、Goのソースコード内の特定の位置(ファイル名、行番号、列番号)を表すために使用されます。コンパイラやツールがソースコードを解析する際に、エラーや警告の発生箇所を正確に報告するために不可欠な情報です。- 型変換 (Type Conversion):
cgo
の主要な機能の一つで、C言語の型(例:int
,struct MyStruct
,char*
)をGo言語の対応する型(例:int32
,struct { ... }
,*C.char
)に変換するプロセスです。この変換は複雑であり、特にポインタ、配列、構造体、共用体などの複雑な型の場合に、予期せぬ問題が発生することがあります。 fatalf
関数: Goのプログラム、特にツールやコンパイラにおいて、回復不能なエラーが発生した場合に、エラーメッセージを出力してプログラムを終了させるために使用される関数です。通常、fmt.Errorf
やlog.Fatalf
のような標準的なエラー報告メカニズムと同様の役割を果たしますが、cgo
の文脈では内部的なエラー報告に使用されます。
これらの概念を理解することで、cgo
がどのようにCの型情報を処理し、エラーが発生した際にどのようにその位置を特定して報告するようになったのかを把握できます。
技術的詳細
このコミットの技術的詳細は、主にcgo
の型変換ロジックとエラー報告メカニズムの変更に集約されます。
-
token.Pos
の伝播:src/cmd/cgo/gcc.go
内のPackage.loadDWARF
関数が、DWARF情報からCの型を読み込む際に、その型が定義されているソースコード上の位置情報(token.Pos
)を取得するようになりました。- この位置情報は、
conv.FuncType
やconv.Type
といった型変換を行う関数に新たな引数pos token.Pos
として渡されるようになりました。これにより、型変換の過程でエラーが発生した場合でも、そのエラーがどのソースコードの行に関連しているかを特定できるようになります。 nameToRef
という新しいマップが導入され、*Name
(DWARFのシンボル名)から*Ref
(DWARFの参照情報、位置情報を含む)へのマッピングを保持することで、位置情報の取得を効率化しています。
-
エラーメッセージへの行番号の追加:
src/cmd/cgo/util.go
にlineno(pos token.Pos) string
という新しいヘルパー関数が追加されました。この関数は、token.Pos
オブジェクトを受け取り、fset.Position(pos).String()
を呼び出すことで、「ファイル名:行番号」形式の文字列(例:main.go:15
)を生成します。src/cmd/cgo/gcc.go
内のtypeConv
構造体のメソッド(Type
,FuncArg
,FuncType
,Struct
など)内で呼び出されるfatalf
関数が、このlineno(pos)
の戻り値を最初の引数として受け取るように変更されました。- 例えば、
fatalf("type conversion loop at %s", dtype)
はfatalf("%s: type conversion loop at %s", lineno(pos), dtype)
のように変更され、エラーメッセージの先頭に位置情報が付加されるようになりました。
-
fatalf
および関連ヘルパー関数のシグネチャの一貫性:- コミットメッセージにあるように、
fatalf
およびerror_
ヘルパー関数のシグネチャが、行番号情報を渡すための一貫した形式に調整されました。これにより、コードベース全体でエラー報告のパターンが統一され、保守性が向上します。
- コミットメッセージにあるように、
-
テストケースの追加:
test/fixedbugs/bug408.go
という新しいテストファイルが追加されました。このテストは、cgo
が意図的にエラーを発生させるようなコード(例:C.printf(nil)
)を含み、そのエラーメッセージに行番号が含まれていることをerrchk
ディレクティブで検証します。これにより、変更が正しく機能していることが保証されます。
これらの変更により、cgo
はより詳細でデバッグしやすいエラーメッセージを提供するようになり、開発者はcgo
関連の問題を迅速に特定し、解決できるようになりました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。
-
src/cmd/cgo/gcc.go
:Package.loadDWARF
関数内で、nameToRef
マップの導入と、token.Pos
の取得ロジックが追加されました。conv.FuncType
とconv.Type
の呼び出しにpos
引数が追加されました。typeConv
構造体のメソッド(Type
,FuncArg
,FuncType
,Struct
)のシグネチャにpos token.Pos
引数が追加され、内部のfatalf
呼び出しに行番号情報が渡されるようになりました。
-
src/cmd/cgo/util.go
:lineno(pos token.Pos) string
ヘルパー関数が新しく追加されました。
-
test/fixedbugs/bug408.go
:- 行番号付きエラーメッセージの出力を検証するための新しいテストケースが追加されました。
src/cmd/cgo/gcc.go
の変更例:
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -597,12 +601,16 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
if types[i] == nil {
continue
}
+ pos := token.NoPos
+ if ref, ok := nameToRef[n]; ok {
+ pos = ref.Pos()
+ }
f, fok := types[i].(*dwarf.FuncType)
if n.Kind != "type" && fok {
n.Kind = "func"
- n.FuncType = conv.FuncType(f)
+ n.FuncType = conv.FuncType(f, pos)
} else {
- n.Type = conv.Type(types[i])
+ n.Type = conv.Type(types[i], pos)
if enums[i] != 0 && n.Type.EnumValues != nil {
k := fmt.Sprintf("__cgo_enum__%d", i)
n.Kind = "const"
@@ -972,10 +980,10 @@ func (tr *TypeRepr) Set(repr string, fargs ...interface{}) {
// Type returns a *Type with the same memory layout as
// dtype when used as the type of a variable or a struct field.
-func (c *typeConv) Type(dtype dwarf.Type) *Type {
+func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
if t, ok := c.m[dtype]; ok {
if t.Go == nil {
- fatalf("type conversion loop at %s", dtype)
+ fatalf("%s: type conversion loop at %s", lineno(pos), dtype)
}
return t
}
src/cmd/cgo/util.go
の変更例:
--- a/src/cmd/cgo/util.go
+++ b/src/cmd/cgo/util.go
@@ -64,6 +64,10 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
return
}
+func lineno(pos token.Pos) string {
+ return fset.Position(pos).String()
+}
+
// Die with an error message.
func fatalf(msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\\n", args...)
test/fixedbugs/bug408.go
の追加:
--- /dev/null
+++ b/test/fixedbugs/bug408.go
@@ -0,0 +1,16 @@
+// errchk cgo $D/$F.go
+
+// Copyright 2012 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 1800: cgo not reporting line numbers.
+
+package main
+
+// #include <stdio.h>
+import "C"
+
+func f() {
+ C.printf(nil) // ERROR "go:15.*unexpected type"
+}
コアとなるコードの解説
このコミットの核心は、cgo
がCの型情報をGoの型に変換する際に、その変換元となるCのコードの位置情報(ファイル名と行番号)を保持し、エラー発生時にその情報をエラーメッセージに含めるようにした点です。
-
位置情報の取得と伝播 (
src/cmd/cgo/gcc.go
):Package.loadDWARF
関数は、Cコンパイラが生成したDWARFデバッグ情報から型定義を読み込みます。この過程で、nameToRef
マップが導入され、DWARFのシンボル名(*Name
)と、そのシンボルが定義されているソースコード上の位置情報を含む*Ref
オブジェクトを関連付けます。pos := token.NoPos
で初期化されたpos
変数に、ref.Pos()
を介して実際の位置情報が格納されます。- この
pos
変数は、conv.FuncType
やconv.Type
といった型変換を行う関数に新たな引数として渡されます。これにより、型変換ロジックの深い階層まで位置情報が伝播されるようになります。
-
エラーメッセージの整形 (
src/cmd/cgo/util.go
):- 新しく追加された
lineno(pos token.Pos) string
関数は、token.Pos
オブジェクトを人間が読める形式の文字列(例:filename.go:line_number
)に変換する役割を担います。fset.Position(pos).String()
がこの変換を実行します。 fatalf
関数は、cgo
が致命的なエラーを報告する際に使用されます。変更後、fatalf
の呼び出し元は、エラーメッセージのフォーマット文字列の最初のプレースホルダーにlineno(pos)
の戻り値を渡すようになりました。これにより、エラーメッセージの冒頭にエラー発生箇所のファイル名と行番号が自動的に付加されます。
- 新しく追加された
-
型変換ロジックの変更 (
src/cmd/cgo/gcc.go
内のtypeConv
メソッド群):typeConv
構造体のType
,FuncArg
,FuncType
,Struct
といった主要な型変換メソッドのシグネチャにpos token.Pos
が追加されました。- これらのメソッド内で発生する
fatalf
呼び出しはすべて、lineno(pos)
を引数として受け取るように変更されました。例えば、型変換ループを検出した際のエラーメッセージは、単に「type conversion loop at ...」ではなく、「filename.go:line_number
: type conversion loop at ...」のように出力されるようになります。 - 再帰的に型変換を行う箇所(例: 配列の要素型、ポインタの指す型、構造体のフィールド型を変換する際)では、親の型から受け取った
pos
を子要素の型変換関数に引き渡すことで、エラー発生時の正確な位置情報を保証しています。
-
テストによる検証 (
test/fixedbugs/bug408.go
):- この新しいテストファイルは、
cgo
がエラーを発生させる特定のシナリオ(この場合はC.printf(nil)
という不正なC関数呼び出し)を記述しています。 // ERROR "go:15.*unexpected type"
というコメントは、errchk
ツールに対する指示であり、この行でcgo
が「go:15」という行番号と「unexpected type」という文字列を含むエラーメッセージを出力することを期待しています。これにより、行番号の出力機能が正しく実装されていることが自動的に検証されます。
- この新しいテストファイルは、
これらの変更により、cgo
のエラーメッセージは格段に分かりやすくなり、cgo
を使用する開発者のデバッグ作業が大幅に効率化されました。
関連リンク
- Go Issue #1800: https://code.google.com/p/go/issues/detail?id=1800 (元のGoプロジェクトのIssueトラッカーはGoogle CodeからGitHubへ移行しているため、直接のリンクは機能しない可能性がありますが、このIssue番号が変更のトリガーとなりました。)
- Go CL 5593049: https://golang.org/cl/5593049 (GoのコードレビューシステムGerritの変更リストへのリンク)
参考にした情報源リンク
- Go言語の
cgo
に関する公式ドキュメント: https://pkg.go.dev/cmd/cgo - Go言語の
go/token
パッケージのドキュメント: https://pkg.go.dev/go/token - DWARFフォーマットに関する一般的な情報 (例: Wikipedia): https://en.wikipedia.org/wiki/DWARF
- Go言語のソースコード (特に
src/cmd/cgo
ディレクトリ): https://github.com/golang/go/tree/master/src/cmd/cgo - Go言語のテストフレームワークと
errchk
ディレクティブに関する情報 (Goのテストコードやドキュメント): https://go.dev/doc/code