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

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

このコミットは、Goコンパイラ(cmd/gc)の一部であるmkbuiltin1.cファイルに対する変更です。具体的には、clangコンパイラが生成する警告を抑制するための修正が行われています。

コミット

commit b8be100d35109e8cd0abac91edec206df37b07c1
Author: Russ Cox <rsc@golang.org>
Date:   Tue Oct 29 11:50:18 2013 -0400

    cmd/gc: silence clang warning
    
    This code is only built when you run 'make' in cmd/gc,
    not in all.bash.
    
    R=golang-dev, jsing, iant
    CC=golang-dev
    https://golang.org/cl/19160043

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

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

元コミット内容

cmd/gc: silence clang warning

このコミットの目的は、cmd/gcディレクトリ内のコードがclangコンパイラによってビルドされる際に発生する警告を解消することです。コミットメッセージには、このコードがall.bash(Goのビルドスクリプト)ではビルドされず、cmd/gc内でmakeを実行した場合にのみビルドされることが明記されています。

変更の背景

ソフトウェア開発において、コンパイラの警告は潜在的なバグや非効率なコード、あるいは将来の互換性の問題を示唆する場合があります。しかし、時にはコンパイラの警告が、コードの意図とは異なる解釈や、特定のコンパイラの厳密なチェックによって発生することもあります。このような場合、警告が誤検知であるか、あるいはコードの動作に影響を与えない「良性の」警告であると判断されることがあります。

このコミットの背景には、clangコンパイラが特定のC言語のイディオムに対して警告を発するという状況があります。具体的には、while(q = strstr(p, "PACKAGE"))という形式の代入式を含む条件文が、clangによって警告の対象とされたと考えられます。これは、C言語では一般的なパターンであり、strstr関数の戻り値(見つかった部分文字列へのポインタ、または見つからなかった場合はNULL)をqに代入し、そのqNULLでない限りループを継続するという意図を持っています。しかし、一部のコンパイラ(特にclangのようなモダンで厳格なコンパイラ)は、代入が条件式の中で行われることを潜在的なプログラミングミスと見なし、警告を発することがあります。

開発者は、このような警告を放置すると、実際の重要な警告が見過ごされるリスクがあるため、可能な限り警告を解消しようとします。この場合、コードのロジックを変更せずに警告を抑制するために、明示的なNULLチェックを追加する修正が選択されました。

前提知識の解説

  • Goコンパイラ (cmd/gc): Go言語の公式コンパイラは、Goソースコードを機械語に変換する役割を担っています。cmd/gcは、そのコンパイラの主要な部分を構成するディレクトリの一つです。Goコンパイラ自体はGo言語で書かれていますが、一部の低レベルな処理やブートストラップの目的でC言語のコードも含まれています。
  • clang: clangは、C、C++、Objective-C、Objective-C++などのプログラミング言語をコンパイルするためのフロントエンドです。LLVMプロジェクトの一部として開発されており、GCC(GNU Compiler Collection)の代替として広く利用されています。clangは、その優れたエラーメッセージと警告機能で知られています。
  • コンパイラの警告: コンパイラは、プログラムの構文エラーだけでなく、潜在的な問題や非推奨のコードパターンに対しても警告を発します。警告はプログラムの実行には影響しませんが、開発者に対してコードの改善点や潜在的なバグの可能性を通知します。
  • strstr関数: C標準ライブラリの<string.h>に含まれる関数で、ある文字列(haystack)の中に別の文字列(needle)が最初に現れる場所を検索します。見つかった場合は、その部分文字列へのポインタを返します。見つからなかった場合はNULLを返します。
    • char *strstr(const char *haystack, const char *needle);
  • C言語の代入式と条件文: C言語では、代入式(例: a = b)は値を持ち、その値は代入された値です。この特性を利用して、while (variable = function_call()) のように、関数呼び出しの結果を変数に代入しつつ、その値が真(非ゼロ)である限りループを継続するというイディオムがよく使われます。しかし、これは==(等価演算子)との混同を招きやすいため、一部のコンパイラは警告を発します。

