this code:
#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;
}
when compliled by clang (I tried 3.7.0, 3.7.1) will out:
before
hi!
after
before
after
The second call of printInfo
was removed... Difference between call
and noCall
only in order of designated initializers.
With -pedantic
option it will produce warnings that designated initializers is the feature of C99 but not C++ but still create code without the second call of printInfo
.
Is it known bug?
I think that is at least unfair if not a bug, because the warning is only at pedandic level when Clang simply removes all references to foo
in function nocall
. We can confirm it by looking at assembly code in debug mode (c++ -S -g file.cpp
) to see exactly how the compiler interprets each and every line.
When we look at the .s genererated file, we can see that in call, lines 20 Foo foo = {...
and 25 printInfo(foo)
are generated:
.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
But for nocall, the corresponding lines (30 and 35) are not:
.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
Where the numbered lines in the cpp file are:
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 }
My understanding is that clang pretends to process the C99 syntax in C++ mode when it does not.
IMHO, this is a bug that could be reported to clang, because at least a diagnostic should be issued per 1.4 Implementation compliance [intro.compliance]
1 The set of diagnosable rules consists of all syntactic and semantic rules in this International Standard except for those rules containing an explicit notation that “no diagnostic is required” or which are described as resulting in “undefined behavior.”
2 Although this International Standard states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs. Such requirements have the following meaning:
- If a program contains no violations of the rules in this International Standard, a conforming implementation shall, within its resource limits, accept and correctly execute2 that program.
- If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this Standard as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.
...
8 A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.