3

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?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 2
    g++ thorws *sorry, unimplemented: non-trivial designated initializers not supported*. Clang might not support it as well but instead of issuing an error it just drops the code. – NathanOliver Jan 18 '17 at 15:42
  • @NathanOliver clang's attitude towards UB and things not in the standard: http://i3.kym-cdn.com/photos/images/newsfeed/001/012/861/520.jpg – Alexei Averchenko Jan 18 '17 at 16:16
  • @AlexeiAverchenko LMAO. I think that how most compilers behave. – NathanOliver Jan 18 '17 at 16:18
  • 2
    @NathanOliver: I think that at least a diagnostic should be issued because it is incorrect syntax according to the standard and it is not a **supported** extension either, because the generated code discards the offending lines. – Serge Ballesta Jan 18 '17 at 16:48

1 Answers1

3

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.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252