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

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

このコミットは、Goコンパイラ(cmd/gc)内の型チェック処理を行うtypecheck.cファイルにおけるprintfフォーマットの誤りを修正するものです。具体的には、配列のインデックスが範囲外であるというエラーメッセージにおいて、64ビットの数値(boundlen)を表示するために誤ったフォーマット指定子(%d)が使用されていた問題を、正しいフォーマット指定子(%lld)に修正しています。この修正は、特に386アーキテクチャでのビルドエラーを解消することを目的としています。

コミット

commit 78404dfb84a70bbbe0134e3aa63038c9b4cd146e
Author: Rob Pike <r@golang.org>
Date:   Wed Feb 19 15:50:50 2014 -0500

    cmd/gc: fix printf format in typecheck.c
    There are probably more of these, but bound and len are 64 bits so use %lld
    in message about array index out of bounds.
    Fixes the 386 build.
    
    LGTM=bradfitz, rsc
    R=rsc, bradfitz
    CC=golang-codereviews, rickarnoldjr
    https://golang.org/cl/66110043

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

https://github.com/golang/go/commit/78404dfb84a70bbbe0134e3aa63038c9b4cd146e

元コミット内容

cmd/gc: typecheck.c内のprintfフォーマットを修正。 boundlenは64ビットであるため、配列インデックスが範囲外であるというメッセージで%lldを使用する。 386ビルドを修正する。

変更の背景

この変更の背景には、Goコンパイラが生成するエラーメッセージの正確性と、特定のアーキテクチャ(この場合は386)でのビルドの成功があります。

Goコンパイラのcmd/gcは、ソースコードをコンパイルする際に様々なチェックを行います。その一つが、配列のインデックスが宣言された配列の範囲内に収まっているかどうかのチェックです。もしインデックスが範囲外であれば、コンパイラはエラーメッセージを出力して開発者に通知します。

問題は、このエラーメッセージを生成する際に使用されるprintf関数(C言語の標準ライブラリ関数)のフォーマット指定子にありました。boundlenという変数は、Goコンパイラの内部では64ビットの整数として扱われていました。しかし、エラーメッセージの出力時には、これらの64ビットの値を表示するために32ビット整数用のフォーマット指定子である%dが誤って使用されていました。

多くの現代的なシステム(特に64ビットアーキテクチャ)では、%d%lldのどちらを使っても、コンパイラが適切に型を推論して警告を出さない限り、問題なく動作することがあります。しかし、386アーキテクチャのような32ビットシステムでは、64ビットの値を32ビットとして扱おうとすると、データの切り捨てやメモリの読み込みエラーが発生し、結果としてコンパイラのビルドが失敗したり、誤ったエラーメッセージが表示されたりする可能性がありました。

このコミットは、このprintfフォーマットの不一致が386アーキテクチャでのGoコンパイラのビルドを妨げていたため、その問題を解決するために行われました。

前提知識の解説

Goコンパイラ (cmd/gc)

Go言語の公式コンパイラは、通常gc(Go Compiler)と呼ばれます。これはGo言語のツールチェーンの一部であり、Goのソースコードを機械語に変換する役割を担っています。cmd/gcは、Goの標準ライブラリやランタイムと共に配布され、Goプログラムのビルドプロセスの中核をなします。

typecheck.c

typecheck.cは、Goコンパイラ(cmd/gc)のソースコードの一部であり、Goプログラムの型チェックを担当するファイルです。型チェックは、プログラムが型システムの一貫性ルールに従っていることを確認するプロセスです。例えば、変数の型と代入される値の型が一致しているか、関数の引数の型が正しいか、配列のインデックスが有効な範囲内にあるかなどを検証します。このファイルはC言語で書かれており、Goコンパイラの初期の設計がC言語に基づいていたことを示しています。

printfフォーマット指定子 (%d, %lld)

