<div dir="ltr"><div>This patch address issue 22924.</div><div><br></div><div>Context of the issue:</div><div>----------------------</div><div>Currently code</div><div><br></div><div><span style="white-space:pre-wrap">     </span>$ cat bad-init.cpp</div><div><span style="white-space:pre-wrap">       </span>class A {</div><div><span style="white-space:pre-wrap">        </span>public:</div><div><span style="white-space:pre-wrap">  </span>    A(int a): i_(a) {}</div><div><span style="white-space:pre-wrap"> </span>    int i_ = 0;</div><div><span style="white-space:pre-wrap">        </span>};</div><div><br></div><div><span style="white-space:pre-wrap">      </span>void foo() {</div><div><span style="white-space:pre-wrap">     </span>      A *p = new A[2] { A(3), A(5) };</div><div><span style="white-space:pre-wrap"> </span>}</div><div><br></div><div>is rejected with error</div><div><br></div><div><span style="white-space:pre-wrap"> </span>bad-init.cpp:8:36: error: no matching constructor for initialization of 'A'</div><div><span style="white-space:pre-wrap">      </span>      A *p = new A[2] { A(3), A(5) };</div><div><br></div><div><br></div><div>Some arguments in defence of this behaviour is that standard is not completely clear in [expr.new] p19. In fact I was defending it as well, when we discussed this case in our local cpp community. But there was a disagreement on this mater, also it was pointed out that clang ignores array size in new[] (see below), thus it allows producing ill-formed code for aggregate initialisation.</div><div><br></div><div>Keeping this behaviour intact produces inconsistency because:</div><div>1) Ill-formed code shouldn't be accepted by compiler.</div><div>2) A *p = new A { A(1) }; // compiles fine in clang.</div><div>3) It differs from array initialisation: A a[2] { {1}, {2} }; // compiles fine.</div><div>4) Other mainline compilers treat this code as valid.</div><div>5) clang backend itself produces code not using default constructor in the following example:</div><div><br></div><div><span style="white-space:pre-wrap">       </span>$ cat good-init.cpp</div><div><span style="white-space:pre-wrap">      </span>class A {</div><div><span style="white-space:pre-wrap">        </span>public:</div><div><span style="white-space:pre-wrap">  </span>    A() {}</div><div><span style="white-space:pre-wrap">     </span>    A(int a): i_(a) {}</div><div><span style="white-space:pre-wrap"> </span>    int i_;</div><div><span style="white-space:pre-wrap">    </span>};</div><div><br></div><div><span style="white-space:pre-wrap">      </span>void foo() {</div><div><span style="white-space:pre-wrap">     </span>      A *p = new A[2] { A(3), A(5) };</div><div><span style="white-space:pre-wrap"> </span>}</div><div><br></div><div><span style="white-space:pre-wrap">       </span>$ clang++ -cc1 --std=c++11 -emit-llvm ./good-init.cpp; cat ./good-init.ll | grep "foo" -A 11</div><div><span style="white-space:pre-wrap">   </span>define void @_Z3foov() #0 {</div><div><span style="white-space:pre-wrap">      </span>  %p = alloca %class.A*, align 8</div><div><span style="white-space:pre-wrap">        </span>  %1 = call noalias i8* @_Znam(i64 8) #2</div><div><span style="white-space:pre-wrap">        </span>  %2 = bitcast i8* %1 to %class.A*</div><div><span style="white-space:pre-wrap">      </span>  call void @_ZN1AC1Ei(%class.A* %2, i32 3)</div><div><span style="white-space:pre-wrap">     </span>  %3 = getelementptr inbounds %class.A, %class.A* %2, i64 1</div><div><span style="white-space:pre-wrap">     </span>  call void @_ZN1AC1Ei(%class.A* %3, i32 5)</div><div><span style="white-space:pre-wrap">     </span>  %4 = getelementptr inbounds %class.A, %class.A* %3, i64 1</div><div><span style="white-space:pre-wrap">     </span>  store %class.A* %2, %class.A** %p, align 8</div><div><span style="white-space:pre-wrap">    </span>  ret void</div><div><span style="white-space:pre-wrap">      </span>}</div><div><br></div><div>It seems that rejection of code written in bad-init.cpp is not caused by interpretation of standard at all.</div><div>Roots of the issue are in a way Sema::BuildCXXNew() handles initialiser list for arrays.</div><div><br></div><div>It is possible in clang to compile this code without any errors:</div><div><br></div><div><span style="white-space:pre-wrap">     </span>int *p = new int[2] { 1, 2, 3 };</div><div><br></div><div>Moreover, AST produced by parsing</div><div><br></div><div><span style="white-space:pre-wrap">       </span>int *p = new int[2] { 1, 2 };</div><div><br></div><div>reveals interesting fact:</div><div><br></div><div><span style="white-space:pre-wrap">  </span>|-VarDecl 0x10387e600 <./test.cpp:1:1, col:28> col:6 p 'int *' cinit</div><div><span style="white-space:pre-wrap">       </span>| `-CXXNewExpr 0x10387ee00 <col:10, col:28> 'int *' array Function 0x10387e8f0 'operator new[]' 'void *(unsigned long)'</div><div><span style="white-space:pre-wrap">    </span>|   |-IntegerLiteral 0x10387e6a0 <col:18> 'int' 2</div><div><span style="white-space:pre-wrap"> </span>|   `-InitListExpr 0x10387ed98 <col:21, col:28> 'int [3]'</div><div><span style="white-space:pre-wrap"> </span>|     |-array filler</div><div><span style="white-space:pre-wrap">   </span>|     | `-ImplicitValueInitExpr 0x10387edf0 <<invalid sloc>> 'int'</div><div><span style="white-space:pre-wrap"> </span>|     |-IntegerLiteral 0x10387e6c0 <col:23> 'int' 1</div><div><span style="white-space:pre-wrap">      </span>|     `-IntegerLiteral 0x10387e6e0 <col:26> 'int' 2</div><div><br></div><div>It treats int[2] as int[3] (note: 3 = 1 + number of elements in initialiser lists). As result array filler appears out of nowhere. Emitted code contains 3 (three) constructor calls, although those objects have no chance to be constructed because new is called with all-ones value, thus with libcxx on my machine it produces std::bad_alloc in runtime (although I’d expect std::bad_array_new_length).</div><div><br></div><div>In brief, clang fails to compile code written in bad-init.cpp because default constructor used as array filler initialiser is absent. Array filler shouldn’t be used if number of initialisers is equal to a number of array elements.</div><div><br></div><div>Attached file 22924.patch addresses this by changing way of handling arrays with size specified by integer constant expressions.</div><div><br></div><div>The changes are:</div><div>* no array fillers are generated for a code similar to mentioned above.</div><div>* error is reported if number of initialisers for an array is bigger than a number of that array elements.</div><div>Some notable differences in compiler diagnostic output are posted in <a href="http://pastebin.com/2cNHf1kq" target="_blank">http://pastebin.com/2cNHf1kq</a></div><div><br></div><div>Patch passes all clang-test regression tests except:</div><div>1) CodeGenCXX/new-array-init.cpp, because it contains excess number of initialisers which is treated as error after applying this patch:</div><div><span style="white-space:pre-wrap"> </span>CodeGenCXX/new-array-init.cpp:<wbr>18:3: error: excess elements in array 'new' initialiser</div><div><span style="white-space:pre-wrap"> </span>  new int[2] { 1, 2, 3 };</div><div><br></div><div>I believe this error report is correct.</div><div>That means test case const_underflow() should be removed, emitting code for underflow cases makes sense only if size is not constant expression known at compile-time.</div><div><br></div><div>2) SemaCXX/new-delete-cxx0x.cpp, because it explicitly rejects code which is valid after applying patch. Basically this test contains simplified version of the code sample from bad-init.cpp.</div><div><br></div><div>Therefore, I’ve updated those tests as well (see attached 22924-tests.patch).</div><div><br></div><div>—</div><div>Best regards,</div><div>Alexey Tarasov</div></div>