技術的詳細

このコミットの技術的な核心は、C言語における代入演算子と条件式の評価に関するclangの警告を解消することにあります。

元のコード:

while(q = strstr(p, "PACKAGE")) {

このコードは、strstr(p, "PACKAGE")の戻り値(char *型)をポインタ変数qに代入し、その代入された値がNULLでない限り(つまり、部分文字列が見つかった限り)ループを継続するという意図を持っています。C言語では、ポインタがNULLでない場合は真と評価され、NULLの場合は偽と評価されます。このため、この記述はC言語の標準的なイディオムとして広く使われています。

しかし、clangのような一部のコンパイラは、このような代入が条件式内で行われることを「===の打ち間違い」である可能性を指摘する警告として扱います。これは、プログラマが誤って代入を行ってしまい、意図しない無限ループやロジックエラーを引き起こすことを防ぐための親切な警告機能です。

修正後のコード:

while((q = strstr(p, "PACKAGE")) != NULL) {

この修正では、代入式 (q = strstr(p, "PACKAGE")) を括弧で囲み、その結果を明示的に NULL と比較しています。これにより、コンパイラに対して「これは意図的な代入であり、その結果をNULLと比較している」ということを明確に伝えます。この明示的な比較によって、clangはもはや潜在的なエラーと見なさなくなり、警告が抑制されます。

この変更は、コードの論理的な動作には一切影響を与えません。strstrNULLを返した場合、qにはNULLが代入され、(NULL != NULL)は偽となるためループは終了します。strstrが非NULLのポインタを返した場合、qにはそのポインタが代入され、(非NULLポインタ != NULL)は真となるためループは継続します。これは元のコードの動作と全く同じです。

この修正は、コードの可読性を向上させるという副次的な効果もあります。明示的な!= NULLの比較は、C言語のイディオムに慣れていない読者にとっても、条件の意図をより明確に伝えます。

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

変更はsrc/cmd/gc/mkbuiltin1.cファイルの一箇所のみです。

--- a/src/cmd/gc/mkbuiltin1.c
+++ b/src/cmd/gc/mkbuiltin1.c
@@ -62,7 +62,7 @@ begin:
 		// sys.go claims to be in package PACKAGE to avoid
 		// conflicts during "6g sys.go".  rename PACKAGE to $2.
 		printf("\"\");
-		while(q = strstr(p, "PACKAGE")) {
+		while((q = strstr(p, "PACKAGE")) != NULL) {
 			*q = 0;
 			esc(p);	// up to the substitution
 			printf("%s", name);	// the sub name

コアとなるコードの解説

変更された行は、mkbuiltin1.c内のwhileループの条件式です。

  • 変更前: while(q = strstr(p, "PACKAGE")) {
    • strstr(p, "PACKAGE")は、文字列pの中から"PACKAGE"という部分文字列を検索します。
    • その結果(見つかった場合はポインタ、見つからなかった場合はNULL)が変数qに代入されます。
    • C言語では、ポインタがNULLでない場合は真、NULLの場合は偽と評価されるため、qNULLでない限りループが継続します。
  • 変更後: while((q = strstr(p, "PACKAGE")) != NULL) {
    • まず、q = strstr(p, "PACKAGE")という代入式が評価され、その結果がqに代入されます。
    • 次に、この代入式の「結果」(つまりqの値)が明示的にNULLと比較されます。
    • != NULLという比較によって、qNULLでない場合にのみ条件が真となり、ループが継続します。

この変更は、コードの動作を変えることなく、clangコンパイラが発する「代入が条件式内で行われている」という警告を解消することを目的としています。これは、より堅牢なコードを記述し、コンパイラの警告リストをクリーンに保つための一般的なプラクティスです。

関連リンク

参考にした情報源リンク