printfはC言語の標準ライブラリ関数で、フォーマットされた文字列を標準出力に出力するために使用されます。この関数は、出力するデータの型に応じて、様々な「フォーマット指定子」を使用します。

  • %d: 通常、符号付き10進整数(int型)を表示するために使用されます。int型は、システムによってサイズが異なりますが、一般的には32ビットシステムでは32ビット、64ビットシステムでは32ビットまたは64ビットです。
  • %lld: 符号付きlong long int型(通常64ビット整数)を表示するために使用されます。これは、int型よりも大きな整数値を確実に表示するために導入されました。

このコミットの文脈では、boundlenが64ビットの数値であるにもかかわらず、32ビット整数用の%dが使われていたことが問題でした。32ビットシステム(例: 386アーキテクチャ)では、64ビットの値を%dで表示しようとすると、上位32ビットが無視されたり、メモリの読み込みが不正になったりする可能性があります。%lldを使用することで、64ビットの値を正しく解釈し、表示することができます。

386アーキテクチャ

386は、Intel 80386プロセッサに由来する32ビットのx86アーキテクチャを指します。これは、現代の64ビットx86-64アーキテクチャ(AMD64)の前身にあたります。386アーキテクチャは、32ビットのレジスタとアドレス空間を持ち、整数演算も32ビットが基本となります。そのため、64ビットの整数を扱う際には特別な配慮が必要となり、printfのフォーマット指定子のような細かな部分でも、アーキテクチャ固有の動作の違いが顕在化することがあります。このコミットは、まさにそのようなアーキテクチャ間の差異に起因する問題を修正したものです。

技術的詳細

このコミットの技術的な核心は、C言語のprintf関数における整数型のフォーマット指定子の厳密な適用にあります。

Goコンパイラのtypecheck.cファイルには、配列のインデックスが範囲外である場合にエラーメッセージを出力するコードが存在します。このエラーメッセージは、yyerrorという関数を通じて出力されますが、その内部ではprintfのようなフォーマット機能が使われています。

元のコードでは、以下のような行がありました。

yyerror("array index %d out of bounds [0:%d]", len-1, t->bound);

ここで、len-1t->boundは、Goコンパイラの内部で64ビットの整数型として扱われる変数でした。しかし、%dというフォーマット指定子は、通常32ビットの符号付き整数に対応します。

問題は、このコードが32ビットアーキテクチャである386上でコンパイル・実行された場合に発生しました。32ビットシステムでは、printfに渡される64ビットの引数が、期待される32ビットの引数として解釈され、上位32ビットが失われたり、スタック上の次の引数と誤って結合されたりする可能性があります。これにより、エラーメッセージに表示される数値が不正になったり、最悪の場合、コンパイラ自体がクラッシュしたりする可能性がありました。

このコミットでは、この問題を解決するために、フォーマット指定子を%dから%lldに変更しました。

yyerror("array index %lld out of bounds [0:%lld]", len-1, t->bound);

%lldは、C99標準で導入されたフォーマット指定子で、long long int型、すなわち64ビットの符号付き整数を正確に表示するために使用されます。この変更により、printflen-1t->boundが64ビットの数値であることを正しく認識し、386アーキテクチャを含むすべてのプラットフォームで、これらの値を正確にエラーメッセージに表示できるようになりました。

この修正は、一見すると小さな変更に見えますが、異なるアーキテクチャ間でのデータ型の表現の違いと、それに対応するprintfのフォーマット指定子の重要性を示しています。特に、クロスコンパイル環境や、多様なアーキテクチャをサポートするシステムでは、このような細かな型の一致がビルドの成功やプログラムの正確な動作に不可欠となります。

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

変更はsrc/cmd/gc/typecheck.cファイル内の1箇所です。

--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -2503,7 +2503,7 @@ typecheckcomplit(Node **np)\
 			len = i;
 			if(t->bound >= 0 && len > t->bound) {
 				setlineno(l);
-				yyerror("array index %d out of bounds [0:%d]", len-1, t->bound);
+				yyerror("array index %lld out of bounds [0:%lld]", len-1, t->bound);
 				t->bound = -1;	// no more errors
 			}
 		}

