3

このコード:

#include <iostream>
struct Acc {
    int a;
};
struct Buu {
    int b;
};

struct Foo {
    const Acc& acc;
    Buu& buu;
};

void printInfo( const Foo& ) {
    std::cout << "hi!" << std::endl;
}

void call( Buu& buu ) {
    Acc acc = { 1 };
    Foo foo = {
        .acc = acc,
        .buu = buu,
    };
    std::cout << "before" << std::endl;
    printInfo( foo );
    std::cout << "after" << std::endl;
}
void noCall( Buu& buu ) {
    Acc acc = { 1 };
    Foo foo = {
        .buu = buu,
        .acc = acc,
    };
    std::cout << "before" << std::endl;
    printInfo( foo );
    std::cout << "after" << std::endl;
}

int main() {
    Buu buu = { 2 };
    call( buu );
    noCall( buu );
    return 0;
}

clang (私は 3.7.0、3.7.1 を試しました) でコンパイルすると、次のようになります。

before
hi!
after
before
after

の 2 番目の呼び出しが削除されました...とのprintInfo違いは、指定されたイニシャライザの順序のみです。callnoCall

オプションを-pedantic指定すると、指定された初期化子は C99 の機能であり、C++ ではなく、2 回目の呼び出しなしでコードを作成するという警告が生成されますprintInfo

既知のバグですか?

4

1 に答える 1

3

fooClangがin functionへのすべての参照を単純に削除する場合、警告はペダンディックレベルでのみ発生するため、バグではないにしても、少なくとも不公平だと思いますnocall。これは、アセンブリ コードをデバッグ モード ( c++ -S -g file.cpp) で見て、コンパイラが各行をどのように解釈するかを正確に確認することで確認できます。

.s で生成されたファイルを見ると、呼び出しで 20 行目Foo foo = {...と 25行目printInfo(foo)が生成されていることがわかります。

    .loc    1 20 0                  # ess.cpp:20:0
    movq    %rcx, -64(%rbp)
    movq    -40(%rbp), %rcx
.Ltmp45:
    movq    %rcx, -56(%rbp)
    .loc    1 24 0                  # ess.cpp:24:0
    movq    %rax, %rdi
    callq   _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
    leaq    -64(%rbp), %rdi
    leaq    _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rcx
    movq    %rax, -24(%rbp)
    movq    %rcx, -32(%rbp)
    movq    -24(%rbp), %rax
    .loc    5 322 0                 # /usr/include/c++/v1/ostream:322:0
.Ltmp46:
    movq    %rdi, -72(%rbp)         # 8-byte Spill
    movq    %rax, %rdi
    callq   *-32(%rbp)
.Ltmp47:
    .loc    1 25 0                  # ess.cpp:25:0
    movq    -72(%rbp), %rdi         # 8-byte Reload
    movq    %rax, -80(%rbp)         # 8-byte Spill
    callq   _Z9printInfoRK3Foo
    leaq    _ZNSt3__14coutE, %rdi
    leaq    .L.str2, %rsi

ただし、nocall の場合、対応する行 (30 と 35) は次のようにはなりません。

    .loc    1 29 0 prologue_end     # ess.cpp:29:0
.Ltmp57:
    movl    .L_ZZ6noCallR3BuuE3acc, %ecx
    movl    %ecx, -48(%rbp)
    .loc    1 34 0                  # ess.cpp:34:0
    movq    %rax, %rdi
    callq   _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
    leaq    _ZNSt3__14coutE, %rdi
    leaq    .L.str2, %rsi
    leaq    _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rdx
    movq    %rax, -24(%rbp)
    movq    %rdx, -32(%rbp)
    movq    -24(%rbp), %rax
    .loc    5 322 0                 # /usr/include/c++/v1/ostream:322:0
.Ltmp58:
    movq    %rdi, -72(%rbp)         # 8-byte Spill
    movq    %rax, %rdi
    movq    %rsi, -80(%rbp)         # 8-byte Spill
    callq   *-32(%rbp)
.Ltmp59:
    .loc    1 36 0                  # ess.cpp:36:0
    movq    -72(%rbp), %rdi         # 8-byte Reload
    movq    -80(%rbp), %rsi         # 8-byte Reload
    movq    %rax, -88(%rbp)         # 8-byte Spill
    callq   _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
    leaq    _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rdx
    movq    %rax, -8(%rbp)
    movq    %rdx, -16(%rbp)
    movq    -8(%rbp), %rdi
    .loc    5 322 0                 # /usr/include/c++/v1/ostream:322:0
.Ltmp60:
    callq   *-16(%rbp)
.Ltmp61:
    .loc    1 37 0                  # ess.cpp:37:0

cpp ファイルの番号付きの行は次のとおりです。

18  void call( Buu& buu ) {
19      Acc acc = { 1 };
20      Foo foo = {
21          .acc = acc,
22          .buu = buu,
23      };
24      std::cout << "before" << std::endl;
25      printInfo( foo );
26      std::cout << "after" << std::endl;
27  }
28  void noCall( Buu& buu ) {
29      Acc acc = { 1 };
30      Foo foo = {
31              .buu = buu,
32              .acc = acc
33      };
34      std::cout << "before" << std::endl;
35      printInfo( foo );
36      std::cout << "after" << std::endl;
37  }

私の理解では、clang は、C++ モードで C99 構文を処理しない場合でも、C99 構文を処理するふりをします。


私見、これは clang に報告できるバグです。少なくとも 1.4 実装コンプライアンス [intro.compliance] に従って診断を発行する必要があるためです。

1診断可能な規則のセットは、「診断は必要ない」という明示的な表記を含む規則、または「未定義の動作」をもたらすと記述されている規則を除いて 、この国際規格のすべての構文規則および意味規則で構成されます。
2 この国際標準は C++ 実装に関する要件のみを述べていますが、これらの要件は、プログラム、プログラムの一部、またはプログラムの実行に関する要件として表現されている場合、理解しやすいことがよくあります。このような要件には、次の意味があります。

  • プログラムにこの国際規格の規則違反が含まれていない場合、適合する実装は、そのリソース制限内で、そのプログラムを受け入れて正しく実行する必要があります。
  • 実装がその構成をサポートしていないのに、プログラムが診断可能な規則の違反、またはこの標準で「条件付きでサポートされる」と記述されている構成の発生を 含む場合、適合する実装は少なくとも 1 つの診断メッセージを発行するものとします

...
8 準拠する実装には、整形式プログラムの動作を変更しないという条件で、拡張 (追加のライブラリ関数を含む) を含めることができます。実装は、この国際標準に従って不適切な形式の拡張機能を使用するプログラムを診断する必要があります。ただし、そうした後は、そのようなプログラムをコンパイルして実行できます。

于 2017-01-18T16:31:55.267 に答える