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

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

このコミットは、Goコンパイラ(gc)における出力(printing)メカニズムの大規模なクリーンアップと再構築を目的としています。主な変更点は、従来の「魔法の謎のグローバル変数」に依存していた出力処理を廃止し、新しいfmt.cファイルに集約された、より構造化されたフォーマットシステムを導入したことです。

具体的には、以下のファイルに影響があります。

  • src/cmd/gc/Makefile: print.cの削除とfmt.cの追加に伴うビルド設定の変更。
  • src/cmd/gc/const.c, src/cmd/gc/dcl.c, src/cmd/gc/esc.c, src/cmd/gc/export.c, src/cmd/gc/gen.c, src/cmd/gc/go.h, src/cmd/gc/go.y, src/cmd/gc/lex.c, src/cmd/gc/obj.c, src/cmd/gc/range.c, src/cmd/gc/reflect.c, src/cmd/gc/subr.c, src/cmd/gc/typecheck.c, src/cmd/gc/unsafe.c, src/cmd/gc/walk.c: これらのファイルでは、古い出力関数への呼び出しが新しいフォーマット関数に置き換えられています。特に、%#Nのようなフォーマット指定子が%Nに変更されている箇所が多く見られます。
  • src/cmd/gc/fmt.c: 新規追加されたファイル。Goコンパイラの新しい出力フォーマットロジックがここに実装されています。
  • src/cmd/gc/print.c: 削除されたファイル。従来の出力処理を担っていたファイルが完全に置き換えられました。
  • src/pkg/fmt/fmt_test.go, src/pkg/reflect/all_test.go, test/ddd1.go, test/fixedbugs/bug340.go, test/named1.go: テストファイルにおける出力の変更や、新しいフォーマットシステムへの対応が行われています。

この変更により、コンパイラのデバッグ出力、シンボル出力、エクスポート出力の制御がより明確になり、将来的な変更や拡張が容易になりました。

コミット

  • コミットハッシュ: 50110c9f83ee0cfa2909ca78a67f14dcca2e83c1
  • Author: Luuk van Dijk lvd@golang.org
  • Date: Mon Oct 31 18:09:40 2011 +0100

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

https://github.com/golang/go/commit/50110c9f83ee0cfa2909ca78a67f14dcca2e83c1

元コミット内容

    gc: clean up printing.
    
    Got rid of all the magic mystery globals. Now
    for %N, %T, and %S, the flags +,- and # set a sticky
    debug, sym and export mode, only visible in the new fmt.c.
    Default is error mode. Handle h and l flags consistently with
    the least side effects, so we can now change
    things without worrying about unrelated things
    breaking.
    
    fixes #2361
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5316043

変更の背景

このコミットの背景には、Goコンパイラ(gc)の内部における出力処理の複雑さと、それに伴う保守性の問題がありました。従来の出力システムは、おそらく複数のグローバル変数や暗黙的な状態に依存しており、これが「魔法の謎のグローバル変数」と表現されています。このような設計は、以下のような問題を引き起こす可能性があります。

  1. 副作用の発生: ある出力設定の変更が、意図しない別の出力に影響を与える可能性がありました。コミットメッセージにある「unrelated things breaking(無関係なものが壊れる)」という表現がこれを裏付けています。
  2. コードの理解と保守の困難さ: グローバル変数に依存した状態管理は、コードの挙動を追跡することを困難にし、新しい機能の追加やバグ修正の際に予期せぬ問題を引き起こす原因となります。
  3. 一貫性の欠如: hlといったフォーマットフラグの挙動が一貫しておらず、予測が難しい状態だった可能性があります。

これらの問題を解決し、コンパイラの出力システムをより堅牢で、理解しやすく、拡張しやすいものにするために、この大規模なリファクタリングが実施されました。特に、デバッグ、シンボル、エクスポートといった異なる出力モードを明示的に制御できる「sticky(粘着性のある)」フラグの導入は、この目的を達成するための重要なステップです。

前提知識の解説