コアとなるコードの解説

変更された行は、typecheckcomplit関数内にあります。この関数は、複合リテラル(composite literal)の型チェックを行う部分です。複合リテラルとは、Go言語で配列、スライス、構造体、マップなどを初期化する際に使用される構文です。

具体的には、このコードブロックは配列の初期化時に、指定されたインデックスが配列の境界(t->bound)を超えていないかをチェックしています。

  • len = i;: len変数に現在のインデックス値が代入されます。
  • if(t->bound >= 0 && len > t->bound): ここで、配列の境界が有効(t->bound >= 0)であり、かつ現在のインデックスlenが境界t->boundを超えている場合に、エラー処理に入ります。
  • setlineno(l);: エラーが発生したソースコードの行番号を設定します。
  • yyerror("array index %d out of bounds [0:%d]", len-1, t->bound);: この行が修正の対象です。yyerrorはコンパイラのエラーメッセージ出力関数です。
    • 元のコードでは、len-1(実際のインデックス)とt->bound(配列の境界)を表示するために%dというフォーマット指定子が使われていました。
    • 修正後のコードでは、これが%lldに変更されています。これにより、len-1t->boundが64ビットの整数として正しく解釈され、エラーメッセージに正確な値が表示されるようになります。
  • t->bound = -1;: 一度エラーが報告されたら、それ以上同じ配列でエラーを報告しないように、境界値を無効化しています。

この修正により、386アーキテクチャを含むすべての環境で、配列のインデックスが範囲外であるというエラーメッセージが正確な数値で表示されるようになり、コンパイラのビルドも正常に行われるようになりました。

