From: Richard Smith Date: Tue, 10 Dec 2013 08:25:00 +0000 (+0000) Subject: Implement DR1460: fix handling of default initializers in unions; don't allow X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6e39d2226ad4b2426434153c56de8670a60eec36;p=clang Implement DR1460: fix handling of default initializers in unions; don't allow more than one such initializer in a union, make mem-initializers override default initializers for other union members, handle anonymous unions with anonymous struct members better. Fix a couple of semi-related bugs exposed by the tests for same. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@196892 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index dc34542cb5..53c127e2cc 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -350,10 +350,15 @@ class CXXRecordDecl : public RecordDecl { /// \brief True if this class (or any subobject) has mutable fields. bool HasMutableFields : 1; + /// \brief True if this class (or any nested anonymous struct or union) + /// has variant members. + bool HasVariantMembers : 1; + /// \brief True if there no non-field members declared by the user. bool HasOnlyCMembers : 1; - /// \brief True if any field has an in-class initializer. + /// \brief True if any field has an in-class initializer, including those + /// within anonymous unions or structs. bool HasInClassInitializer : 1; /// \brief True if any field is of reference type, and does not have an @@ -1058,7 +1063,8 @@ public: bool isAggregate() const { return data().Aggregate; } /// \brief Whether this class has any in-class initializers - /// for non-static data members. + /// for non-static data members (including those in anonymous unions or + /// structs). bool hasInClassInitializer() const { return data().HasInClassInitializer; } /// \brief Whether this class or any of its subobjects has any members of @@ -1117,6 +1123,9 @@ public: /// contains a mutable field. bool hasMutableFields() const { return data().HasMutableFields; } + /// \brief Determine whether this class has any variant members. + bool hasVariantMembers() const { return data().HasVariantMembers; } + /// \brief Determine whether this class has a trivial default constructor /// (C++11 [class.ctor]p5). bool hasTrivialDefaultConstructor() const { @@ -1144,7 +1153,7 @@ public: /// would be constexpr. bool defaultedDefaultConstructorIsConstexpr() const { return data().DefaultedDefaultConstructorIsConstexpr && - (!isUnion() || hasInClassInitializer()); + (!isUnion() || hasInClassInitializer() || !hasVariantMembers()); } /// \brief Determine whether this class has a constexpr default constructor. diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 4292d35816..cb967b3fb2 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -1947,6 +1947,7 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, ToData.HasProtectedFields = FromData.HasProtectedFields; ToData.HasPublicFields = FromData.HasPublicFields; ToData.HasMutableFields = FromData.HasMutableFields; + ToData.HasVariantMembers = FromData.HasVariantMembers; ToData.HasOnlyCMembers = FromData.HasOnlyCMembers; ToData.HasInClassInitializer = FromData.HasInClassInitializer; ToData.HasUninitializedReferenceMember diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 5fd52c4243..5f7dcd5d46 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -50,7 +50,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false), Abstract(false), IsStandardLayout(true), HasNoNonEmptyBases(true), HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false), - HasMutableFields(false), HasOnlyCMembers(true), + HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true), HasInClassInitializer(false), HasUninitializedReferenceMember(false), NeedOverloadResolutionForMoveConstructor(false), NeedOverloadResolutionForMoveAssignment(false), @@ -653,7 +653,13 @@ void CXXRecordDecl::addedMember(Decl *D) { // Keep track of the presence of mutable fields. if (Field->isMutable()) data().HasMutableFields = true; - + + // C++11 [class.union]p8, DR1460: + // If X is a union, a non-static data member of X that is not an anonymous + // union is a variant member of X. + if (isUnion() && !Field->isAnonymousStructOrUnion()) + data().HasVariantMembers = true; + // C++0x [class]p9: // A POD struct is a class that is both a trivial class and a // standard-layout class, and has no non-static data members of type @@ -689,7 +695,9 @@ void CXXRecordDecl::addedMember(Decl *D) { if (!T->isLiteralType(Context) || T.isVolatileQualified()) data().HasNonLiteralTypeFieldsOrBases = true; - if (Field->hasInClassInitializer()) { + if (Field->hasInClassInitializer() || + (Field->isAnonymousStructOrUnion() && + Field->getType()->getAsCXXRecordDecl()->hasInClassInitializer())) { data().HasInClassInitializer = true; // C++11 [class]p5: @@ -806,7 +814,7 @@ void CXXRecordDecl::addedMember(Decl *D) { // Virtual bases and virtual methods make a class non-empty, but they // also make it non-standard-layout so we needn't check here. // A non-empty base class may leave the class standard-layout, but not - // if we have arrived here, and have at least on non-static data + // if we have arrived here, and have at least one non-static data // member. If IsStandardLayout remains true, then the first non-static // data member must come through here with Empty still true, and Empty // will subsequently be set to false below. @@ -859,6 +867,13 @@ void CXXRecordDecl::addedMember(Decl *D) { if (FieldRec->hasUninitializedReferenceMember() && !Field->hasInClassInitializer()) data().HasUninitializedReferenceMember = true; + + // C++11 [class.union]p8, DR1460: + // a non-static data member of an anonymous union that is a member of + // X is also a variant member of X. + if (FieldRec->hasVariantMembers() && + Field->isAnonymousStructOrUnion()) + data().HasVariantMembers = true; } } else { // Base element type of field is a non-class type. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 10137a1095..cd28c228c0 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3547,6 +3547,39 @@ StorageClassSpecToVarDeclStorageClass(const DeclSpec &DS) { llvm_unreachable("unknown storage class specifier"); } +static SourceLocation findDefaultInitializer(const CXXRecordDecl *Record) { + assert(Record->hasInClassInitializer()); + + for (DeclContext::decl_iterator I = Record->decls_begin(), + E = Record->decls_end(); + I != E; ++I) { + FieldDecl *FD = dyn_cast(*I); + if (IndirectFieldDecl *IFD = dyn_cast(*I)) + FD = IFD->getAnonField(); + if (FD && FD->hasInClassInitializer()) + return FD->getLocation(); + } + + llvm_unreachable("couldn't find in-class initializer"); +} + +static void checkDuplicateDefaultInit(Sema &S, CXXRecordDecl *Parent, + SourceLocation DefaultInitLoc) { + if (!Parent->isUnion() || !Parent->hasInClassInitializer()) + return; + + S.Diag(DefaultInitLoc, diag::err_multiple_mem_union_initialization); + S.Diag(findDefaultInitializer(Parent), diag::note_previous_initializer) << 0; +} + +static void checkDuplicateDefaultInit(Sema &S, CXXRecordDecl *Parent, + CXXRecordDecl *AnonUnion) { + if (!Parent->isUnion() || !Parent->hasInClassInitializer()) + return; + + checkDuplicateDefaultInit(S, Parent, findDefaultInitializer(AnonUnion)); +} + /// BuildAnonymousStructOrUnion - Handle the declaration of an /// anonymous structure or union. Anonymous unions are a C++ feature /// (C++ [class.union]) and a C11 feature; anonymous structures @@ -3704,6 +3737,14 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, } } } + + // C++11 [class.union]p8 (DR1460): + // At most one variant member of a union may have a + // brace-or-equal-initializer. + if (cast(Record)->hasInClassInitializer() && + Owner->isRecord()) + checkDuplicateDefaultInit(*this, cast(Owner), + cast(Record)); } if (!Record->isUnion() && !Owner->isRecord()) { @@ -3756,11 +3797,14 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, } Anon->setImplicit(); + // Mark this as an anonymous struct/union type. + Record->setAnonymousStructOrUnion(true); + // Add the anonymous struct/union object to the current // context. We'll be referencing this object when we refer to one of // its members. Owner->addDecl(Anon); - + // Inject the members of the anonymous struct/union into the owning // context and into the identifier resolver chain for name lookup // purposes. @@ -3771,14 +3815,6 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, Chain, false)) Invalid = true; - // Mark this as an anonymous struct/union type. Note that we do not - // do this until after we have already checked and injected the - // members of this anonymous struct/union type, because otherwise - // the members could be injected twice: once by DeclContext when it - // builds its lookup table, and once by - // InjectAnonymousStructOrUnionMembers. - Record->setAnonymousStructOrUnion(true); - if (Invalid) Anon->setInvalidDecl(); @@ -11516,6 +11552,12 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, } } + // C++11 [class.union]p8 (DR1460): + // At most one variant member of a union may have a + // brace-or-equal-initializer. + if (InitStyle != ICIS_NoInit) + checkDuplicateDefaultInit(*this, cast(Record), Loc); + FieldDecl *NewFD = FieldDecl::Create(Context, Record, TSSL, Loc, II, T, TInfo, BitWidth, Mutable, InitStyle); if (InvalidDecl) diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 499b23f5ef..834c189275 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -880,7 +880,8 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, diag::err_constexpr_local_var_non_literal_type, isa(Dcl))) return false; - if (!VD->hasInit() && !VD->isCXXForRangeDecl()) { + if (!VD->getType()->isDependentType() && + !VD->hasInit() && !VD->isCXXForRangeDecl()) { SemaRef.Diag(VD->getLocation(), diag::err_constexpr_local_var_no_init) << isa(Dcl); @@ -932,8 +933,13 @@ static void CheckConstexprCtorInitializer(Sema &SemaRef, if (Field->isUnnamedBitfield()) return; + // Anonymous unions with no variant members and empty anonymous structs do not + // need to be explicitly initialized. FIXME: Anonymous structs that contain no + // indirect fields don't need initializing. if (Field->isAnonymousStructOrUnion() && - Field->getType()->getAsCXXRecordDecl()->isEmpty()) + (Field->getType()->isUnionType() + ? !Field->getType()->getAsCXXRecordDecl()->hasVariantMembers() + : Field->getType()->getAsCXXRecordDecl()->isEmpty())) return; if (!Inits.count(Field)) { @@ -1116,11 +1122,12 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { // DR1359: // - every non-variant non-static data member and base class sub-object // shall be initialized; - // - if the class is a non-empty union, or for each non-empty anonymous - // union member of a non-union class, exactly one non-static data member + // DR1460: + // - if the class is a union having variant members, exactly one of them // shall be initialized; if (RD->isUnion()) { - if (Constructor->getNumCtorInitializers() == 0 && !RD->isEmpty()) { + if (Constructor->getNumCtorInitializers() == 0 && + RD->hasVariantMembers()) { Diag(Dcl->getLocation(), diag::err_constexpr_union_ctor_no_init); return false; } @@ -1139,6 +1146,10 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) { break; } } + // DR1460: + // - if the class is a union-like class, but is not a union, for each of + // its anonymous union members having variant members, exactly one of + // them shall be initialized; if (AnyAnonStructUnionMembers || Constructor->getNumCtorInitializers() != RD->getNumBases() + Fields) { // Check initialization of non-static data members. Base classes are @@ -3320,6 +3331,7 @@ struct BaseAndFieldInfo { ImplicitInitializerKind IIK; llvm::DenseMap AllBaseFields; SmallVector AllToInit; + llvm::DenseMap ActiveUnionMember; BaseAndFieldInfo(Sema &S, CXXConstructorDecl *Ctor, bool ErrorsInInits) : S(S), Ctor(Ctor), AnyErrorsInInits(ErrorsInInits) { @@ -3357,20 +3369,50 @@ struct BaseAndFieldInfo { return false; } -}; -} -/// \brief Determine whether the given indirect field declaration is somewhere -/// within an anonymous union. -static bool isWithinAnonymousUnion(IndirectFieldDecl *F) { - for (IndirectFieldDecl::chain_iterator C = F->chain_begin(), - CEnd = F->chain_end(); - C != CEnd; ++C) - if (CXXRecordDecl *Record = dyn_cast((*C)->getDeclContext())) - if (Record->isUnion()) + bool isInactiveUnionMember(FieldDecl *Field) { + RecordDecl *Record = Field->getParent(); + if (!Record->isUnion()) + return false; + + FieldDecl *Active = ActiveUnionMember.lookup(Record->getCanonicalDecl()); + if (Active) + return Active != Field->getCanonicalDecl(); + + // In an implicit copy or move constructor, ignore any in-class initializer. + if (isImplicitCopyOrMove()) + return true; + + // If there's no explicit initialization, the field is active only if it + // has an in-class initializer... + if (Field->hasInClassInitializer()) + return false; + // ... or it's an anonymous struct or union whose class has an in-class + // initializer. + if (!Field->isAnonymousStructOrUnion()) + return true; + CXXRecordDecl *FieldRD = Field->getType()->getAsCXXRecordDecl(); + return !FieldRD->hasInClassInitializer(); + } + + /// \brief Determine whether the given field is, or is within, a union member + /// that is inactive (because there was an initializer given for a different + /// member of the union, or because the union was not initialized at all). + bool isWithinInactiveUnionMember(FieldDecl *Field, + IndirectFieldDecl *Indirect) { + if (!Indirect) + return isInactiveUnionMember(Field); + + for (IndirectFieldDecl::chain_iterator C = Indirect->chain_begin(), + CEnd = Indirect->chain_end(); + C != CEnd; ++C) { + FieldDecl *Field = dyn_cast(*C); + if (Field && isInactiveUnionMember(Field)) return true; - - return false; + } + return false; + } +}; } /// \brief Determine whether the given type is an incomplete or zero-lenfgth @@ -3399,9 +3441,21 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, if (CXXCtorInitializer *Init = Info.AllBaseFields.lookup(Field)) return Info.addFieldInitializer(Init); - // C++11 [class.base.init]p8: if the entity is a non-static data member that - // has a brace-or-equal-initializer, the entity is initialized as specified - // in [dcl.init]. + // C++11 [class.base.init]p8: + // if the entity is a non-static data member that has a + // brace-or-equal-initializer and either + // -- the constructor's class is a union and no other variant member of that + // union is designated by a mem-initializer-id or + // -- the constructor's class is not a union, and, if the entity is a member + // of an anonymous union, no other member of that union is designated by + // a mem-initializer-id, + // the entity is initialized as specified in [dcl.init]. + // + // We also apply the same rules to handle anonymous structs within anonymous + // unions. + if (Info.isWithinInactiveUnionMember(Field, Indirect)) + return false; + if (Field->hasInClassInitializer() && !Info.isImplicitCopyOrMove()) { Expr *DIE = CXXDefaultInitExpr::Create(SemaRef.Context, Info.Ctor->getLocation(), Field); @@ -3419,12 +3473,6 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, return Info.addFieldInitializer(Init); } - // Don't build an implicit initializer for union members if none was - // explicitly specified. - if (Field->getParent()->isUnion() || - (Indirect && isWithinAnonymousUnion(Indirect))) - return false; - // Don't initialize incomplete or zero-length arrays. if (isIncompleteOrZeroLengthArrayType(SemaRef.Context, Field->getType())) return false; @@ -3502,8 +3550,24 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors, if (Member->isBaseInitializer()) Info.AllBaseFields[Member->getBaseClass()->getAs()] = Member; - else + else { Info.AllBaseFields[Member->getAnyMember()] = Member; + + if (IndirectFieldDecl *F = Member->getIndirectMember()) { + for (IndirectFieldDecl::chain_iterator C = F->chain_begin(), + CEnd = F->chain_end(); + C != CEnd; ++C) { + FieldDecl *FD = dyn_cast(*C); + if (FD && FD->getParent()->isUnion()) + Info.ActiveUnionMember.insert(std::make_pair( + FD->getParent()->getCanonicalDecl(), FD->getCanonicalDecl())); + } + } else if (FieldDecl *FD = Member->getMember()) { + if (FD->getParent()->isUnion()) + Info.ActiveUnionMember.insert(std::make_pair( + FD->getParent()->getCanonicalDecl(), FD->getCanonicalDecl())); + } + } } // Keep track of the direct virtual bases. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 8efdffaf09..fc3eeb8630 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -11184,37 +11184,34 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { // Note that this declaration has been used. if (CXXConstructorDecl *Constructor = dyn_cast(Func)) { + Constructor = cast(Constructor->getFirstDecl()); if (Constructor->isDefaulted() && !Constructor->isDeleted()) { if (Constructor->isDefaultConstructor()) { if (Constructor->isTrivial()) return; - if (!Constructor->isUsed(false)) - DefineImplicitDefaultConstructor(Loc, Constructor); + DefineImplicitDefaultConstructor(Loc, Constructor); } else if (Constructor->isCopyConstructor()) { - if (!Constructor->isUsed(false)) - DefineImplicitCopyConstructor(Loc, Constructor); + DefineImplicitCopyConstructor(Loc, Constructor); } else if (Constructor->isMoveConstructor()) { - if (!Constructor->isUsed(false)) - DefineImplicitMoveConstructor(Loc, Constructor); + DefineImplicitMoveConstructor(Loc, Constructor); } } else if (Constructor->getInheritedConstructor()) { - if (!Constructor->isUsed(false)) - DefineInheritingConstructor(Loc, Constructor); + DefineInheritingConstructor(Loc, Constructor); } MarkVTableUsed(Loc, Constructor->getParent()); } else if (CXXDestructorDecl *Destructor = dyn_cast(Func)) { - if (Destructor->isDefaulted() && !Destructor->isDeleted() && - !Destructor->isUsed(false)) + Destructor = cast(Destructor->getFirstDecl()); + if (Destructor->isDefaulted() && !Destructor->isDeleted()) DefineImplicitDestructor(Loc, Destructor); if (Destructor->isVirtual()) MarkVTableUsed(Loc, Destructor->getParent()); } else if (CXXMethodDecl *MethodDecl = dyn_cast(Func)) { - if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted() && - MethodDecl->isOverloadedOperator() && + if (MethodDecl->isOverloadedOperator() && MethodDecl->getOverloadedOperator() == OO_Equal) { - if (!MethodDecl->isUsed(false)) { + MethodDecl = cast(MethodDecl->getFirstDecl()); + if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) { if (MethodDecl->isCopyAssignmentOperator()) DefineImplicitCopyAssignment(Loc, MethodDecl); else @@ -11222,7 +11219,8 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { } } else if (isa(MethodDecl) && MethodDecl->getParent()->isLambda()) { - CXXConversionDecl *Conversion = cast(MethodDecl); + CXXConversionDecl *Conversion = + cast(MethodDecl->getFirstDecl()); if (Conversion->isLambdaToBlockPointerConversion()) DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion); else diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 4399fec553..70fb062b2c 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1187,6 +1187,7 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.HasProtectedFields = Record[Idx++]; Data.HasPublicFields = Record[Idx++]; Data.HasMutableFields = Record[Idx++]; + Data.HasVariantMembers = Record[Idx++]; Data.HasOnlyCMembers = Record[Idx++]; Data.HasInClassInitializer = Record[Idx++]; Data.HasUninitializedReferenceMember = Record[Idx++]; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index d8f170fd51..56eddb77e0 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5092,6 +5092,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec Record.push_back(Data.HasProtectedFields); Record.push_back(Data.HasPublicFields); Record.push_back(Data.HasMutableFields); + Record.push_back(Data.HasVariantMembers); Record.push_back(Data.HasOnlyCMembers); Record.push_back(Data.HasInClassInitializer); Record.push_back(Data.HasUninitializedReferenceMember); diff --git a/test/CXX/class/class.union/p8.cpp b/test/CXX/class/class.union/p8.cpp new file mode 100644 index 0000000000..cc54ba4066 --- /dev/null +++ b/test/CXX/class/class.union/p8.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +union U { + int x = 0; // expected-note {{previous initialization is here}} + union {}; + union { + int z; + int y = 1; // expected-error {{initializing multiple members of union}} + }; +}; diff --git a/test/CXX/drs/dr14xx.cpp b/test/CXX/drs/dr14xx.cpp new file mode 100644 index 0000000000..48487b5235 --- /dev/null +++ b/test/CXX/drs/dr14xx.cpp @@ -0,0 +1,196 @@ +// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++1y %s -verify -fexceptions -fcxx-exceptions -pedantic-errors + +#if __cplusplus < 201103L +// expected-no-diagnostics +#endif + +namespace dr1460 { // dr1460: 3.5 +#if __cplusplus >= 201103L + namespace DRExample { + union A { + union {}; + union {}; + constexpr A() {} + }; + constexpr A a = A(); + + union B { + union {}; + union {}; + constexpr B() = default; + }; + constexpr B b = B(); + + union C { + union {}; + union {}; + }; + constexpr C c = C(); +#if __cplusplus > 201103L + constexpr void f() { C c; } + static_assert((f(), true), ""); +#endif + } + + union A {}; + union B { int n; }; // expected-note +{{here}} + union C { int n = 0; }; + struct D { union {}; }; + struct E { union { int n; }; }; // expected-note +{{here}} + struct F { union { int n = 0; }; }; + + struct X { + friend constexpr A::A() noexcept; + friend constexpr B::B() noexcept; // expected-error {{follows non-constexpr declaration}} + friend constexpr C::C() noexcept; + friend constexpr D::D() noexcept; + friend constexpr E::E() noexcept; // expected-error {{follows non-constexpr declaration}} + friend constexpr F::F() noexcept; + }; + + // These are OK, because value-initialization doesn't actually invoke the + // constructor. + constexpr A a = A(); + constexpr B b = B(); + constexpr C c = C(); + constexpr D d = D(); + constexpr E e = E(); + constexpr F f = F(); + + namespace Defaulted { + union A { constexpr A() = default; }; + union B { int n; constexpr B() = default; }; // expected-error {{not constexpr}} + union C { int n = 0; constexpr C() = default; }; + struct D { union {}; constexpr D() = default; }; + struct E { union { int n; }; constexpr E() = default; }; // expected-error {{not constexpr}} + struct F { union { int n = 0; }; constexpr F() = default; }; + + struct G { union { int n = 0; }; union { int m; }; constexpr G() = default; }; // expected-error {{not constexpr}} + struct H { + union { + int n = 0; + }; + union { // expected-note 2{{member not initialized}} + int m; + }; + constexpr H() {} // expected-error {{must initialize all members}} + constexpr H(bool) : m(1) {} + constexpr H(char) : n(1) {} // expected-error {{must initialize all members}} + constexpr H(double) : m(1), n(1) {} + }; + } + +#if __cplusplus > 201103L + template constexpr bool check() { + T t; // expected-note-re 2{{non-constexpr constructor '[BE]'}} + return true; + } + static_assert(check(), ""); + static_assert(check(), ""); // expected-error {{constant}} expected-note {{in call}} + static_assert(check(), ""); + static_assert(check(), ""); + static_assert(check(), ""); // expected-error {{constant}} expected-note {{in call}} + static_assert(check(), ""); +#endif + + union G { + int a = 0; // expected-note {{previous initialization is here}} + int b = 0; // expected-error {{initializing multiple members of union}} + }; + union H { + union { + int a = 0; // expected-note {{previous initialization is here}} + }; + union { + int b = 0; // expected-error {{initializing multiple members of union}} + }; + }; + struct I { + union { + int a = 0; // expected-note {{previous initialization is here}} + int b = 0; // expected-error {{initializing multiple members of union}} + }; + }; + struct J { + union { int a = 0; }; + union { int b = 0; }; + }; + + namespace Overriding { + struct A { + int a = 1, b, c = 3; + constexpr A() : b(2) {} + }; + static_assert(A().a == 1 && A().b == 2 && A().c == 3, ""); + + union B { + int a, b = 2, c; + constexpr B() : a(1) {} + constexpr B(char) : b(4) {} + constexpr B(int) : c(3) {} + constexpr B(const char*) {} + }; + static_assert(B().a == 1, ""); + static_assert(B().b == 2, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(B('x').a == 0, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(B('x').b == 4, ""); + static_assert(B(123).b == 2, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(B(123).c == 3, ""); + static_assert(B("").a == 1, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(B("").b == 2, ""); + static_assert(B("").c == 3, ""); // expected-error {{constant}} expected-note {{read of}} + + struct C { + union { int a, b = 2, c; }; + union { int d, e = 5, f; }; + constexpr C() : a(1) {} + constexpr C(char) : c(3) {} + constexpr C(int) : d(4) {} + constexpr C(float) : f(6) {} + constexpr C(const char*) {} + }; + + static_assert(C().a == 1, ""); + static_assert(C().b == 2, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C().d == 4, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C().e == 5, ""); + + static_assert(C('x').b == 2, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C('x').c == 3, ""); + static_assert(C('x').d == 4, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C('x').e == 5, ""); + + static_assert(C(1).b == 2, ""); + static_assert(C(1).c == 3, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C(1).d == 4, ""); + static_assert(C(1).e == 5, ""); // expected-error {{constant}} expected-note {{read of}} + + static_assert(C(1.f).b == 2, ""); + static_assert(C(1.f).c == 3, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C(1.f).e == 5, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C(1.f).f == 6, ""); + + static_assert(C("").a == 1, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C("").b == 2, ""); + static_assert(C("").c == 3, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C("").d == 4, ""); // expected-error {{constant}} expected-note {{read of}} + static_assert(C("").e == 5, ""); + static_assert(C("").f == 6, ""); // expected-error {{constant}} expected-note {{read of}} + + struct D; + extern const D d; + struct D { + int a; + union { + int b = const_cast(d).a = 1; // not evaluated + int c; + }; + constexpr D() : a(0), c(0) {} + }; + constexpr D d {}; + static_assert(d.a == 0, ""); + } +#endif +} diff --git a/test/CXX/special/class.init/class.base.init/p8-0x.cpp b/test/CXX/special/class.init/class.base.init/p8-0x.cpp index a108533bed..45755caa8e 100644 --- a/test/CXX/special/class.init/class.base.init/p8-0x.cpp +++ b/test/CXX/special/class.init/class.base.init/p8-0x.cpp @@ -16,16 +16,13 @@ struct S { } s(0); union U { - int a = 0; // desired-note 5 {{previous initialization is here}} - char b = 'x'; + int a = 0; // expected-note {{previous initialization}} + char b = 'x'; // expected-error {{initializing multiple members of union}} - // FIXME: these should all be rejected - U() {} // desired-error {{initializing multiple members of union}} - U(int) : a(1) {} // desired-error {{initializing multiple members of union}} - U(char) : b('y') {} // desired-error {{initializing multiple members of union}} - // this expected note should be removed & the note should appear on the - // declaration of 'a' when this set of cases is handled correctly. - U(double) : a(1), // expected-note{{previous initialization is here}} desired-error {{initializing multiple members of union}} + U() {} + U(int) : a(1) {} + U(char) : b('y') {} + U(double) : a(1), // expected-note{{previous initialization is here}} b('y') {} // expected-error{{initializing multiple members of union}} }; diff --git a/test/CodeGenCXX/member-init-anon-union.cpp b/test/CodeGenCXX/member-init-anon-union.cpp index bfe1667c8c..80db104d78 100644 --- a/test/CodeGenCXX/member-init-anon-union.cpp +++ b/test/CodeGenCXX/member-init-anon-union.cpp @@ -21,12 +21,42 @@ int g() { int a; int b = 81; }; - // CHECK: define {{.*}}_Z1gv + // CHECK-LABEL: define {{.*}}_Z1gv // CHECK-NOT: } // CHECK: call {{.*}}@"[[CONSTRUCT_LOCAL:.*]]C1Ev" return b; } +struct A { + A(); +}; +union B { + int k; + struct { + A x; + int y = 123; + }; + B() {} + B(int n) : k(n) {} +}; + +B b1; +B b2(0); + + +// CHECK-LABEL: define {{.*}} @_ZN1BC2Ei( +// CHECK-NOT: call void @_ZN1AC1Ev( +// CHECK-NOT: store i32 123, +// CHECK: store i32 % +// CHECK-NOT: call void @_ZN1AC1Ev( +// CHECK-NOT: store i32 123, +// CHECK: } + +// CHECK-LABEL: define {{.*}} @_ZN1BC2Ev( +// CHECK: call void @_ZN1AC1Ev( +// CHECK: store i32 123, +// CHECK: } + // CHECK: define {{.*}}@"[[CONSTRUCT_LOCAL]]C2Ev" // CHECK-NOT: }