このコミットを理解するためには、以下の前提知識が役立ちます。

  1. Goコンパイラ (gc): Go言語の公式コンパイラであり、Goソースコードを機械語に変換する役割を担っています。コンパイルプロセスは複数のステージ(字句解析、構文解析、型チェック、エスケープ解析、コード生成など)に分かれており、各ステージで中間表現(AST: 抽象構文木など)が生成・操作されます。コンパイラ内部では、これらの処理のデバッグや、生成されたシンボル、型、ノードなどの情報を出力するために、独自のフォーマットシステムが使用されます。

  2. コンパイラの出力とデバッグ: コンパイラ開発において、内部の状態や中間表現を人間が読める形式で出力する機能は非常に重要です。これは、バグの特定、最適化の検証、新しい機能の実装時の挙動確認などに不可欠です。しかし、出力される情報が多岐にわたるため、必要な情報だけを効率的に表示するための柔軟なフォーマット機能が求められます。

  3. フォーマット指定子とフラグ: C言語のprintf関数に代表されるように、多くのプログラミング言語やシステムでは、文字列のフォーマットに「フォーマット指定子」(例: %d%s)と「フラグ」(例: %02d02)を使用します。

    • フォーマット指定子: 出力するデータの型(整数、文字列、浮動小数点数など)や、そのデータの表現形式(10進数、16進数など)を指定します。
    • フラグ: フォーマット指定子に追加の修飾を与え、出力の挙動を制御します。例えば、パディング、アラインメント、符号の表示、代替形式などがあります。
  4. 「Sticky(粘着性のある)フラグ」の概念: 通常のフォーマットフラグは、その指定子にのみ適用され、次の指定子には影響しません。しかし、「stickyフラグ」は、一度設定されると、その後の同じ種類のフォーマット指定子(例えば、%N%T%S)に対してもその効果が持続するという特性を持ちます。これは、特定の出力モード(デバッグ、エクスポートなど)を切り替える際に便利です。

  5. AST (Abstract Syntax Tree): 抽象構文木は、ソースコードの構文構造を木構造で表現したものです。コンパイラはソースコードを解析してASTを構築し、その後の処理(型チェック、コード生成など)でASTを操作します。このコミットで言及される%N(Node)は、ASTのノードを指します。

  6. シンボル (Symbol): プログラミング言語において、変数、関数、型などの名前(識別子)はシンボルとして扱われます。コンパイラはシンボルテーブルを管理し、各シンボルに関連する情報(型、スコープ、メモリ位置など)を格納します。%S(Sym)は、これらのシンボルを指します。

  7. 型 (Type): プログラミング言語におけるデータの種類(整数、文字列、構造体、関数など)を定義するものです。コンパイラは型システムを用いて、プログラムの型安全性や正しい挙動を保証します。%T(Type)は、Go言語の型を指します。

技術的詳細

このコミットの核心は、Goコンパイラ内部の出力システムを、従来のprint.cから新しいfmt.cへと移行し、より柔軟で制御可能なフォーマットメカニズムを導入した点にあります。

新しいフォーマットモード

fmt.cでは、%S(シンボル)、%T(型)、%N(ノード)のフォーマットにおいて、以下の「sticky(粘着性のある)」フラグが導入されました。これらのフラグは、一度設定されると、その後の同じ種類のフォーマット指定子に対してもその効果が持続します。

  • + (FmtSign): デバッグモード (FDbg)
  • - (FmtLeft): 型IDモード (FTypeId)
  • # (FmtSharp): エクスポートモード (FExp)

これらのモードは、fmtmodeという静的変数によって管理されます。デフォルトはFErr(エラーモード)です。

enum {
	FErr,	//     error mode (default)
	FDbg,	//     "%+N" debug mode
	FExp,	//     "%#N" export mode
	FTypeId,  //   "%-N" turning-types-into-symbols-mode: identical types give identical strings
};
static int fmtmode;
static int fmtpkgpfx;	// %uT stickyness

setfmode関数は、フォーマットフラグ(FmtSign, FmtSharp, FmtLeft)をチェックし、それに応じてfmtmodeを設定します。これにより、フォーマットのコンテキストが動的に切り替わります。

フォーマット指定子の詳細な挙動

fmt.cでは、各フォーマット指定子(%L, %E, %O, %J, %V, %S, %T, %N, %H, %Z)に対して、専用の変換関数(例: Lconv, Econv, Oconvなど)が実装されています。これらの関数は、fmtmodeや追加のフラグ(h, l, uなど)に基づいて、出力形式を調整します。

  • %L (Line numbers): 行番号を出力します。Hist構造体を用いて、インクルードファイルや#lineディレクティブによる行番号の変更履歴を追跡し、正確なソースコード上の位置を表示します。
  • %E (etype values): Goの内部的な型(etype)の値を文字列で出力します(例: INT, UINT, TFUNCなど)。
  • %O (Node Opcodes): ノードのオペコードを文字列で出力します。%#Oフラグが指定された場合や、FDbgモードでない場合は、Goの構文に対応するオペコード名(例: +, &&, ifなど)を出力します。
  • %J (Node details): ノードの内部的な詳細情報(ullman, addable, vargen, lineno, xoffset, class, escなど)を出力します。%hJフラグは、ウォーク(AST走査)まで不要な情報を抑制します。
  • %V (Val)*: 定数値をその型に応じてフォーマットして出力します(整数、浮動小数点数、複素数、文字列、真偽値、nilなど)。
  • %S (Sym)*: シンボルを出力します。
    • FErrモード(デフォルト): パッケージ名が異なる場合は完全なパス、同じパッケージ内ではシンボル名のみ。
    • FDbgモード: パッケージ名とシンボル名。
    • FTypeIdモード: 型ID生成用。%uSフラグでパッケージ名、それ以外でプレフィックスを使用。
    • FExpモード: エクスポート形式(@と完全なパッケージパス)。
    • %hS: パッケージ修飾なしの識別子。
    • %hhS: メソッド名から型修飾子を削除。
  • %T (Type)*: 型を出力します。
    • lフラグ: 型の名前ではなく定義を出力。
    • hフラグ: 関数型の場合、funcキーワードとレシーバを省略。
    • uフラグ: FTypeIdモードでのみ有効で、パッケージプレフィックスの代わりにパッケージ名を使用。
    • 再帰的な型定義の無限ループを防ぐためのtrecurカウンタが導入されています。
  • %N (Node)*: ノードを出力します。
    • FErr, FExpモード: nodefmt関数でノードの構文的な表現を出力。lフラグで「foo (type Bar)」形式。
    • FDbgモード: nodedump関数でノードの詳細なデバッグ情報を出力。hフラグで再帰を抑制。
  • %H (NodeList)*: ノードリストを出力します。%Nのフラグに加え、,フラグで項目をカンマで区切ります。デバッグモードでは改行で区切られます。
  • %Z (Strlit)*: エスケープされた文字列リテラルを出力します。

