Patch for issue #22924

Alexey Tarasov via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 24 07:16:15 PDT 2016


This patch address issue 22924.

Context of the issue:
----------------------
Currently code

$ cat bad-init.cpp
class A {
public:
   A(int a): i_(a) {}
   int i_ = 0;
};

void foo() {
     A *p = new A[2] { A(3), A(5) };
}

is rejected with error

bad-init.cpp:8:36: error: no matching constructor for initialization of 'A'
     A *p = new A[2] { A(3), A(5) };


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.

Keeping this behaviour intact produces inconsistency because:
1) Ill-formed code shouldn't be accepted by compiler.
2) A *p = new A { A(1) }; // compiles fine in clang.
3) It differs from array initialisation: A a[2] { {1}, {2} }; // compiles
fine.
4) Other mainline compilers treat this code as valid.
5) clang backend itself produces code not using default constructor in the
following example:

$ cat good-init.cpp
class A {
public:
   A() {}
   A(int a): i_(a) {}
   int i_;
};

void foo() {
     A *p = new A[2] { A(3), A(5) };
}

$ clang++ -cc1 --std=c++11 -emit-llvm ./good-init.cpp; cat ./good-init.ll |
grep "foo" -A 11
define void @_Z3foov() #0 {
 %p = alloca %class.A*, align 8
 %1 = call noalias i8* @_Znam(i64 8) #2
 %2 = bitcast i8* %1 to %class.A*
 call void @_ZN1AC1Ei(%class.A* %2, i32 3)
 %3 = getelementptr inbounds %class.A, %class.A* %2, i64 1
 call void @_ZN1AC1Ei(%class.A* %3, i32 5)
 %4 = getelementptr inbounds %class.A, %class.A* %3, i64 1
 store %class.A* %2, %class.A** %p, align 8
 ret void
}

It seems that rejection of code written in bad-init.cpp is not caused by
interpretation of standard at all.
Roots of the issue are in a way Sema::BuildCXXNew() handles initialiser
list for arrays.

It is possible in clang to compile this code without any errors:

int *p = new int[2] { 1, 2, 3 };

Moreover, AST produced by parsing

int *p = new int[2] { 1, 2 };

reveals interesting fact:

|-VarDecl 0x10387e600 <./test.cpp:1:1, col:28> col:6 p 'int *' cinit
| `-CXXNewExpr 0x10387ee00 <col:10, col:28> 'int *' array Function
0x10387e8f0 'operator new[]' 'void *(unsigned long)'
|   |-IntegerLiteral 0x10387e6a0 <col:18> 'int' 2
|   `-InitListExpr 0x10387ed98 <col:21, col:28> 'int [3]'
|     |-array filler
|     | `-ImplicitValueInitExpr 0x10387edf0 <<invalid sloc>> 'int'
|     |-IntegerLiteral 0x10387e6c0 <col:23> 'int' 1
|     `-IntegerLiteral 0x10387e6e0 <col:26> 'int' 2

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).

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.

Attached file 22924.patch addresses this by changing way of handling arrays
with size specified by integer constant expressions.

The changes are:
* no array fillers are generated for a code similar to mentioned above.
* error is reported if number of initialisers for an array is bigger than a
number of that array elements.
Some notable differences in compiler diagnostic output are posted in
http://pastebin.com/2cNHf1kq

Patch passes all clang-test regression tests except:
1) CodeGenCXX/new-array-init.cpp, because it contains excess number of
initialisers which is treated as error after applying this patch:
CodeGenCXX/new-array-init.cpp:18:3: error: excess elements in array 'new'
initialiser
 new int[2] { 1, 2, 3 };

I believe this error report is correct.
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.

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.

Therefore, I’ve updated those tests as well (see attached
22924-tests.patch).

—
Best regards,
Alexey Tarasov
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20160925/01d14221/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 22924-tests.patch
Type: application/octet-stream
Size: 1595 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20160925/01d14221/attachment-0002.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 22924.patch
Type: application/octet-stream
Size: 6210 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20160925/01d14221/attachment-0003.obj>


More information about the cfe-commits mailing list