如果不是错误,我认为这至少是不公平的,因为只有当 Clang 简单地删除对foo
in function 的所有引用时,警告才处于迂腐级别nocall
。我们可以通过在调试模式 ( ) 下查看汇编代码来确认它,c++ -S -g file.cpp
以准确了解编译器如何解释每一行。
当我们查看 .s 生成的文件时,我们可以看到在 call 中,生成了第 20Foo 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 语法,而不是。
恕我直言,这是一个可以报告给 clang 的错误,因为至少应该根据 1.4 实施合规性发布诊断 [intro.compliance]
1可诊断规则集由本国际标准中的所有句法和语义规则组成,但那些包含“不需要诊断”的明确表示法或被描述为导致“未定义行为”的规则除外。
2 尽管本国际标准仅规定了对 C++ 实现的要求,但如果将这些要求表述为对程序、程序部分或程序执行的要求,则通常更容易理解。此类要求具有以下含义:
- 如果程序不违反本国际标准中的规则,则符合要求的实现应在其资源限制内接受并正确执行该程序。
- 如果程序包含违反任何可诊断规则或出现在本标准中描述为“有条件支持”的构造,而实现不支持该构造,则
符合要求的实现应发出至少一个诊断消息。
...
8 一个符合要求的实现可以有扩展(包括额外的库函数),只要它们不改变任何格式良好的程序的行为。需要实现来诊断使用根据本国际标准格式错误的扩展的程序。然而,这样做之后,他们可以编译和执行这样的程序。