異なる翻訳単位での競合する宣言に関する Polyspace リンク作成エラーの修正
問題
解析で、次のエラー メッセージのいずれかのようなエラーまたは警告が表示されます。
Declaration of [...] is incompatible with a declaration in another translation unit ([...])
このメッセージは、競合する宣言が同一のヘッダー ファイルに由来していない場合に表示されます。
競合する宣言の 1 つがヘッダー ファイルにある場合。
Declaration of [...] had a different meaning during compilation of [...] ([...])
このメッセージは、競合する宣言が異なるソース ファイルにインクルードされた同一のヘッダー ファイルに由来している場合に表示されます。
このエラーは、異なる翻訳単位で同じ変数、関数、またはデータ型が異なって宣言されていることを示します。競合する宣言は、One Definition Rule (定義は 1 度の規則。C++ 規格、ISO/IEC 14882:2003、節 3.2 参照) に違反します。宣言の競合が発生した場合、Polyspace® Code Prover™ では宣言を選択せず、解析を続行しません。
一般的なコンパイル ツールチェーンでは、多くの場合、リンク作成プロセスでデータ型情報は格納されません。競合する宣言によってコンパイラでエラーは発生しません。特定のランタイム エラーがないことを保証するために、Polyspace Code Prover はリンク作成の厳格な規格に準拠しています。
エラーの根本原因を特定するには、以下を行います。
エラー メッセージから、宣言が競合している 2 つのソース ファイルを特定します。
たとえば、エラー メッセージは次のメッセージのようになります。
メッセージに、C:\field.h, line 1: declaration of class "a_struct" had a different meaning during compilation of "file1.cpp" | struct a_struct { | | Detected during compilation of secondary translation unit "file2.cpp"file1.cppおよびfile2.cppで構造体a_structの宣言が競合しており、両方がヘッダー ファイルfield.hをインクルードしていることが示されています。別のエラー メッセージは以下のようになります。
メッセージに、C:\field2.h, line 1: declaration of class "a_struct" had is incompatible with a declaration in another translation unit | the other declaration is at line 1 of field1.h" | struct a_struct { | | Detected during compilation of secondary translation unit "file2.cpp"field2.hおよびfield.hで構造体a_structの宣言が競合していることが示されています。ヘッダー ファイルfield2.hがソース ファイルfile2.cppにインクルードされています。ソース ファイル内の競合する宣言の特定を試みます。
そうしない場合は、それらのファイルを含む翻訳単位を開きます。場合によっては、ソース ファイルよりも翻訳単位や前処理済みファイルに宣言の競合がよりはっきり示されることがあります。それは、
#includeステートメントや#defineステートメントなどのプリプロセッサ命令が適切に置換されて、マクロが展開されるからです。すべての翻訳単位が保存されるようにフラグ
-keep-relaunch-filesを使用して解析を再実行します。ユーザー インターフェイスで、オプションOtherにフラグを入力します。コンパイル後に解析が停止します。翻訳単位や前処理済みファイルは、結果フォルダーのサブフォルダー
.relaunchにある圧縮ファイルci.zipに格納されます。ci.zipの内容を解凍します。前処理済みファイルの名前はソース ファイルと同じです。たとえば、
file1.cppの前処理済みファイルの名前はfile1.ciです。
前処理済みファイルでエラー メッセージに示された行番号を開くと、競合する宣言を見つけることができます。
考えられる原因: 変数の宣言と定義の不一致
変数宣言が定義と一致しません。次に例を示します。
宣言と定義で使用しているデータ型が異なる。
変数が符号付きと宣言されているが、定義では符号なしになっている。
宣言と定義で使用している型修飾子が異なる。
変数が配列と宣言されているが、定義では非配列変数になっている。
宣言と定義で使用している配列変数の配列サイズが異なる。
この例では、コードで型修飾子の不一致によりリンク作成エラーが表示されます。file1.c の宣言では型修飾子が使用されていませんが、file2.c の定義では volatile 修飾子が使用されています。
file1.c | file2.c |
|---|---|
extern int x;
void main(void)
{/* Variable x used */}
| volatile int x; |
この場合、通常はソース ファイルを調査することで違いを見つけることができます。前処理済みファイルを確認する必要はありません。
解決法
変数宣言が定義と一致していることを確認します。
考えられる原因: 関数の宣言と定義の不一致
関数宣言が定義と一致しません。次に例を示します。
宣言と定義で使用している引数または戻り値のデータ型が異なる。
宣言と定義で使用している引数の数が異なる。
ある関数で可変引数 (varargs) 関数が宣言されているが、事前の宣言がない別の関数で呼び出されている。
この場合、エラー メッセージで関数に必要なプロトタイプがないことが示されます。
この例では、コードで戻り値の型の不一致によりリンク作成エラーが表示されます。file1.c の宣言では戻り値の型が int になっていますが、file2.c の定義では戻り値の型が float になっています。
file1.c | file2.c |
|---|---|
int input(void);
void main() {
int val = input();
} | float input(void) {
float x = 1.0;
return x;
} |
この場合、通常はソース ファイルを調査することで違いを見つけることができます。前処理済みファイルを確認する必要はありません。
解決法
関数宣言が定義と一致していることを確認します。
それらのエラーは、ビルド プロセスでは許容される場合でも、実行時に予期しない結果をもたらすことがあります。コード内の関数の宣言と定義でプロトタイプに競合があると、その関数を呼び出したときに予期しない結果になる可能性があります。
可変引数 (varargs) 関数の場合は、関数を定義してから呼び出します。ソース コードを変更せずに、このリンク作成エラーを回避できます。
別のファイルで関数宣言を追加します。
検証のためだけに、このファイルをオプション
[インクルード] (-include)を使用してすべてのソース ファイルに#includeします。
考えられる原因: 関連していない宣言が競合している
関連していない 2 つのオブジェクトに同一の識別子名を使用しています。関連していないオブジェクトが同一の Polyspace プロジェクトに含まれる一般的な理由のいくつかを以下に示します。
外部リンクを含まないようオブジェクトを
staticで宣言することを意図していたが、static指定子を省略した。宣言をヘッダー ファイルに配置してソース ファイルにインクルードするのではなく、同じオブジェクトを複数のソース ファイルで宣言した。
polyspace-configureコマンドを使用して、ビルド コマンドから Polyspace プロジェクトを作成した。ビルド コマンドによって複数の独立したバイナリが作成されたが、すべてのバイナリに関連するファイルが 1 つの Polyspace プロジェクトにまとめられた。
解決法
同じ名前を使用する関連のないオブジェクトが生じた根本原因に応じて、適切な解決法を使用します。
Polyspace プロジェクトがビルド コマンドから作成され、独立したバイナリのソース ファイルがまとめられた場合、ビルド コマンドをトレースする際にプロジェクトをモジュールに分割します。詳細は、以下を参照してください。
考えられる原因: マクロに依存する定義
先に定義されたマクロに変数定義が依存しています。あるソース ファイルがマクロを定義しており、別のソース ファイルでは定義していない場合、変数定義の競合が発生します。
この例では、file1.cpp および file2.cpp がヘッダー ファイル field.h をインクルードしています。ヘッダー ファイルでは、マクロ定義に依存する構造体 a_struct を定義しています。2 つのファイルのうちの 1 つ file2.cpp のみがマクロ DEBUG を定義しています。file1.cpp の翻訳単位に含まれる a_struct の定義が file2.cpp の単位に含まれる定義と異なっています。
file1.cpp | file2.cpp |
|---|---|
#include "field.h"
int main()
{
a_struct s;
init_a_struct(&s);
return 0;
} | #define DEBUG
#include <string.h>
#include "field.h"
void init_a_struct(a_struct* s)
{
memset(s, 0, sizeof(*s));
} |
struct a_struct {
int n;
#ifdef DEBUG
int debug;
#endif
};
| |
前処理済みファイル file1.ci と file2.ci を開くと、競合する宣言があります。
file1.ci | file2.ci |
|---|---|
struct a_struct {
int n;
}; | struct a_struct {
int n;
int debug;
}; |
解決法
マクロに依存する定義を回避します。そうでない場合、リンク作成エラーを修正します。変数定義を含むすべてのパスでマクロが定義または定義解除されていることを確認します。
考えられる原因: マクロとして再定義されたキーワード
キーワードがマクロとして再定義されていますが、すべてのファイルで再定義されているわけではありません。
この例では、bool は file1.cpp のキーワードですが、file2.cpp でマクロとして再定義されています。
file1.cpp | file2.cpp |
|---|---|
#include "bool.h"
int main()
{
return 0;
} | #define false 0 #define true (!false) #include "bool.h" |
template <class T>
struct a_struct {
bool flag;
T t;
a_struct() {
flag = true;
}
};
| |
解決法
プログラム全体でキーワードの使用方法が一貫するようにします。標準ライブラリ ヘッダーで定義されたキーワードを使用するか、再定義したバージョンを使用します。
考えられる原因: 構造体パックの違い
#pragma pack(n) ステートメントは、構造体パック アライメントを変更しますが、すべてのファイルで変更するわけではありません。#pragma 命令に関する Code Prover の仮定も参照してください。
この例では、file1.cpp では既定のパック アライメントが使用されていますが、file2.cpp では #pragma pack(1) ステートメントによって 1 バイトのパック アライメントが強制されています。
file1.cpp | file2.cpp |
|---|---|
int main()
{
return 0;
} | #pragma pack(1) #include "pack.h" |
struct a_struct {
char ch;
short sh;
};
| |
解決法
#pragma pack(n) ステートメントをヘッダー ファイルに入力し、ステートメントがそのヘッダーを含むすべてのソース ファイルに適用されるようにします。