From: Richard Smith Date: Sat, 7 Jul 2012 08:35:56 +0000 (+0000) Subject: PR12670: Support for initializing an array of non-aggregate class type from an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=20599392a99956eaac4cf351a0935574090cb6c3;p=clang PR12670: Support for initializing an array of non-aggregate class type from an initializer list. Patch by Olivier Goffart, with extra testcases by Meador Inge and Daniel Lunow. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159896 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 903c0c4b18..0e5a02f342 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -687,22 +687,21 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, } else if (DeclType->isVectorType()) { CheckVectorType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); - } else if (DeclType->isAggregateType()) { - if (DeclType->isRecordType()) { - RecordDecl *RD = DeclType->getAs()->getDecl(); - CheckStructUnionTypes(Entity, IList, DeclType, RD->field_begin(), - SubobjectIsDesignatorContext, Index, - StructuredList, StructuredIndex, - TopLevelObject); - } else if (DeclType->isArrayType()) { - llvm::APSInt Zero( - SemaRef.Context.getTypeSize(SemaRef.Context.getSizeType()), - false); - CheckArrayType(Entity, IList, DeclType, Zero, - SubobjectIsDesignatorContext, Index, - StructuredList, StructuredIndex); - } else - llvm_unreachable("Aggregate that isn't a structure or array?!"); + } else if (DeclType->isRecordType()) { + assert(DeclType->isAggregateType() && + "non-aggregate records should be handed in CheckSubElementType"); + RecordDecl *RD = DeclType->getAs()->getDecl(); + CheckStructUnionTypes(Entity, IList, DeclType, RD->field_begin(), + SubobjectIsDesignatorContext, Index, + StructuredList, StructuredIndex, + TopLevelObject); + } else if (DeclType->isArrayType()) { + llvm::APSInt Zero( + SemaRef.Context.getTypeSize(SemaRef.Context.getSizeType()), + false); + CheckArrayType(Entity, IList, DeclType, Zero, + SubobjectIsDesignatorContext, Index, + StructuredList, StructuredIndex); } else if (DeclType->isVoidType() || DeclType->isFunctionType()) { // This type is invalid, issue a diagnostic. ++Index; @@ -710,19 +709,6 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, SemaRef.Diag(IList->getLocStart(), diag::err_illegal_initializer_type) << DeclType; hadError = true; - } else if (DeclType->isRecordType()) { - // C++ [dcl.init]p14: - // [...] If the class is an aggregate (8.5.1), and the initializer - // is a brace-enclosed list, see 8.5.1. - // - // Note: 8.5.1 is handled below; here, we diagnose the case where - // we have an initializer list and a destination type that is not - // an aggregate. - // FIXME: In C++0x, this is yet another form of initialization. - if (!VerifyOnly) - SemaRef.Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list) - << DeclType << IList->getSourceRange(); - hadError = true; } else if (DeclType->isReferenceType()) { CheckReferenceType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); @@ -747,18 +733,25 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, unsigned &StructuredIndex) { Expr *expr = IList->getInit(Index); if (InitListExpr *SubInitList = dyn_cast(expr)) { - unsigned newIndex = 0; - unsigned newStructuredIndex = 0; - InitListExpr *newStructuredList - = getStructuredSubobjectInit(IList, Index, ElemType, - StructuredList, StructuredIndex, - SubInitList->getSourceRange()); - CheckExplicitInitList(Entity, SubInitList, ElemType, newIndex, - newStructuredList, newStructuredIndex); - ++StructuredIndex; - ++Index; - return; - } else if (ElemType->isScalarType()) { + if (!ElemType->isRecordType() || ElemType->isAggregateType()) { + unsigned newIndex = 0; + unsigned newStructuredIndex = 0; + InitListExpr *newStructuredList + = getStructuredSubobjectInit(IList, Index, ElemType, + StructuredList, StructuredIndex, + SubInitList->getSourceRange()); + CheckExplicitInitList(Entity, SubInitList, ElemType, newIndex, + newStructuredList, newStructuredIndex); + ++StructuredIndex; + ++Index; + return; + } + assert(SemaRef.getLangOpts().CPlusPlus && + "non-aggregate records are only possible in C++"); + // C++ initialization is handled later. + } + + if (ElemType->isScalarType()) { return CheckScalarType(Entity, IList, ElemType, Index, StructuredList, StructuredIndex); } else if (ElemType->isReferenceType()) { diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp new file mode 100644 index 0000000000..3450003a6e --- /dev/null +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.list/p3-0x.cpp @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +namespace std { + typedef decltype(sizeof(int)) size_t; + + template + struct initializer_list + { + const E *p; + size_t n; + initializer_list(const E *p, size_t n) : p(p), n(n) {} + }; + + struct string { + string(const char *); + }; + + template + struct pair { + pair(const A&, const B&); + }; +} + +namespace bullet2 { + double ad[] = { 1, 2.0 }; + int ai[] = { 1, 2.0 }; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}} + + struct S2 { + int m1; + double m2, m3; + }; + + S2 s21 = { 1, 2, 3.0 }; + S2 s22 { 1.0, 2, 3 }; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}} + S2 s23 { }; +} + +namespace bullet4_example1 { + struct S { + S(std::initializer_list d) {} + S(std::initializer_list i) {} + S() {} + }; + + S s1 = { 1.0, 2.0, 3.0 }; + S s2 = { 1, 2, 3 }; + S s3 = { }; +} + +namespace bullet4_example2 { + struct Map { + Map(std::initializer_list>) {} + }; + + Map ship = {{"Sophie",14}, {"Surprise",28}}; +} + +namespace bullet4_example3 { + struct S { + S(int, double, double) {} + S() {} + }; + + S s1 = { 1, 2, 3.0 }; + // FIXME: This is an ill-formed narrowing initialization. + S s2 { 1.0, 2, 3 }; + S s3 {}; +} + +namespace bullet5 { + struct S { + S(std::initializer_list) {} + S(const std::string &) {} + }; + + const S& r1 = { 1, 2, 3.0 }; + const S& r2 = { "Spinach" }; + S& r3 = { 1, 2, 3 }; // expected-error {{non-const lvalue reference to type 'bullet5::S' cannot bind to an initializer list temporary}} + const int& i1 = { 1 }; + const int& i2 = { 1.1 }; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}} expected-warning {{implicit conversion}} + const int (&iar)[2] = { 1, 2 }; +} + +namespace bullet6 { + int x1 {2}; + int x2 {2.0}; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}} +} + +namespace bullet7 { + int** pp {}; +} + +namespace bullet8 { + struct A { int i; int j; }; + A a1 { 1, 2 }; + A a2 { 1.2 }; // expected-error {{type 'double' cannot be narrowed to 'int' in initializer list}} expected-note {{override}} expected-warning {{implicit conversion}} + + struct B { + B(std::initializer_list i) {} + }; + B b1 { 1, 2 }; + B b2 { 1, 2.0 }; + + struct C { + C(int i, double j) {} + }; + C c1 = { 1, 2.2 }; + // FIXME: This is an ill-formed narrowing initialization. + C c2 = { 1.1, 2 }; // expected-warning {{implicit conversion}} + + int j { 1 }; + int k { }; +} diff --git a/test/CodeGenCXX/cxx0x-initializer-constructors.cpp b/test/CodeGenCXX/cxx0x-initializer-constructors.cpp new file mode 100644 index 0000000000..e141f30cca --- /dev/null +++ b/test/CodeGenCXX/cxx0x-initializer-constructors.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -std=c++11 -S -emit-llvm -o - %s | FileCheck %s + +struct S { + S(int x) { } + S(int x, double y, double z) { } +}; + +void fn1() { + // CHECK: define void @_Z3fn1v + S s { 1 }; + // CHECK: alloca %struct.S, align 1 + // CHECK: call void @_ZN1SC1Ei(%struct.S* %s, i32 1) +} + +void fn2() { + // CHECK: define void @_Z3fn2v + S s { 1, 2.0, 3.0 }; + // CHECK: alloca %struct.S, align 1 + // CHECK: call void @_ZN1SC1Eidd(%struct.S* %s, i32 1, double 2.000000e+00, double 3.000000e+00) +} + +void fn3() { + // CHECK: define void @_Z3fn3v + S sa[] { { 1 }, { 2 }, { 3 } }; + // CHECK: alloca [3 x %struct.S], align 1 + // CHECK: call void @_ZN1SC1Ei(%struct.S* %arrayinit.begin, i32 1) + // CHECK: call void @_ZN1SC1Ei(%struct.S* %arrayinit.element, i32 2) + // CHECK: call void @_ZN1SC1Ei(%struct.S* %arrayinit.element1, i32 3) +} + +void fn4() { + // CHECK: define void @_Z3fn4v + S sa[] { { 1, 2.0, 3.0 }, { 4, 5.0, 6.0 } }; + // CHECK: alloca [2 x %struct.S], align 1 + // CHECK: call void @_ZN1SC1Eidd(%struct.S* %arrayinit.begin, i32 1, double 2.000000e+00, double 3.000000e+00) + // CHECK: call void @_ZN1SC1Eidd(%struct.S* %arrayinit.element, i32 4, double 5.000000e+00, double 6.000000e+00) +} diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index facd4375b0..2cd66b6f62 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1318,3 +1318,15 @@ namespace PR13273 { // actually call it. static_assert(S{}.t == 0, ""); } + +namespace PR12670 { + struct S { + constexpr S(int a0) : m(a0) {} + constexpr S() : m(6) {} + int m; + }; + constexpr S x[3] = { {4}, 5 }; + static_assert(x[0].m == 4, ""); + static_assert(x[1].m == 5, ""); + static_assert(x[2].m == 6, ""); +} diff --git a/test/SemaCXX/cxx0x-initializer-aggregates.cpp b/test/SemaCXX/cxx0x-initializer-aggregates.cpp index 801a82f570..c83058a5e1 100644 --- a/test/SemaCXX/cxx0x-initializer-aggregates.cpp +++ b/test/SemaCXX/cxx0x-initializer-aggregates.cpp @@ -87,3 +87,32 @@ namespace array_explicit_conversion { (void)test4{{{1}}}; // expected-note {{in instantiation of template class 'array_explicit_conversion::A<-1>' requested here}} } } + +namespace sub_constructor { + struct DefaultConstructor { // expected-note 2 {{not viable}} + DefaultConstructor(); // expected-note {{not viable}} + int x; + }; + struct NoDefaultConstructor1 { // expected-note 2 {{not viable}} + NoDefaultConstructor1(int); // expected-note {{not viable}} + int x; + }; + struct NoDefaultConstructor2 { // expected-note 4 {{not viable}} + NoDefaultConstructor2(int,int); // expected-note 2 {{not viable}} + int x; + }; + + struct Aggr { + DefaultConstructor a; + NoDefaultConstructor1 b; + NoDefaultConstructor2 c; + }; + + Aggr ok1 { {}, {0} , {0,0} }; + Aggr ok2 = { {}, {0} , {0,0} }; + Aggr too_many { {0} , {0} , {0,0} }; // expected-error {{no matching constructor for initialization}} + Aggr too_few { {} , {0} , {0} }; // expected-error {{no matching constructor for initialization}} + Aggr invalid { {} , {&ok1} , {0,0} }; // expected-error {{no matching constructor for initialization}} + NoDefaultConstructor2 array_ok[] = { {0,0} , {0,1} }; + NoDefaultConstructor2 array_error[] = { {0,0} , {0} }; // expected-error {{no matching constructor for initialization}} +} \ No newline at end of file diff --git a/test/SemaCXX/dcl_init_aggr.cpp b/test/SemaCXX/dcl_init_aggr.cpp index bd3de9eb7e..8c5e654fca 100644 --- a/test/SemaCXX/dcl_init_aggr.cpp +++ b/test/SemaCXX/dcl_init_aggr.cpp @@ -15,7 +15,7 @@ struct NonAggregate { }; NonAggregate non_aggregate_test = { 1, 2 }; // expected-error{{non-aggregate type 'NonAggregate' cannot be initialized with an initializer list}} -NonAggregate non_aggregate_test2[2] = { { 1, 2 }, { 3, 4 } }; // expected-error 2 {{initialization of non-aggregate type 'NonAggregate' with an initializer list}} +NonAggregate non_aggregate_test2[2] = { { 1, 2 }, { 3, 4 } }; // expected-error 2 {{non-aggregate type 'NonAggregate' cannot be initialized with an initializer list}} // C++ [dcl.init.aggr]p3