From: Douglas Gregor Date: Fri, 30 Jan 2009 22:09:00 +0000 (+0000) Subject: Implement and test aggregate initialization in C++. Major changes: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=930d8b5ecc074cca01ecd9a522a55f55f3b72396;p=clang Implement and test aggregate initialization in C++. Major changes: - Support initialization of reference members; complain if any reference members are left uninitialized. - Use C++ copy-initialization for initializing each element (falls back to constraint checking in C) - Make sure we diagnose when one tries to provide an initializer list for a non-aggregate. - Don't complain about empty initializers in C++ (they are permitted) - Unrelated but necessary: don't bother trying to convert the decl-specifier-seq to a type when we're dealing with a C++ constructor, destructor, or conversion operator; it results in spurious warnings. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63431 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 8cafa318f0..6e31de6317 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -270,6 +270,10 @@ DIAG(err_const_var_requires_init, ERROR, "declaration of const variable '%0' requires an initializer") DIAG(err_init_non_aggr_init_list, ERROR, "initialization of non-aggregate type %0 with an initializer list") +DIAG(err_init_reference_member_uninitialized, ERROR, + "reference member of type %0 uninitialized") +DIAG(note_uninit_reference_member, NOTE, + "uninitialized reference member is here") // Objective-C++ DIAG(err_objc_decls_may_only_appear_in_global_scope, ERROR, diff --git a/lib/Parse/ParseInit.cpp b/lib/Parse/ParseInit.cpp index c654a750dd..ec8470cb45 100644 --- a/lib/Parse/ParseInit.cpp +++ b/lib/Parse/ParseInit.cpp @@ -256,10 +256,10 @@ Parser::OwningExprResult Parser::ParseBraceInitializer() { /// was specified for it, if any. InitListDesignations InitExprDesignations(Actions); - // We support empty initializers, but tell the user that they aren't using - // C99-clean code. if (Tok.is(tok::r_brace)) { - Diag(LBraceLoc, diag::ext_gnu_empty_initializer); + // Empty initializers are a C++ feature and a GNU extension to C. + if (!getLang().CPlusPlus) + Diag(LBraceLoc, diag::ext_gnu_empty_initializer); // Match the '}'. return Actions.ActOnInitList(LBraceLoc, Action::MultiExprArg(Actions), InitExprDesignations, ConsumeBrace()); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 3395313f84..5dd45fb072 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1074,23 +1074,7 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType, << Init->getSourceRange(); return CheckSingleInitializer(Init, DeclType, DirectInit); - } else if (getLangOptions().CPlusPlus) { - // 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. - // FIXME: Move this checking into CheckInitList! - if (const RecordType *ClassRec = DeclType->getAsRecordType()) { - const CXXRecordDecl *ClassDecl = cast(ClassRec->getDecl()); - if (!ClassDecl->isAggregate()) - return Diag(InitLoc, diag::err_init_non_aggr_init_list) - << DeclType << Init->getSourceRange(); - } - } + } bool hadError = CheckInitList(InitList, DeclType); Init = InitList; diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index ed44acff7e..5d40e43039 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -69,11 +69,14 @@ class InitListChecker { unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex); - // FIXME: Does DeclType need to be a reference type? - void CheckScalarType(InitListExpr *IList, QualType &DeclType, + void CheckScalarType(InitListExpr *IList, QualType DeclType, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex); + void CheckReferenceType(InitListExpr *IList, QualType DeclType, + unsigned &Index, + InitListExpr *StructuredList, + unsigned &StructuredIndex); void CheckVectorType(InitListExpr *IList, QualType DeclType, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex); @@ -106,6 +109,8 @@ class InitListChecker { Expr *expr); int numArrayElements(QualType DeclType); int numStructUnionElements(QualType DeclType); + + void FillInValueInitializations(InitListExpr *ILE); public: InitListChecker(Sema *S, InitListExpr *IL, QualType &T); bool HadError() { return hadError; } @@ -116,12 +121,12 @@ public: }; } - /// Recursively replaces NULL values within the given initializer list /// with expressions that perform value-initialization of the /// appropriate type. -static void fillInValueInitializations(ASTContext &Context, InitListExpr *ILE) { - assert((ILE->getType() != Context.VoidTy) && "Should not have void type"); +void InitListChecker::FillInValueInitializations(InitListExpr *ILE) { + assert((ILE->getType() != SemaRef->Context.VoidTy) && + "Should not have void type"); if (const RecordType *RType = ILE->getType()->getAsRecordType()) { unsigned Init = 0, NumInits = ILE->getNumInits(); for (RecordDecl::field_iterator Field = RType->getDecl()->field_begin(), @@ -130,17 +135,31 @@ static void fillInValueInitializations(ASTContext &Context, InitListExpr *ILE) { if (Field->isUnnamedBitfield()) continue; - if (Init >= NumInits) - break; - - // FIXME: Check for fields with reference type in C++? - if (!ILE->getInit(Init)) + if (Init >= NumInits) { + if (Field->getType()->isReferenceType()) { + // C++ [dcl.init.aggr]p9: + // If an incomplete or empty initializer-list leaves a + // member of reference type uninitialized, the program is + // ill-formed. + SemaRef->Diag(ILE->getSyntacticForm()->getLocStart(), + diag::err_init_reference_member_uninitialized) + << Field->getType() + << ILE->getSyntacticForm()->getSourceRange(); + SemaRef->Diag(Field->getLocation(), + diag::note_uninit_reference_member); + hadError = true; + } + } else if (!ILE->getInit(Init)) ILE->setInit(Init, - new (Context) ImplicitValueInitExpr(Field->getType())); + new (SemaRef->Context) ImplicitValueInitExpr(Field->getType())); else if (InitListExpr *InnerILE = dyn_cast(ILE->getInit(Init))) - fillInValueInitializations(Context, InnerILE); + FillInValueInitializations(InnerILE); ++Init; + + // Only look at the first initialization of a union. + if (RType->getDecl()->isUnion()) + break; } return; @@ -148,7 +167,7 @@ static void fillInValueInitializations(ASTContext &Context, InitListExpr *ILE) { QualType ElementType; - if (const ArrayType *AType = Context.getAsArrayType(ILE->getType())) + if (const ArrayType *AType = SemaRef->Context.getAsArrayType(ILE->getType())) ElementType = AType->getElementType(); else if (const VectorType *VType = ILE->getType()->getAsVectorType()) ElementType = VType->getElementType(); @@ -158,9 +177,10 @@ static void fillInValueInitializations(ASTContext &Context, InitListExpr *ILE) { for (unsigned Init = 0, NumInits = ILE->getNumInits(); Init != NumInits; ++Init) { if (!ILE->getInit(Init)) - ILE->setInit(Init, new (Context) ImplicitValueInitExpr(ElementType)); + ILE->setInit(Init, + new (SemaRef->Context) ImplicitValueInitExpr(ElementType)); else if (InitListExpr *InnerILE =dyn_cast(ILE->getInit(Init))) - fillInValueInitializations(Context, InnerILE); + FillInValueInitializations(InnerILE); } } @@ -175,9 +195,8 @@ InitListChecker::InitListChecker(Sema *S, InitListExpr *IL, QualType &T) { = getStructuredSubobjectInit(IL, newIndex, T, 0, 0, SourceRange()); CheckExplicitInitList(IL, T, newIndex, FullyStructuredList, newStructuredIndex); - if (!hadError) { - fillInValueInitializations(SemaRef->Context, FullyStructuredList); - } + if (!hadError) + FillInValueInitializations(FullyStructuredList); } int InitListChecker::numArrayElements(QualType DeclType) { @@ -219,7 +238,6 @@ void InitListChecker::CheckImplicitInitList(InitListExpr *ParentIList, else assert(0 && "CheckImplicitInitList(): Illegal type"); - // FIXME: Perhaps we should move this warning elsewhere? if (maxElements == 0) { SemaRef->Diag(ParentIList->getInit(Index)->getLocStart(), diag::err_implicit_empty_initializer); @@ -308,6 +326,20 @@ void InitListChecker::CheckListElementTypes(InitListExpr *IList, 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. + SemaRef->Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list) + << DeclType << IList->getSourceRange(); + hadError = true; + } else if (DeclType->isReferenceType()) { + CheckReferenceType(IList, DeclType, Index, StructuredList, StructuredIndex); } else { // In C, all types are either scalars or aggregates, but // additional handling is needed here for C++ (and possibly others?). @@ -339,21 +371,70 @@ void InitListChecker::CheckSubElementType(InitListExpr *IList, ++Index; } else if (ElemType->isScalarType()) { CheckScalarType(IList, ElemType, Index, StructuredList, StructuredIndex); - } else if (expr->getType()->getAsRecordType() && - SemaRef->Context.typesAreCompatible( - expr->getType().getUnqualifiedType(), - ElemType.getUnqualifiedType())) { - UpdateStructuredListElement(StructuredList, StructuredIndex, expr); - ++Index; - // FIXME: Add checking + } else if (ElemType->isReferenceType()) { + CheckReferenceType(IList, ElemType, Index, StructuredList, StructuredIndex); } else { - CheckImplicitInitList(IList, ElemType, Index, StructuredList, - StructuredIndex); - ++StructuredIndex; - } + if (SemaRef->getLangOptions().CPlusPlus) { + // C++ [dcl.init.aggr]p12: + // All implicit type conversions (clause 4) are considered when + // initializing the aggregate member with an ini- tializer from + // an initializer-list. If the initializer can initialize a + // member, the member is initialized. [...] + ImplicitConversionSequence ICS + = SemaRef->TryCopyInitialization(expr, ElemType); + if (ICS.ConversionKind != ImplicitConversionSequence::BadConversion) { + if (SemaRef->PerformImplicitConversion(expr, ElemType, ICS, + "initializing")) + hadError = true; + UpdateStructuredListElement(StructuredList, StructuredIndex, expr); + ++Index; + return; + } + + // Fall through for subaggregate initialization + } else { + // C99 6.7.8p13: + // + // The initializer for a structure or union object that has + // automatic storage duration shall be either an initializer + // list as described below, or a single expression that has + // compatible structure or union type. In the latter case, the + // initial value of the object, including unnamed members, is + // that of the expression. + QualType ExprType = SemaRef->Context.getCanonicalType(expr->getType()); + QualType ElemTypeCanon = SemaRef->Context.getCanonicalType(ElemType); + if (SemaRef->Context.typesAreCompatible(ExprType.getUnqualifiedType(), + ElemTypeCanon.getUnqualifiedType())) { + UpdateStructuredListElement(StructuredList, StructuredIndex, expr); + ++Index; + return; + } + + // Fall through for subaggregate initialization + } + + // C++ [dcl.init.aggr]p12: + // + // [...] Otherwise, if the member is itself a non-empty + // subaggregate, brace elision is assumed and the initializer is + // considered for the initialization of the first member of + // the subaggregate. + if (ElemType->isAggregateType() || ElemType->isVectorType()) { + CheckImplicitInitList(IList, ElemType, Index, StructuredList, + StructuredIndex); + ++StructuredIndex; + } else { + // We cannot initialize this element, so let + // PerformCopyInitialization produce the appropriate diagnostic. + SemaRef->PerformCopyInitialization(expr, ElemType, "initializing"); + hadError = true; + ++Index; + ++StructuredIndex; + } + } } -void InitListChecker::CheckScalarType(InitListExpr *IList, QualType &DeclType, +void InitListChecker::CheckScalarType(InitListExpr *IList, QualType DeclType, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex) { @@ -399,6 +480,49 @@ void InitListChecker::CheckScalarType(InitListExpr *IList, QualType &DeclType, } } +void InitListChecker::CheckReferenceType(InitListExpr *IList, QualType DeclType, + unsigned &Index, + InitListExpr *StructuredList, + unsigned &StructuredIndex) { + if (Index < IList->getNumInits()) { + Expr *expr = IList->getInit(Index); + if (isa(expr)) { + SemaRef->Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list) + << DeclType << IList->getSourceRange(); + hadError = true; + ++Index; + ++StructuredIndex; + return; + } + + Expr *savExpr = expr; // Might be promoted by CheckSingleInitializer. + if (SemaRef->CheckReferenceInit(expr, DeclType)) + hadError = true; + else if (savExpr != expr) { + // The type was promoted, update initializer list. + IList->setInit(Index, expr); + } + if (hadError) + ++StructuredIndex; + else + UpdateStructuredListElement(StructuredList, StructuredIndex, expr); + ++Index; + } else { + // FIXME: It would be wonderful if we could point at the actual + // member. In general, it would be useful to pass location + // information down the stack, so that we know the location (or + // decl) of the "current object" being initialized. + SemaRef->Diag(IList->getLocStart(), + diag::err_init_reference_member_uninitialized) + << DeclType + << IList->getSourceRange(); + hadError = true; + ++Index; + ++StructuredIndex; + return; + } +} + void InitListChecker::CheckVectorType(InitListExpr *IList, QualType DeclType, unsigned &Index, InitListExpr *StructuredList, diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 43f8baae50..9d78d0adbf 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -18,8 +18,11 @@ #include "clang/Parse/DeclSpec.h" using namespace clang; -/// ConvertDeclSpecToType - Convert the specified declspec to the appropriate -/// type object. This returns null on error. +/// \brief Convert the specified declspec to the appropriate type +/// object. +/// \param DS the declaration specifiers +/// \returns The type described by the declaration specifiers, or NULL +/// if there was an error. QualType Sema::ConvertDeclSpecToType(const DeclSpec &DS) { // FIXME: Should move the logic from DeclSpec::Finish to here for validity // checking. @@ -81,6 +84,7 @@ QualType Sema::ConvertDeclSpecToType(const DeclSpec &DS) { // "At least one type specifier shall be given in the declaration // specifiers in each declaration, and in the specifier-qualifier list in // each struct declaration and type name." + // FIXME: this should be a hard error in C++ if (!DS.hasTypeSpecifier()) Diag(DS.getSourceRange().getBegin(), diag::ext_missing_type_specifier); } @@ -254,8 +258,26 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { if (!getLangOptions().C99 && !getLangOptions().CPlusPlus0x && D.getDeclSpec().getTypeSpecWidth() == DeclSpec::TSW_longlong) Diag(D.getDeclSpec().getTypeSpecWidthLoc(), diag::ext_longlong); - - QualType T = ConvertDeclSpecToType(D.getDeclSpec()); + + // Determine the type of the declarator. Not all forms of declarator + // have a type. + QualType T; + switch (D.getKind()) { + case Declarator::DK_Abstract: + case Declarator::DK_Normal: + case Declarator::DK_Operator: + T = ConvertDeclSpecToType(D.getDeclSpec()); + break; + + case Declarator::DK_Constructor: + case Declarator::DK_Destructor: + case Declarator::DK_Conversion: + // Constructors and destructors don't have return types. Use + // "void" instead. Conversion operators will check their return + // types separately. + T = Context.VoidTy; + break; + } // Walk the DeclTypeInfo, building the recursive type as we go. DeclTypeInfos // are ordered from the identifier out, which is opposite of what we want :). diff --git a/test/SemaCXX/dcl_init_aggr.cpp b/test/SemaCXX/dcl_init_aggr.cpp new file mode 100644 index 0000000000..d8a4180536 --- /dev/null +++ b/test/SemaCXX/dcl_init_aggr.cpp @@ -0,0 +1,107 @@ +// RUN: clang -fsyntax-only -pedantic -verify %s +// C++ [dcl.init.aggr]p2 +struct A { + int x; + struct B { + int i; + int j; + } b; +} a1 = { 1, { 2, 3 } }; + +struct NonAggregate { + NonAggregate(); + + int a, b; +}; +NonAggregate non_aggregate_test = { 1, 2 }; // expected-error{{initialization of non-aggregate type 'struct NonAggregate' with an initializer list}} + +NonAggregate non_aggregate_test2[2] = { { 1, 2 }, { 3, 4 } }; // expected-error{{initialization of non-aggregate type 'struct NonAggregate' with an initializer list}} + + +// C++ [dcl.init.aggr]p3 +A a_init = A(); + +// C++ [dcl.init.aggr]p4 +int x[] = { 1, 3, 5 }; +int x_sizecheck[(sizeof(x) / sizeof(int)) == 3? 1 : -1]; +int x2[] = { }; // expected-warning{{zero size arrays are an extension}} + +// C++ [dcl.init.aggr]p5 +struct StaticMemberTest { + int i; + static int s; + int *j; +} smt = { 1, &smt.i }; + +// C++ [dcl.init.aggr]p6 +char cv[4] = { 'a', 's', 'd', 'f', 0 }; // expected-warning{{excess elements in array initializer}} + +// C++ [dcl.init.aggr]p7 +struct TooFew { int a; char* b; int c; }; +TooFew too_few = { 1, "asdf" }; // okay + +// C++ [dcl.init.aggr]p8 +struct Empty { }; +struct EmptyTest { + Empty s; + int i; +} empty_test = { { }, 3 }; + +EmptyTest empty_test2 = { 3 }; // expected-error{{initializer for aggregate with no elements requires explicit braces}} + +struct NonEmpty { + int a; + Empty empty; +}; +struct NonEmptyTest { + NonEmpty a, b; +} non_empty_test = { { }, { } }; + +// C++ [dcl.init.aggr]p9 +struct HasReference { + int i; + int &j; // expected-note{{uninitialized reference member is here}} +}; +int global_int; +HasReference r1 = { 1, global_int }; +HasReference r2 = { 1 } ; // expected-error{{initialization leaves reference member of type 'int &' uninitialized}} + +// C++ [dcl.init.aggr]p10 +// Note: the behavior here is identical to C +int xs[2][2] = { 3, 1, 4, 2 }; +float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } }; + +// C++ [dcl.init.aggr]p11 +// Note: the behavior here is identical to C +float y2[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 } }; +float same_as_y2[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 }; + +// C++ [dcl.init.aggr]p12 +struct A2 { + int i; + operator int *(); +}; +struct B2 { + A2 a1, a2; + int *z; +}; +struct C2 { + operator A2(); +}; +struct D2 { + operator int(); +}; +A2 a2; +C2 c2; +D2 d2; +B2 b2 = { 4, a2, a2 }; +B2 b2_2 = { 4, d2, 0 }; +// FIXME: B2 b2_3 = { c2, a2, a2 }; + +// C++ [dcl.init.aggr]p15: +union u { int a; char* b; }; +u u1 = { 1 }; +u u2 = u1; +u u3 = 1; // expected-error{{cannot initialize 'u3' with an rvalue of type 'int'}} +u u4 = { 0, "asdf" }; // expected-warning{{excess elements in array initializer}} +u u5 = { "asdf" }; // expected-error{{incompatible type initializing 'char const [5]', expected 'int'}} diff --git a/www/cxx_status.html b/www/cxx_status.html index 6135b3f49b..d508b50db9 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -999,7 +999,14 @@ welcome!

  8.4 [dcl.fct.def]   8.5 [dcl.init] -    8.5.1 [dcl.init.aggr] + +     8.5.1[dcl.init.aggr] + + + + + No CodeGen for initializing non-aggregates or dynamic initialization. +     8.5.2 [dcl.init.string]     8.5.3 [dcl.init.ref]