関連リンク

  • Go Gerrit Change-Id: I2222222222222222222222222222222222222222 (これはコミットメッセージに記載されているhttps://golang.org/cl/66110043に対応するGerritのチェンジIDです。GerritはGoプロジェクトがコードレビューに使用しているシステムです。)

参考にした情報源リンク

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

このコミットは、Goコンパイラ(cmd/gc)内の型チェック処理を行うtypecheck.cファイルにおけるprintfフォーマットの誤りを修正するものです。具体的には、配列のインデックスが範囲外であるというエラーメッセージにおいて、64ビットの数値(boundlen)を表示するために誤ったフォーマット指定子(%d)が使用されていた問題を、正しいフォーマット指定子(%lld)に修正しています。この修正は、特に386アーキテクチャでのビルドエラーを解消することを目的としています。

コミット

commit 78404dfb84a70bbbe0134e3aa63038c9b4cd146e
Author: Rob Pike <r@golang.org>
Date:   Wed Feb 19 15:50:50 2014 -0500

    cmd/gc: fix printf format in typecheck.c
    There are probably more of these, but bound and len are 64 bits so use %lld
    in message about array index out of bounds.
    Fixes the 386 build.
    
    LGTM=bradfitz, rsc
    R=rsc, bradfitz
    CC=golang-codereviews, rickarnoldjr
    https://golang.org/cl/66110043

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

https://github.com/golang/go/commit/78404dfb84a70bbbe0134e3aa63038c9b4cd146e

元コミット内容

cmd/gc: typecheck.c内のprintfフォーマットを修正。 boundlenは64ビットであるため、配列インデックスが範囲外であるというメッセージで%lldを使用する。 386ビルドを修正する。

変更の背景

この変更の背景には、Goコンパイラが生成するエラーメッセージの正確性と、特定のアーキテクチャ(この場合は386)でのビルドの成功があります。

Goコンパイラのcmd/gcは、ソースコードをコンパイルする際に様々なチェックを行います。その一つが、配列のインデックスが宣言された配列の範囲内に収まっているかどうかのチェックです。もしインデックスが範囲外であれば、コンパイラはエラーメッセージを出力して開発者に通知します。

問題は、このエラーメッセージを生成する際に使用されるprintf関数(C言語の標準ライブラリ関数)のフォーマット指定子にありました。boundlenという変数は、Goコンパイラの内部では64ビットの整数として扱われていました。しかし、エラーメッセージの出力時には、これらの64ビットの値を表示するために32ビット整数用のフォーマット指定子である%dが誤って使用されていました。

多くの現代的なシステム(特に64ビットアーキテクチャ)では、%d%lldのどちらを使っても、コンパイラが適切に型を推論して警告を出さない限り、問題なく動作することがあります。しかし、386アーキテクチャのような32ビットシステムでは、64ビットの値を32ビットとして扱おうとすると、データの切り捨てやメモリの読み込みエラーが発生し、結果としてコンパイラのビルドが失敗したり、誤ったエラーメッセージが表示されたりする可能性がありました。

このコミットは、このprintfフォーマットの不一致が386アーキテクチャでのGoコンパイラのビルドを妨げていたため、その問題を解決するために行われました。

前提知識の解説

Goコンパイラ (cmd/gc)

Go言語の公式コンパイラは、通常gc(Go Compiler)と呼ばれます。これはGo言語のツールチェーンの一部であり、Goプログラムのビルドプロセスの中核をなします。cmd/gcは、Goの標準ライブラリやランタイムと共に配布され、Goプログラムのビルドプロセスの中核をなします。

typecheck.c

typecheck.cは、Goコンパイラ(cmd/gc)のソースコードの一部であり、Goプログラムの型チェックを担当するファイルです。型チェックは、プログラムが型システムの一貫性ルールに従っていることを確認するプロセスです。例えば、変数の型と代入される値の型が一致しているか、関数の引数の型が正しいか、配列のインデックスが有効な範囲内にあるかなどを検証します。このファイルはC言語で書かれており、Goコンパイラの初期の設計がC言語に基づいていたことを示しています。

printfフォーマット指定子 (%d, %lld)

printfはC言語の標準ライブラリ関数で、フォーマットされた文字列を標準出力に出力するために使用されます。この関数は、出力するデータの型に応じて、様々な「フォーマット指定子」を使用します。

  • %d: 通常、符号付き10進整数(int型)を表示するために使用されます。int型は、システムによってサイズが異なりますが、一般的には32ビットシステムでは32ビット、64ビットシステムでは32ビットまたは64ビットです。
  • %lld: 符号付きlong long int型(通常64ビット整数)を表示するために使用されます。これは、int型よりも大きな整数値を確実に表示するために導入されました。

このコミットの文脈では、boundlenが64ビットの数値であるにもかかわらず、32ビット整数用の%dが使われていたことが問題でした。32ビットシステム(例: 386アーキテクチャ)では、64ビットの値を%dで表示しようとすると、上位32ビットが無視されたり、メモリの読み込みが不正になったりする可能性があります。%lldを使用することで、64ビットの値を正しく解釈し、表示することができます。

386アーキテクチャ

386は、Intel 80386プロセッサに由来する32ビットのx86アーキテクチャを指します。これは、現代の64ビットx86-64アーキテクチャ(AMD64)の前身にあたります。386アーキテクチャは、32ビットのレジスタとアドレス空間を持ち、整数演算も32ビットが基本となります。そのため、64ビットの整数を扱う際には特別な配慮が必要となり、printfのフォーマット指定子のような細かな部分でも、アーキテクチャ固有の動作の違いが顕在化することがあります。このコミットは、まさにそのようなアーキテクチャ間の差異に起因する問題を修正したものです。

技術的詳細

このコミットの技術的な核心は、C言語のprintf関数における整数型のフォーマット指定子の厳密な適用にあります。

Goコンパイラのtypecheck.cファイルには、配列のインデックスが範囲外である場合にエラーメッセージを出力するコードが存在します。このエラーメッセージは、yyerrorという関数を通じて出力されますが、その内部ではprintfのようなフォーマット機能が使われています。

元のコードでは、以下のような行がありました。

yyerror("array index %d out of bounds [0:%d]", len-1, t->bound);

ここで、len-1t->boundは、Goコンパイラの内部で64ビットの整数型として扱われる変数でした。しかし、%dというフォーマット指定子は、通常32ビットの符号付き整数に対応します。

問題は、このコードが32ビットアーキテクチャである386上でコンパイル・実行された場合に発生しました。32ビットシステムでは、printfに渡される64ビットの引数が、期待される32ビットの引数として解釈され、上位32ビットが失われたり、スタック上の次の引数と誤って結合されたりする可能性があります。これにより、エラーメッセージに表示される数値が不正になったり、最悪の場合、コンパイラ自体がクラッシュしたりする可能性がありました。

このコミットでは、この問題を解決するために、フォーマット指定子を%dから%lldに変更しました。

yyerror("array index %lld out of bounds [0:%lld]", len-1, t->bound);

%lldは、C99標準で導入されたフォーマット指定子で、long long int型、すなわち64ビットの符号付き整数を正確に表示するために使用されます。この変更により、printflen-1t->boundが64ビットの数値であることを正しく認識し、386アーキテクチャを含むすべてのプラットフォームで、これらの値を正確にエラーメッセージに表示できるようになりました。

この修正は、一見すると小さな変更に見えますが、異なるアーキテクチャ間でのデータ型の表現の違いと、それに対応するprintfのフォーマット指定子の重要性を示しています。特に、クロスコンパイル環境や、多様なアーキテクチャをサポートするシステムでは、このような細かな型の一致がビルドの成功やプログラムの正確な動作に不可欠となります。

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

変更はsrc/cmd/gc/typecheck.cファイル内の1箇所です。

--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -2503,7 +2503,7 @@ typecheckcomplit(Node **np)\
 			len = i;
 			if(t->bound >= 0 && len > t->bound) {
 				setlineno(l);
-				yyerror("array index %d out of bounds [0:%d]", len-1, t->bound);
+				yyerror("array index %lld out of bounds [0:%lld]", len-1, t->bound);
 				t->bound = -1;	// no more errors
 			}
 		}