print.cの削除とfmt.cへの移行

このコミットの最も大きな変更は、従来のprint.cファイルが削除され、その機能がfmt.cに完全に置き換えられたことです。これにより、出力ロジックが一元化され、よりモジュール化された設計になりました。

yyerrorなどの変更

src/cmd/gc/const.csrc/cmd/gc/dcl.cなど、多くのファイルでyyerrorsmprintなどの関数呼び出しにおけるフォーマット指定子が変更されています。特に、%#Nのような古い形式が%Nに置き換えられているのは、新しいfmt.cの設計思想(モードはstickyフラグで制御し、個々の指定子ではシンプルにする)を反映しています。

export.cの変更

export.cは、Goのパッケージエクスポート情報を生成する役割を担っています。このコミットでは、dumpexporttype関数の引数がSym*からType*に変更され、型の出力ロジックがfmt.cの新しい%Tフォーマットに依存するように修正されました。これにより、エクスポートされる型の情報がより正確かつ一貫した形式で出力されるようになりました。また、dumpprereq関数が削除され、型の依存関係の出力もdumpexporttype内で処理されるようになりました。

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

このコミットのコアとなるコードの変更は、主に以下のファイルに集中しています。

  1. src/cmd/gc/fmt.c (新規追加):

    • enumで定義されたfmtmodeFErr, FDbg, FExp, FTypeId)と、setfmode関数によるモードの切り替えロジック。
    • 各フォーマット指定子(%L, %E, %O, %J, %V, %S, %T, %N, %H, %Z)に対応する変換関数(Lconv, Econv, Oconv, Jconv, Vconv, Sconv, Tconv, Nconv, Hconv, Zconv)。
    • fmtinstallgo関数によるこれらの変換関数の登録。
    • symfmt, typefmt, stmtfmt, exprfmt, nodefmt, nodedumpといった、シンボル、型、ステートメント、式、ノードの具体的なフォーマットロジック。
    • opprec配列による演算子の優先順位定義。
  2. src/cmd/gc/print.c (削除):

    • このファイル全体が削除されました。これは、従来の出力ロジックが完全に新しいfmt.cに置き換えられたことを意味します。
  3. src/cmd/gc/export.c (大幅な変更):

    • dumpexporttype関数のシグネチャがstatic void dumpexporttype(Sym* s)からstatic void dumpexporttype(Type* t)に変更されました。
    • dumpprereq関数が削除されました。
    • 定数、変数、型のエクスポートロジックが、新しいfmt.cBprint関数と%S, %T, %Vフォーマット指定子を使用するように書き換えられました。特に、%#S, %#T, %#Vといったエクスポートモードのフラグが活用されています。
    • dumpsym関数内の型エクスポート処理が、dumpexporttype(s->def->type)を呼び出すように変更されました。
  4. その他のファイル (src/cmd/gc/*.c群):

    • yyerrorsmprintなどの出力関数呼び出しにおいて、フォーマット指定子(特に%#N%Nに、%#S%Sに、%#hT%-hTに)が変更されています。これは、新しいfmt.cのstickyフラグの導入により、個々のフォーマット指定子でモードを明示的に指定する必要がなくなったためです。

コアとなるコードの解説

fmt.cの主要な関数と構造

  • fmtmodesetfmode: fmtmodeは現在のフォーマットモード(FErr, FDbg, FExp, FTypeId)を保持する静的変数です。setfmode関数は、Fmt構造体のフラグ(FmtSignFmtSharpFmtLeft)を読み取り、それに応じてfmtmodeを設定します。これにより、printfのようなフォーマット関数が呼び出された際に、そのコンテキストに応じた出力モードが自動的に適用されます。

  • Lconv(Fmt *fp): 行番号(%L)の変換を担当します。Hist構造体(Goコンパイラ内部のファイルと行番号の履歴を管理する)を利用して、ソースファイル名と正確な行番号を出力します。#lineディレクティブやインクルードファイルの情報を考慮し、デバッグ時には完全なパスも表示できます。

  • Oconv(Fmt *fp): ノードのオペコード(%O)を変換します。goopnames配列とopnames配列を使用して、数値のオペコードを人間が読める文字列(例: +, if, forなど)に変換します。%#OフラグやfmtmodeFDbgでない場合は、Goの構文に合わせた名前を出力します。

  • Jconv(Fmt *fp): ノードの詳細情報(%J)を変換します。ノードのullmanaddablevargenlinenoxoffsetclassescなどの内部的な属性を出力します。%hJフラグが指定された場合は、一部の情報を省略して簡潔な出力を提供します。

  • Vconv(Fmt *fp): 定数値(%V)を変換します。Val構造体に含まれる定数の型(整数、浮動小数点数、複素数、文字列、真偽値、nil)に応じて、適切な形式で値を出力します。

  • Zconv(Fmt *fp): エスケープされた文字列リテラル(%Z)を変換します。文字列内の特殊文字(タブ、改行、引用符、バックスラッシュなど)を適切にエスケープして出力します。

  • Sconv(Fmt *fp): シンボル(%S)を変換します。symfmt関数を呼び出し、現在のfmtmodeとフラグ(%hS%hhSなど)に基づいて、シンボル名とパッケージ修飾を調整します。これにより、デバッグ、エクスポート、型ID生成といった異なるコンテキストでシンボルが適切に表示されます。

  • Tconv(Fmt *fp): 型(%T)を変換します。typefmt関数を呼び出し、現在のfmtmodeとフラグ(%lT%hT%uTなど)に基づいて、型の定義、関数型の省略形式、パッケージ名の表示などを制御します。再帰的な型定義による無限ループを防ぐためのtrecurカウンタもここで管理されます。

  • Nconv(Fmt *fp): ノード(%N)を変換します。現在のfmtmodeに応じて、nodefmt(構文的な表現)またはnodedump(詳細なデバッグ情報)を呼び出します。FDbgモードでは、dumpdepthを使用してインデントを行い、ノードの階層構造を視覚的に表現します。

  • Hconv(Fmt *fp): ノードリスト(%H)を変換します。リスト内の各ノードに対して%Nフォーマットを適用し、指定された区切り文字(デフォルトはセミコロン、,フラグでカンマ、FDbgモードで改行)で連結して出力します。

  • fmtinstallgo(void): Goコンパイラで使用されるカスタムフォーマット指定子と、それに対応する変換関数をfmtライブラリに登録します。これにより、Bprintなどの内部的な出力関数でこれらのカスタムフォーマット指定子を使用できるようになります。

export.cの変更点

export.cにおけるdumpexporttype関数の変更は、型の出力方法が根本的に変わったことを示しています。

  • dumpexporttype(Type* t): この関数は、エクスポートされる型tの情報を出力します。
    • まず、t->printedフラグと基本的な型(bytetype, runetype)をチェックして、既に処理済みか、または特別な処理が不要な型かを判断します。
    • 型がシンボルを持つ場合(t->sym != S)、そのパッケージをdumppkgで出力します。
    • 再帰的に、型の内部構造(t->typet->down)をdumpexporttypeで処理します。
    • 型がメソッドを持つ場合、それらのメソッドもソートして出力します。
    • 最終的に、Bprint関数と%#S%#lT%#hTといったエクスポートモードのフォーマット指定子を使用して、型の名前と定義、メソッドシグネチャを整形して出力します。

この変更により、型のエクスポート処理がfmt.cの新しいフォーマットシステムと密接に連携し、より正確で一貫性のあるエクスポート形式が保証されるようになりました。

関連リンク

  • fixes #2361: このコミットメッセージにはfixes #2361と記載されていますが、Go言語の公式リポジトリ(golang/go)のIssueトラッカーでこの番号のIssueを直接見つけることはできませんでした。これは、Issue番号が内部的なトラッカーのものであるか、または非常に古いIssueであるため、現在のGitHubのIssue番号と一致しない可能性があります。
  • https://golang.org/cl/5316043: GoプロジェクトのGerrit Code Reviewへのリンクです。このリンクは、このコミットがGerrit上でレビューされたことを示しています。

参考にした情報源リンク

  • Go言語のコンパイラに関する一般的な知識
  • C言語のprintfフォーマット指定子に関する知識
  • Go言語の内部構造に関する一般的な知識 (AST, シンボル, 型など)
  • 提供されたコミットデータ (commit_data/10152.txt)
  • GitHubのコミットページ (https://github.com/golang/go/commit/50110c9f83ee0cfa2909ca78a67f14dcca2e83c1)
  • Go言語のGerrit Code Review (https://golang.org/cl/5316043)
  • Go言語のIssueトラッカー (https://github.com/golang/go/issues) - fixes #2361の確認のため
  • Go言語のソースコード (特にsrc/cmd/gc/ディレクトリ)```

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

このコミットは、Goコンパイラ(gc)における出力(printing)メカニズムの大規模なクリーンアップと再構築を目的としています。主な変更点は、従来の「魔法の謎のグローバル変数」に依存していた出力処理を廃止し、新しいfmt.cファイルに集約された、より構造化されたフォーマットシステムを導入したことです。

具体的には、以下のファイルに影響があります。

  • src/cmd/gc/Makefile: print.cの削除とfmt.cの追加に伴うビルド設定の変更。
  • src/cmd/gc/const.c, src/cmd/gc/dcl.c, src/cmd/gc/esc.c, src/cmd/gc/export.c, src/cmd/gc/gen.c, src/cmd/gc/go.h, src/cmd/gc/go.y, src/cmd/gc/lex.c, src/cmd/gc/obj.c, src/cmd/gc/range.c, src/cmd/gc/reflect.c, src/cmd/gc/subr.c, src/cmd/gc/typecheck.c, src/cmd/gc/unsafe.c, src/cmd/gc/walk.c: これらのファイルでは、古い出力関数への呼び出しが新しいフォーマット関数に置き換えられています。特に、%#Nのようなフォーマット指定子が%Nに変更されている箇所が多く見られます。
  • src/cmd/gc/fmt.c: 新規追加されたファイル。Goコンパイラの新しい出力フォーマットロジックがここに実装されています。
  • src/cmd/gc/print.c: 削除されたファイル。従来の出力処理を担っていたファイルが完全に置き換えられました。
  • src/pkg/fmt/fmt_test.go, src/pkg/reflect/all_test.go, test/ddd1.go, test/fixedbugs/bug340.go, test/named1.go: テストファイルにおける出力の変更や、新しいフォーマットシステムへの対応が行われています。

この変更により、コンパイラのデバッグ出力、シンボル出力、エクスポート出力の制御がより明確になり、将来的な変更や拡張が容易になりました。

コミット

  • コミットハッシュ: 50110c9f83ee0cfa2909ca78a67f14dcca2e83c1
  • Author: Luuk van Dijk lvd@golang.org
  • Date: Mon Oct 31 18:09:40 2011 +0100

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

https://github.com/golang/go/commit/50110c9f83ee0cfa2909ca78a67f14dcca2e83c1

元コミット内容

    gc: clean up printing.
    
    Got rid of all the magic mystery globals. Now
    for %N, %T, and %S, the flags +,- and # set a sticky
    debug, sym and export mode, only visible in the new fmt.c.
    Default is error mode. Handle h and l flags consistently with
    the least side effects, so we can now change
    things without worrying about unrelated things
    breaking.
    
    fixes #2361
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5316043

変更の背景

このコミットの背景には、Goコンパイラ(gc)の内部における出力処理の複雑さと、それに伴う保守性の問題がありました。従来の出力システムは、おそらく複数のグローバル変数や暗黙的な状態に依存しており、これが「魔法の謎のグローバル変数」と表現されています。このような設計は、以下のような問題を引き起こす可能性があります。

  1. 副作用の発生: ある出力設定の変更が、意図しない別の出力に影響を与える可能性がありました。コミットメッセージにある「unrelated things breaking(無関係なものが壊れる)」という表現がこれを裏付けています。
  2. コードの理解と保守の困難さ: グローバル変数に依存した状態管理は、コードの挙動を追跡することを困難にし、新しい機能の追加やバグ修正の際に予期せぬ問題を引き起こす原因となります。
  3. 一貫性の欠如: hlといったフォーマットフラグの挙動が一貫しておらず、予測が難しい状態だった可能性があります。

これらの問題を解決し、コンパイラの出力システムをより堅牢で、理解しやすく、拡張しやすいものにするために、この大規模なリファクタリングが実施されました。特に、デバッグ、シンボル、エクスポートといった異なる出力モードを明示的に制御できる「sticky(粘着性のある)」フラグの導入は、この目的を達成するための重要なステップです。

前提知識の解説

このコミットを理解するためには、以下の前提知識が役立ちます。

  1. Goコンパイラ (gc): Go言語の公式コンパイラであり、Goソースコードを機械語に変換する役割を担っています。コンパイルプロセスは複数のステージ(字句解析、構文解析、型チェック、エスケープ解析、コード生成など)に分かれており、各ステージで中間表現(AST: 抽象構文木など)が生成・操作されます。コンパイラ内部では、これらの処理のデバッグや、生成されたシンボル、型、ノードなどの情報を出力するために、独自のフォーマットシステムが使用されます。

  2. コンパイラの出力とデバッグ: コンパイラ開発において、内部の状態や中間表現を人間が読める形式で出力する機能は非常に重要です。これは、バグの特定、最適化の検証、新しい機能の実装時の挙動確認などに不可欠です。しかし、出力される情報が多岐にわたるため、必要な情報だけを効率的に表示するための柔軟なフォーマット機能が求められます。

  3. フォーマット指定子とフラグ: C言語のprintf関数に代表されるように、多くのプログラミング言語やシステムでは、文字列のフォーマットに「フォーマット指定子」(例: %d%s)と「フラグ」(例: %02d02)を使用します。

    • フォーマット指定子: 出力するデータの型(整数、文字列、浮動小数点数など)や、そのデータの表現形式(10進数、16進数など)を指定します。
    • フラグ: フォーマット指定子に追加の修飾を与え、出力の挙動を制御します。例えば、パディング、アラインメント、符号の表示、代替形式などがあります。
  4. 「Sticky(粘着性のある)フラグ」の概念: 通常のフォーマットフラグは、その指定子にのみ適用され、次の指定子には影響しません。しかし、「stickyフラグ」は、一度設定されると、その後の同じ種類のフォーマット指定子(例えば、%N%T%S)に対してもその効果が持続するという特性を持ちます。これは、特定の出力モード(デバッグ、エクスポートなど)を切り替える際に便利です。

  5. AST (Abstract Syntax Tree): 抽象構文木は、ソースコードの構文構造を木構造で表現したものです。コンパイラはソースコードを解析してASTを構築し、その後の処理(型チェック、コード生成など)でASTを操作します。このコミットで言及される%N(Node)は、ASTのノードを指します。

  6. シンボル (Symbol): プログラミング言語において、変数、関数、型などの名前(識別子)はシンボルとして扱われます。コンパイラはシンボルテーブルを管理し、各シンボルに関連する情報(型、スコープ、メモリ位置など)を格納します。%S(Sym)は、これらのシンボルを指します。

  7. 型 (Type): プログラミング言語におけるデータの種類(整数、文字列、構造体、関数など)を定義するものです。コンパイラは型システムを用いて、プログラムの型安全性や正しい挙動を保証します。%T(Type)は、Go言語の型を指します。

技術的詳細

このコミットの核心は、Goコンパイラ内部の出力システムを、従来のprint.cから新しいfmt.cへと移行し、より柔軟で制御可能なフォーマットメカニズムを導入した点にあります。

新しいフォーマットモード

fmt.cでは、%S(シンボル)、%T(型)、%N(ノード)のフォーマットにおいて、以下の「sticky(粘着性のある)」フラグが導入されました。これらのフラグは、一度設定されると、その後の同じ種類のフォーマット指定子に対してもその効果が持続します。

  • + (FmtSign): デバッグモード (FDbg)
  • - (FmtLeft): 型IDモード (FTypeId)
  • # (FmtSharp): エクスポートモード (FExp)

これらのモードは、fmtmodeという静的変数によって管理されます。デフォルトはFErr(エラーモード)です。

enum {
	FErr,	//     error mode (default)
	FDbg,	//     "%+N" debug mode
	FExp,	//     "%#N" export mode
	FTypeId,  //   "%-N" turning-types-into-symbols-mode: identical types give identical strings
};
static int fmtmode;
static int fmtpkgpfx;	// %uT stickyness

setfmode関数は、フォーマットフラグ(FmtSign, FmtSharp, FmtLeft)をチェックし、それに応じてfmtmodeを設定します。これにより、フォーマットのコンテキストが動的に切り替わります。

フォーマット指定子の詳細な挙動

fmt.cでは、各フォーマット指定子(%L, %E, %O, %J, %V, %S, %T, %N, %H, %Z)に対して、専用の変換関数(例: Lconv, Econv, Oconvなど)が実装されています。これらの関数は、fmtmodeや追加のフラグ(h, l, uなど)に基づいて、出力形式を調整します。

  • %L (Line numbers): 行番号を出力します。Hist構造体を用いて、インクルードファイルや#lineディレクティブによる行番号の変更履歴を追跡し、正確なソースコード上の位置を表示します。
  • %E (etype values): Goの内部的な型(etype)の値を文字列で出力します(例: INT, UINT, TFUNCなど)。
  • %O (Node Opcodes): ノードのオペコードを文字列で出力します。%#Oフラグが指定された場合や、FDbgモードでない場合は、Goの構文に対応するオペコード名(例: +, &&, ifなど)を出力します。
  • %J (Node details): ノードの内部的な詳細情報(ullman, addable, vargen, lineno, xoffset, class, escなど)を出力します。%hJフラグは、ウォーク(AST走査)まで不要な情報を抑制します。
  • %V (Val)*: 定数値をその型に応じてフォーマットして出力します(整数、浮動小数点数、複素数、文字列、真偽値、nilなど)。
  • %S (Sym)*: シンボルを出力します。
    • FErrモード(デフォルト): パッケージ名が異なる場合は完全なパス、同じパッケージ内ではシンボル名のみ。
    • FDbgモード: パッケージ名とシンボル名。
    • FTypeIdモード: 型ID生成用。%uSフラグでパッケージ名、それ以外でプレフィックスを使用。
    • FExpモード: エクスポート形式(@と完全なパッケージパス)。
    • %hS: パッケージ修飾なしの識別子。
    • %hhS: メソッド名から型修飾子を削除。
  • %T (Type)*: 型を出力します。
    • lフラグ: 型の名前ではなく定義を出力。
    • hフラグ: 関数型の場合、funcキーワードとレシーバを省略。
    • uフラグ: FTypeIdモードでのみ有効で、パッケージプレフィックスの代わりにパッケージ名を使用。
    • 再帰的な型定義の無限ループを防ぐためのtrecurカウンタが導入されています。
  • %N (Node)*: ノードを出力します。
    • FErr, FExpモード: nodefmt関数でノードの構文的な表現を出力。lフラグで「foo (type Bar)」形式。
    • FDbgモード: nodedump関数でノードの詳細なデバッグ情報を出力。hフラグで再帰を抑制。
  • %H (NodeList)*: ノードリストを出力します。%Nのフラグに加え、,フラグで項目をカンマで区切ります。デバッグモードでは改行で区切られます。
  • %Z (Strlit)*: エスケープされた文字列リテラルを出力します。

print.cの削除とfmt.cへの移行

このコミットの最も大きな変更は、従来のprint.cファイルが削除され、その機能がfmt.cに完全に置き換えられたことです。これにより、出力ロジックが一元化され、よりモジュール化された設計になりました。

yyerrorなどの変更

src/cmd/gc/const.csrc/cmd/gc/dcl.cなど、多くのファイルでyyerrorsmprintなどの関数呼び出しにおけるフォーマット指定子が変更されています。特に、%#Nのような古い形式が%Nに置き換えられているのは、新しいfmt.cの設計思想(モードはstickyフラグで制御し、個々の指定子ではシンプルにする)を反映しています。

export.cの変更

export.cは、Goのパッケージエクスポート情報を生成する役割を担っています。このコミットでは、dumpexporttype関数の引数がSym*からType*に変更され、型の出力ロジックがfmt.cの新しい%Tフォーマットに依存するように修正されました。これにより、エクスポートされる型の情報がより正確かつ一貫した形式で出力されるようになりました。また、dumpprereq関数が削除され、型の依存関係の出力もdumpexporttype内で処理されるようになりました。

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

このコミットのコアとなるコードの変更は、主に以下のファイルに集中しています。

  1. src/cmd/gc/fmt.c (新規追加):

    • enumで定義されたfmtmodeFErr, FDbg, FExp, FTypeId)と、setfmode関数によるモードの切り替えロジック。
    • 各フォーマット指定子(%L, %E, %O, %J, %V, %S, %T, %N, %H, %Z)に対応する変換関数(Lconv, Econv, Oconv, Jconv, Vconv, Sconv, Tconv, Nconv, Hconv, Zconv)。
    • fmtinstallgo関数によるこれらの変換関数の登録。
    • symfmt, typefmt, stmtfmt, exprfmt, nodefmt, nodedumpといった、シンボル、型、ステートメント、式、ノードの具体的なフォーマットロジック。
    • opprec配列による演算子の優先順位定義。
  2. src/cmd/gc/print.c (削除):

    • このファイル全体が削除されました。これは、従来の出力ロジックが完全に新しいfmt.cに置き換えられたことを意味します。
  3. src/cmd/gc/export.c (大幅な変更):

    • dumpexporttype関数のシグネチャがstatic void dumpexporttype(Sym* s)からstatic void dumpexporttype(Type* t)に変更されました。
    • dumpprereq関数が削除されました。
    • 定数、変数、型のエクスポートロジックが、新しいfmt.cBprint関数と%S, %T, %Vフォーマット指定子を使用するように書き換えられました。特に、%#S, %#T, %#Vといったエクスポートモードのフラグが活用されています。
    • dumpsym関数内の型エクスポート処理が、dumpexporttype(s->def->type)を呼び出すように変更されました。
  4. その他のファイル (src/cmd/gc/*.c群):

    • yyerrorsmprintなどの出力関数呼び出しにおいて、フォーマット指定子(特に%#N%Nに、%#S%Sに、%#hT%-hTに)が変更されています。これは、新しいfmt.cのstickyフラグの導入により、個々のフォーマット指定子でモードを明示的に指定する必要がなくなったためです。

コアとなるコードの解説

fmt.cの主要な関数と構造

  • fmtmodesetfmode: fmtmodeは現在のフォーマットモード(FErr, FDbg, FExp, FTypeId)を保持する静的変数です。setfmode関数は、Fmt構造体のフラグ(FmtSignFmtSharpFmtLeft)を読み取り、それに応じてfmtmodeを設定します。これにより、printfのようなフォーマット関数が呼び出された際に、そのコンテキストに応じた出力モードが自動的に適用されます。

  • Lconv(Fmt *fp): 行番号(%L)の変換を担当します。Hist構造体(Goコンパイラ内部のファイルと行番号の履歴を管理する)を利用して、ソースファイル名と正確な行番号を出力します。#lineディレクティブやインクルードファイルの情報を考慮し、デバッグ時には完全なパスも表示できます。

  • Oconv(Fmt *fp): ノードのオペコード(%O)を変換します。goopnames配列とopnames配列を使用して、数値のオペコードを人間が読める文字列(例: +, if, forなど)に変換します。%#OフラグやfmtmodeFDbgでない場合は、Goの構文に合わせた名前を出力します。

  • Jconv(Fmt *fp): ノードの詳細情報(%J)を変換します。ノードのullmanaddablevargenlinenoxoffsetclassescなどの内部的な属性を出力します。%hJフラグが指定された場合は、一部の情報を省略して簡潔な出力を提供します。

  • Vconv(Fmt *fp): 定数値(%V)を変換します。Val構造体に含まれる定数の型(整数、浮動小数点数、複素数、文字列、真偽値、nil)に応じて、適切な形式で値を出力します。

  • Zconv(Fmt *fp): エスケープされた文字列リテラル(%Z)を変換します。文字列内の特殊文字(タブ、改行、引用符、バックスラッシュなど)を適切にエスケープして出力します。

  • Sconv(Fmt *fp): シンボル(%S)を変換します。symfmt関数を呼び出し、現在のfmtmodeとフラグ(%hS%hhSなど)に基づいて、シンボル名とパッケージ修飾を調整します。これにより、デバッグ、エクスポート、型ID生成といった異なるコンテキストでシンボルが適切に表示されます。

  • Tconv(Fmt *fp): 型(%T)を変換します。typefmt関数を呼び出し、現在のfmtmodeとフラグ(%lT%hT%uTなど)に基づいて、型の定義、関数型の省略形式、パッケージ名の表示などを制御します。再帰的な型定義による無限ループを防ぐためのtrecurカウンタもここで管理されます。

  • Nconv(Fmt *fp): ノード(%N)を変換します。現在のfmtmodeに応じて、nodefmt(構文的な表現)またはnodedump(詳細なデバッグ情報)を呼び出します。FDbgモードでは、dumpdepthを使用してインデントを行い、ノードの階層構造を視覚的に表現します。

  • Hconv(Fmt *fp): ノードリスト(%H)を変換します。リスト内の各ノードに対して%Nフォーマットを適用し、指定された区切り文字(デフォルトはセミコロン、,フラグでカンマ、FDbgモードで改行)で連結して出力します。

  • fmtinstallgo(void): Goコンパイラで使用されるカスタムフォーマット指定子と、それに対応する変換関数をfmtライブラリに登録します。これにより、Bprintなどの内部的な出力関数でこれらのカスタムフォーマット指定子を使用できるようになります。

export.cの変更点

export.cにおけるdumpexporttype関数の変更は、型の出力方法が根本的に変わったことを示しています。

  • dumpexporttype(Type* t): この関数は、エクスポートされる型tの情報を出力します。
    • まず、t->printedフラグと基本的な型(bytetype, runetype)をチェックして、既に処理済みか、または特別な処理が不要な型かを判断します。
    • 型がシンボルを持つ場合(t->sym != S)、そのパッケージをdumppkgで出力します。
    • 再帰的に、型の内部構造(t->typet->down)をdumpexporttypeで処理します。
    • 型がメソッドを持つ場合、それらのメソッドもソートして出力します。
    • 最終的に、Bprint関数と%#S%#lT%#hTといったエクスポートモードのフォーマット指定子を使用して、型の名前と定義、メソッドシグネチャを整形して出力します。

この変更により、型のエクスポート処理がfmt.cの新しいフォーマットシステムと密接に連携し、より正確で一貫性のあるエクスポート形式が保証されるようになりました。

関連リンク

  • fixes #2361: このコミットメッセージにはfixes #2361と記載されていますが、Go言語の公式リポジトリ(golang/go)のIssueトラッカーでこの番号のIssueを直接見つけることはできませんでした。これは、Issue番号が内部的なトラッカーのものであるか、または非常に古いIssueであるため、現在のGitHubのIssue番号と一致しない可能性があります。
  • https://golang.org/cl/5316043: GoプロジェクトのGerrit Code Reviewへのリンクです。このリンクは、このコミットがGerrit上でレビューされたことを示しています。

参考にした情報源リンク

  • Go言語のコンパイラに関する一般的な知識
  • C言語のprintfフォーマット指定子に関する知識
  • Go言語の内部構造に関する一般的な知識 (AST, シンボル, 型など)
  • 提供されたコミットデータ (commit_data/10152.txt)
  • GitHubのコミットページ (https://github.com/golang/go/commit/50110c9f83ee0cfa2909ca78a67f14dcca2e83c1)
  • Go言語のGerrit Code Review (https://golang.org/cl/5316043)
  • Go言語のIssueトラッカー (https://github.com/golang/go/issues) - fixes #2361の確認のため
  • Go言語のソースコード (特にsrc/cmd/gc/ディレクトリ)