/// \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
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
/// 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 {
/// would be constexpr.
bool defaultedDefaultConstructorIsConstexpr() const {
return data().DefaultedDefaultConstructorIsConstexpr &&
- (!isUnion() || hasInClassInitializer());
+ (!isUnion() || hasInClassInitializer() || !hasVariantMembers());
}
/// \brief Determine whether this class has a constexpr default constructor.
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
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),
// 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
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:
// 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.
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.
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<FieldDecl>(*I);
+ if (IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(*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
}
}
}
+
+ // C++11 [class.union]p8 (DR1460):
+ // At most one variant member of a union may have a
+ // brace-or-equal-initializer.
+ if (cast<CXXRecordDecl>(Record)->hasInClassInitializer() &&
+ Owner->isRecord())
+ checkDuplicateDefaultInit(*this, cast<CXXRecordDecl>(Owner),
+ cast<CXXRecordDecl>(Record));
}
if (!Record->isUnion() && !Owner->isRecord()) {
}
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.
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();
}
}
+ // 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<CXXRecordDecl>(Record), Loc);
+
FieldDecl *NewFD = FieldDecl::Create(Context, Record, TSSL, Loc, II, T, TInfo,
BitWidth, Mutable, InitStyle);
if (InvalidDecl)
diag::err_constexpr_local_var_non_literal_type,
isa<CXXConstructorDecl>(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<CXXConstructorDecl>(Dcl);
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)) {
// 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;
}
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
ImplicitInitializerKind IIK;
llvm::DenseMap<const void *, CXXCtorInitializer*> AllBaseFields;
SmallVector<CXXCtorInitializer*, 8> AllToInit;
+ llvm::DenseMap<TagDecl*, FieldDecl*> ActiveUnionMember;
BaseAndFieldInfo(Sema &S, CXXConstructorDecl *Ctor, bool ErrorsInInits)
: S(S), Ctor(Ctor), AnyErrorsInInits(ErrorsInInits) {
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<CXXRecordDecl>((*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<FieldDecl>(*C);
+ if (Field && isInactiveUnionMember(Field))
return true;
-
- return false;
+ }
+ return false;
+ }
+};
}
/// \brief Determine whether the given type is an incomplete or zero-lenfgth
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);
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;
if (Member->isBaseInitializer())
Info.AllBaseFields[Member->getBaseClass()->getAs<RecordType>()] = 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<FieldDecl>(*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.
// Note that this declaration has been used.
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Func)) {
+ Constructor = cast<CXXConstructorDecl>(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<CXXDestructorDecl>(Func)) {
- if (Destructor->isDefaulted() && !Destructor->isDeleted() &&
- !Destructor->isUsed(false))
+ Destructor = cast<CXXDestructorDecl>(Destructor->getFirstDecl());
+ if (Destructor->isDefaulted() && !Destructor->isDeleted())
DefineImplicitDestructor(Loc, Destructor);
if (Destructor->isVirtual())
MarkVTableUsed(Loc, Destructor->getParent());
} else if (CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(Func)) {
- if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted() &&
- MethodDecl->isOverloadedOperator() &&
+ if (MethodDecl->isOverloadedOperator() &&
MethodDecl->getOverloadedOperator() == OO_Equal) {
- if (!MethodDecl->isUsed(false)) {
+ MethodDecl = cast<CXXMethodDecl>(MethodDecl->getFirstDecl());
+ if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) {
if (MethodDecl->isCopyAssignmentOperator())
DefineImplicitCopyAssignment(Loc, MethodDecl);
else
}
} else if (isa<CXXConversionDecl>(MethodDecl) &&
MethodDecl->getParent()->isLambda()) {
- CXXConversionDecl *Conversion = cast<CXXConversionDecl>(MethodDecl);
+ CXXConversionDecl *Conversion =
+ cast<CXXConversionDecl>(MethodDecl->getFirstDecl());
if (Conversion->isLambdaToBlockPointerConversion())
DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion);
else
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++];
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);
--- /dev/null
+// 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}}
+ };
+};
--- /dev/null
+// 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<typename T> constexpr bool check() {
+ T t; // expected-note-re 2{{non-constexpr constructor '[BE]'}}
+ return true;
+ }
+ static_assert(check<A>(), "");
+ static_assert(check<B>(), ""); // expected-error {{constant}} expected-note {{in call}}
+ static_assert(check<C>(), "");
+ static_assert(check<D>(), "");
+ static_assert(check<E>(), ""); // expected-error {{constant}} expected-note {{in call}}
+ static_assert(check<F>(), "");
+#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&>(d).a = 1; // not evaluated
+ int c;
+ };
+ constexpr D() : a(0), c(0) {}
+ };
+ constexpr D d {};
+ static_assert(d.a == 0, "");
+ }
+#endif
+}
} 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}}
};
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: }