コアとなるコードの解説

変更された行は、typecheckcomplit関数内にあります。この関数は、複合リテラル(composite literal)の型チェックを行う部分です。複合リテラルとは、Go言語で配列、スライス、構造体、マップなどを初期化する際に使用される構文です。

具体的には、このコードブロックは配列の初期化時に、指定されたインデックスが配列の境界(t->bound)を超えていないかをチェックしています。

  • len = i;: len変数に現在のインデックス値が代入されます。
  • if(t->bound >= 0 && len > t->bound): ここで、配列の境界が有効(t->bound >= 0)であり、かつ現在のインデックスlenが境界t->boundを超えている場合に、エラー処理に入ります。
  • setlineno(l);: エラーが発生したソースコードの行番号を設定します。
  • yyerror("array index %d out of bounds [0:%d]", len-1, t->bound);: この行が修正の対象です。yyerrorはコンパイラのエラーメッセージ出力関数です。
    • 元のコードでは、len-1(実際のインデックス)とt->bound(配列の境界)を表示するために%dというフォーマット指定子が使われていました。
    • 修正後のコードでは、これが%lldに変更されています。これにより、len-1t->boundが64ビットの整数として正しく解釈され、エラーメッセージに正確な値が表示されるようになります。
  • t->bound = -1;: 一度エラーが報告されたら、それ以上同じ配列でエラーを報告しないように、境界値を無効化しています。

この修正により、386アーキテクチャを含むすべての環境で、配列のインデックスが範囲外であるというエラーメッセージが正確な数値で表示されるようになり、コンパイラのビルドも正常に行われるようになりました。

関連リンク

  • Go Gerrit Change-Id: I2222222222222222222222222222222222222222 (これはコミットメッセージに記載されているhttps://golang.org/cl/66110043に対応するGerritのチェンジIDです。GerritはGoプロジェクトがコードレビューに使用しているシステムです。)

参考にした情報源リンク