"This patch implements the restrictions on union members detailed in

[class.union]p1", from John McCall!

llvm-svn: 76766
This commit is contained in:
Douglas Gregor 2009-07-22 18:25:24 +00:00
parent f03c9bec63
commit 8a27391190
8 changed files with 443 additions and 38 deletions

View File

@ -673,6 +673,7 @@ private:
bool HasInheritedPrototype : 1;
bool HasWrittenPrototype : 1;
bool IsDeleted : 1;
bool IsTrivial : 1; // sunk from CXXMethodDecl
// Move to DeclGroup when it is implemented.
SourceLocation TypeSpecStartLoc;
@ -712,8 +713,8 @@ protected:
ParamInfo(0), Body(),
SClass(S), IsInline(isInline), C99InlineDefinition(false),
IsVirtualAsWritten(false), IsPure(false), HasInheritedPrototype(false),
HasWrittenPrototype(true), IsDeleted(false), TypeSpecStartLoc(TSSL),
EndRangeLoc(L), TemplateOrSpecialization() {}
HasWrittenPrototype(true), IsDeleted(false), IsTrivial(false),
TypeSpecStartLoc(TSSL), EndRangeLoc(L), TemplateOrSpecialization() {}
virtual ~FunctionDecl() {}
virtual void Destroy(ASTContext& C);
@ -782,6 +783,13 @@ public:
bool isPure() const { return IsPure; }
void setPure(bool P = true) { IsPure = P; }
/// Whether this function is "trivial" in some specialized C++ senses.
/// Can only be true for default constructors, copy constructors,
/// copy assignment operators, and destructors. Not meaningful until
/// the class has been fully built by Sema.
bool isTrivial() const { return IsTrivial; }
void setTrivial(bool IT) { IsTrivial = IT; }
/// \brief Whether this function has a prototype, either because one
/// was explicitly written or because it was "inherited" by merging
/// a declaration without a prototype with a declaration that has a

View File

@ -297,12 +297,51 @@ class CXXRecordDecl : public RecordDecl {
/// pure virtual function, (that can come from a base class).
bool Abstract : 1;
/// HasTrivialConstructor - True when this class has a trivial constructor
/// HasTrivialConstructor - True when this class has a trivial constructor.
///
/// C++ [class.ctor]p5. A constructor is trivial if it is an
/// implicitly-declared default constructor and if:
/// * its class has no virtual functions and no virtual base classes, and
/// * all the direct base classes of its class have trivial constructors, and
/// * for all the nonstatic data members of its class that are of class type
/// (or array thereof), each such class has a trivial constructor.
bool HasTrivialConstructor : 1;
/// HasTrivialDestructor - True when this class has a trivial destructor
bool HasTrivialDestructor : 1;
/// HasTrivialCopyConstructor - True when this class has a trivial copy
/// constructor.
///
/// C++ [class.copy]p6. A copy constructor for class X is trivial
/// if it is implicitly declared and if
/// * class X has no virtual functions and no virtual base classes, and
/// * each direct base class of X has a trivial copy constructor, and
/// * for all the nonstatic data members of X that are of class type (or
/// array thereof), each such class type has a trivial copy constructor;
/// otherwise the copy constructor is non-trivial.
bool HasTrivialCopyConstructor : 1;
/// HasTrivialCopyAssignment - True when this class has a trivial copy
/// assignment operator.
///
/// C++ [class.copy]p11. A copy assignment operator for class X is
/// trivial if it is implicitly declared and if
/// * class X has no virtual functions and no virtual base classes, and
/// * each direct base class of X has a trivial copy assignment operator, and
/// * for all the nonstatic data members of X that are of class type (or
/// array thereof), each such class type has a trivial copy assignment
/// operator;
/// otherwise the copy assignment operator is non-trivial.
bool HasTrivialCopyAssignment : 1;
/// HasTrivialDestructor - True when this class has a trivial destructor.
///
/// C++ [class.dtor]p3. A destructor is trivial if it is an
/// implicitly-declared destructor and if:
/// * all of the direct base classes of its class have trivial destructors
/// and
/// * for all of the non-static data members of its class that are of class
/// type (or array thereof), each such class has a trivial destructor.
bool HasTrivialDestructor : 1;
/// Bases - Base classes of this class.
/// FIXME: This is wasted space for a union.
CXXBaseSpecifier *Bases;
@ -342,11 +381,11 @@ protected:
public:
/// base_class_iterator - Iterator that traverses the base classes
/// of a clas.
/// of a class.
typedef CXXBaseSpecifier* base_class_iterator;
/// base_class_const_iterator - Iterator that traverses the base
/// classes of a clas.
/// classes of a class.
typedef const CXXBaseSpecifier* base_class_const_iterator;
static CXXRecordDecl *Create(ASTContext &C, TagKind TK, DeclContext *DC,
@ -379,6 +418,28 @@ public:
base_class_iterator vbases_end() { return VBases + NumVBases; }
base_class_const_iterator vbases_end() const { return VBases + NumVBases; }
/// Iterator access to method members. The method iterator visits
/// all method members of the class, including non-instance methods,
/// special methods, etc.
typedef specific_decl_iterator<CXXMethodDecl> method_iterator;
method_iterator method_begin() const {
return method_iterator(decls_begin());
}
method_iterator method_end() const {
return method_iterator(decls_end());
}
/// Iterator access to constructor members.
typedef specific_decl_iterator<CXXConstructorDecl> ctor_iterator;
ctor_iterator ctor_begin() const {
return ctor_iterator(decls_begin());
}
ctor_iterator ctor_end() const {
return ctor_iterator(decls_end());
}
/// hasConstCopyConstructor - Determines whether this class has a
/// copy constructor that accepts a const-qualified argument.
bool hasConstCopyConstructor(ASTContext &Context) const;
@ -487,6 +548,22 @@ public:
// (C++ [class.ctor]p5)
void setHasTrivialConstructor(bool TC) { HasTrivialConstructor = TC; }
// hasTrivialCopyConstructor - Whether this class has a trivial copy
// constructor (C++ [class.copy]p6)
bool hasTrivialCopyConstructor() const { return HasTrivialCopyConstructor; }
// setHasTrivialCopyConstructor - Set whether this class has a trivial
// copy constructor (C++ [class.copy]p6)
void setHasTrivialCopyConstructor(bool TC) { HasTrivialCopyConstructor = TC; }
// hasTrivialCopyAssignment - Whether this class has a trivial copy
// assignment operator (C++ [class.copy]p11)
bool hasTrivialCopyAssignment() const { return HasTrivialCopyAssignment; }
// setHasTrivialCopyAssignment - Set whether this class has a
// trivial copy assignment operator (C++ [class.copy]p11)
void setHasTrivialCopyAssignment(bool TC) { HasTrivialCopyAssignment = TC; }
// hasTrivialDestructor - Whether this class has a trivial destructor
// (C++ [class.dtor]p3)
bool hasTrivialDestructor() const { return HasTrivialDestructor; }

View File

@ -339,6 +339,18 @@ def err_implicit_object_parameter_init : Error<
"cannot initialize object parameter of type %0 with an expression "
"of type %1">;
def err_illegal_union_member : Error<
"union member %0 has a non-trivial %select{constructor|"
"copy constructor|copy assignment operator|destructor}1">;
def note_nontrivial_has_virtual : Note<
"because type %0 has a virtual %select{member function|base class}1">;
def note_nontrivial_has_nontrivial : Note<
"because type %0 has a %select{member|base class}1 with a non-trivial "
"%select{constructor|copy constructor|copy assignment operator|destructor}2">;
def note_nontrivial_user_defined : Note<
"because type %0 has a user-declared %select{constructor|copy constructor|"
"copy assignment operator|destructor}1">;
def err_different_return_type_for_overriding_virtual_function : Error<
"virtual function %0 has a different return type (%1) than the "
"function it overrides (which has return type %2)">;

View File

@ -30,7 +30,8 @@ CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC,
UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false),
UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false),
Aggregate(true), PlainOldData(true), Polymorphic(false), Abstract(false),
HasTrivialConstructor(true), HasTrivialDestructor(true),
HasTrivialConstructor(true), HasTrivialCopyConstructor(true),
HasTrivialCopyAssignment(true), HasTrivialDestructor(true),
Bases(0), NumBases(0), VBases(0), NumVBases(0),
Conversions(DC, DeclarationName()),
TemplateOrInstantiation() { }
@ -123,7 +124,7 @@ CXXRecordDecl::setBases(ASTContext &C,
}
if (vbaseCount > 0) {
// build AST for inhireted, direct or indirect, virtual bases.
this->VBases = new(C) CXXBaseSpecifier [vbaseCount];
this->VBases = new (C) CXXBaseSpecifier [vbaseCount];
this->NumVBases = vbaseCount;
for (int i = 0; i < vbaseCount; i++) {
QualType QT = UniqueVbases[i]->getType();
@ -225,12 +226,19 @@ CXXRecordDecl::addedConstructor(ASTContext &Context,
// C++ [class.ctor]p5:
// A constructor is trivial if it is an implicitly-declared default
// constructor.
// FIXME: C++0x: don't do this for "= default" default constructors.
HasTrivialConstructor = false;
// Note when we have a user-declared copy constructor, which will
// suppress the implicit declaration of a copy constructor.
if (ConDecl->isCopyConstructor(Context))
if (ConDecl->isCopyConstructor(Context)) {
UserDeclaredCopyConstructor = true;
// C++ [class.copy]p6:
// A copy constructor is trivial if it is implicitly declared.
// FIXME: C++0x: don't do this for "= default" copy constructors.
HasTrivialCopyConstructor = false;
}
}
void CXXRecordDecl::addedAssignmentOperator(ASTContext &Context,
@ -254,6 +262,11 @@ void CXXRecordDecl::addedAssignmentOperator(ASTContext &Context,
// Suppress the implicit declaration of a copy constructor.
UserDeclaredCopyAssignment = true;
// C++ [class.copy]p11:
// A copy assignment operator is trivial if it is implicitly declared.
// FIXME: C++0x: don't do this for "= default" copy operators.
HasTrivialCopyAssignment = false;
// C++ [class]p4:
// A POD-struct is an aggregate class that [...] has no user-defined copy
// assignment operator [...].

View File

@ -541,6 +541,14 @@ public:
SourceLocation TSSL,
AccessSpecifier AS, NamedDecl *PrevDecl,
Declarator *D = 0);
enum CXXSpecialMember {
CXXDefaultConstructor = 0,
CXXCopyConstructor = 1,
CXXCopyAssignment = 2,
CXXDestructor = 3
};
void DiagnoseNontrivial(const RecordType* Record, CXXSpecialMember mem);
virtual DeclPtrTy ActOnIvar(Scope *S, SourceLocation DeclStart,
DeclPtrTy IntfDecl,

View File

@ -2265,6 +2265,8 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
CurClass->setPOD(false);
CurClass->setPolymorphic(true);
CurClass->setHasTrivialConstructor(false);
CurClass->setHasTrivialCopyConstructor(false);
CurClass->setHasTrivialCopyAssignment(false);
}
}
@ -2505,6 +2507,7 @@ void Sema::CheckFunctionDeclaration(FunctionDecl *NewFD, NamedDecl *&PrevDecl,
// C++ [class.dtor]p3: A destructor is trivial if it is an implicitly-
// declared destructor.
// FIXME: C++0x: don't do this for "= default" destructors
Record->setHasTrivialDestructor(false);
} else if (CXXConversionDecl *Conversion
= dyn_cast<CXXConversionDecl>(NewFD))
@ -4085,6 +4088,57 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
NewFD->setInvalidDecl();
}
if (getLangOptions().CPlusPlus) {
QualType EltTy = T;
while (const ArrayType *AT = Context.getAsArrayType(EltTy))
EltTy = AT->getElementType();
if (const RecordType *RT = EltTy->getAsRecordType()) {
CXXRecordDecl* RDecl = cast<CXXRecordDecl>(RT->getDecl());
if (!RDecl->hasTrivialConstructor())
cast<CXXRecordDecl>(Record)->setHasTrivialConstructor(false);
if (!RDecl->hasTrivialCopyConstructor())
cast<CXXRecordDecl>(Record)->setHasTrivialCopyConstructor(false);
if (!RDecl->hasTrivialCopyAssignment())
cast<CXXRecordDecl>(Record)->setHasTrivialCopyAssignment(false);
if (!RDecl->hasTrivialDestructor())
cast<CXXRecordDecl>(Record)->setHasTrivialDestructor(false);
// C++ 9.5p1: An object of a class with a non-trivial
// constructor, a non-trivial copy constructor, a non-trivial
// destructor, or a non-trivial copy assignment operator
// cannot be a member of a union, nor can an array of such
// objects.
// TODO: C++0x alters this restriction significantly.
if (Record->isUnion()) {
// We check for copy constructors before constructors
// because otherwise we'll never get complaints about
// copy constructors.
const CXXSpecialMember invalid = (CXXSpecialMember) -1;
CXXSpecialMember member;
if (!RDecl->hasTrivialCopyConstructor())
member = CXXCopyConstructor;
else if (!RDecl->hasTrivialConstructor())
member = CXXDefaultConstructor;
else if (!RDecl->hasTrivialCopyAssignment())
member = CXXCopyAssignment;
else if (!RDecl->hasTrivialDestructor())
member = CXXDestructor;
else
member = invalid;
if (member != invalid) {
Diag(Loc, diag::err_illegal_union_member) << Name << member;
DiagnoseNontrivial(RT, member);
NewFD->setInvalidDecl();
}
}
}
}
if (getLangOptions().CPlusPlus && !T->isPODType())
cast<CXXRecordDecl>(Record)->setPOD(false);
@ -4113,6 +4167,133 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T,
return NewFD;
}
/// DiagnoseNontrivial - Given that a class has a non-trivial
/// special member, figure out why.
void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) {
QualType QT(T, 0U);
CXXRecordDecl* RD = cast<CXXRecordDecl>(T->getDecl());
// Check whether the member was user-declared.
switch (member) {
case CXXDefaultConstructor:
if (RD->hasUserDeclaredConstructor()) {
typedef CXXRecordDecl::ctor_iterator ctor_iter;
for (ctor_iter ci = RD->ctor_begin(), ce = RD->ctor_end(); ci != ce; ++ci)
if (!ci->isImplicitlyDefined(Context)) {
SourceLocation CtorLoc = ci->getLocation();
Diag(CtorLoc, diag::note_nontrivial_user_defined) << QT << member;
return;
}
assert(0 && "found no user-declared constructors");
return;
}
break;
case CXXCopyConstructor:
if (RD->hasUserDeclaredCopyConstructor()) {
SourceLocation CtorLoc =
RD->getCopyConstructor(Context, 0)->getLocation();
Diag(CtorLoc, diag::note_nontrivial_user_defined) << QT << member;
return;
}
break;
case CXXCopyAssignment:
if (RD->hasUserDeclaredCopyAssignment()) {
// FIXME: this should use the location of the copy
// assignment, not the type.
SourceLocation TyLoc = RD->getSourceRange().getBegin();
Diag(TyLoc, diag::note_nontrivial_user_defined) << QT << member;
return;
}
break;
case CXXDestructor:
if (RD->hasUserDeclaredDestructor()) {
SourceLocation DtorLoc = RD->getDestructor(Context)->getLocation();
Diag(DtorLoc, diag::note_nontrivial_user_defined) << QT << member;
return;
}
break;
}
typedef CXXRecordDecl::base_class_iterator base_iter;
// Virtual bases and members inhibit trivial copying/construction,
// but not trivial destruction.
if (member != CXXDestructor) {
// Check for virtual bases. vbases includes indirect virtual bases,
// so we just iterate through the direct bases.
for (base_iter bi = RD->bases_begin(), be = RD->bases_end(); bi != be; ++bi)
if (bi->isVirtual()) {
SourceLocation BaseLoc = bi->getSourceRange().getBegin();
Diag(BaseLoc, diag::note_nontrivial_has_virtual) << QT << 1;
return;
}
// Check for virtual methods.
typedef CXXRecordDecl::method_iterator meth_iter;
for (meth_iter mi = RD->method_begin(), me = RD->method_end(); mi != me;
++mi) {
if (mi->isVirtual()) {
SourceLocation MLoc = mi->getSourceRange().getBegin();
Diag(MLoc, diag::note_nontrivial_has_virtual) << QT << 0;
return;
}
}
}
bool (CXXRecordDecl::*hasTrivial)() const;
switch (member) {
case CXXDefaultConstructor:
hasTrivial = &CXXRecordDecl::hasTrivialConstructor; break;
case CXXCopyConstructor:
hasTrivial = &CXXRecordDecl::hasTrivialCopyConstructor; break;
case CXXCopyAssignment:
hasTrivial = &CXXRecordDecl::hasTrivialCopyAssignment; break;
case CXXDestructor:
hasTrivial = &CXXRecordDecl::hasTrivialDestructor; break;
default:
assert(0 && "unexpected special member"); return;
}
// Check for nontrivial bases (and recurse).
for (base_iter bi = RD->bases_begin(), be = RD->bases_end(); bi != be; ++bi) {
const RecordType *BaseRT = bi->getType()->getAsRecordType();
assert(BaseRT);
CXXRecordDecl *BaseRecTy = cast<CXXRecordDecl>(BaseRT->getDecl());
if (!(BaseRecTy->*hasTrivial)()) {
SourceLocation BaseLoc = bi->getSourceRange().getBegin();
Diag(BaseLoc, diag::note_nontrivial_has_nontrivial) << QT << 1 << member;
DiagnoseNontrivial(BaseRT, member);
return;
}
}
// Check for nontrivial members (and recurse).
typedef RecordDecl::field_iterator field_iter;
for (field_iter fi = RD->field_begin(), fe = RD->field_end(); fi != fe;
++fi) {
QualType EltTy = (*fi)->getType();
while (const ArrayType *AT = Context.getAsArrayType(EltTy))
EltTy = AT->getElementType();
if (const RecordType *EltRT = EltTy->getAsRecordType()) {
CXXRecordDecl* EltRD = cast<CXXRecordDecl>(EltRT->getDecl());
if (!(EltRD->*hasTrivial)()) {
SourceLocation FLoc = (*fi)->getLocation();
Diag(FLoc, diag::note_nontrivial_has_nontrivial) << QT << 0 << member;
DiagnoseNontrivial(EltRT, member);
return;
}
}
}
assert(0 && "found no explanation for non-trivial member");
}
/// TranslateIvarVisibility - Translate visibility from a token ID to an
/// AST enum value.
static ObjCIvarDecl::AccessControl

View File

@ -400,19 +400,40 @@ Sema::CheckBaseSpecifier(CXXRecordDecl *Class,
// C++ [class.ctor]p5:
// A constructor is trivial if its class has no virtual base classes.
Class->setHasTrivialConstructor(false);
// C++ [class.copy]p6:
// A copy constructor is trivial if its class has no virtual base classes.
Class->setHasTrivialCopyConstructor(false);
// C++ [class.copy]p11:
// A copy assignment operator is trivial if its class has no virtual
// base classes.
Class->setHasTrivialCopyAssignment(false);
} else {
// C++ [class.ctor]p5:
// A constructor is trivial if all the direct base classes of its
// class have trivial constructors.
Class->setHasTrivialConstructor(cast<CXXRecordDecl>(BaseDecl)->
hasTrivialConstructor());
if (!cast<CXXRecordDecl>(BaseDecl)->hasTrivialConstructor())
Class->setHasTrivialConstructor(false);
// C++ [class.copy]p6:
// A copy constructor is trivial if all the direct base classes of its
// class have trivial copy constructors.
if (!cast<CXXRecordDecl>(BaseDecl)->hasTrivialCopyConstructor())
Class->setHasTrivialCopyConstructor(false);
// C++ [class.copy]p11:
// A copy assignment operator is trivial if all the direct base classes
// of its class have trivial copy assignment operators.
if (!cast<CXXRecordDecl>(BaseDecl)->hasTrivialCopyAssignment())
Class->setHasTrivialCopyAssignment(false);
}
// C++ [class.ctor]p3:
// A destructor is trivial if all the direct base classes of its class
// have trivial destructors.
Class->setHasTrivialDestructor(cast<CXXRecordDecl>(BaseDecl)->
hasTrivialDestructor());
if (!cast<CXXRecordDecl>(BaseDecl)->hasTrivialDestructor())
Class->setHasTrivialDestructor(false);
// Create the base specifier.
// FIXME: Allocate via ASTContext?
@ -1154,30 +1175,6 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
if (RD->isAbstract())
AbstractClassUsageDiagnoser(*this, RD);
if (RD->hasTrivialConstructor() || RD->hasTrivialDestructor()) {
for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
i != e; ++i) {
// All the nonstatic data members must have trivial constructors.
QualType FTy = i->getType();
while (const ArrayType *AT = Context.getAsArrayType(FTy))
FTy = AT->getElementType();
if (const RecordType *RT = FTy->getAsRecordType()) {
CXXRecordDecl *FieldRD = cast<CXXRecordDecl>(RT->getDecl());
if (!FieldRD->hasTrivialConstructor())
RD->setHasTrivialConstructor(false);
if (!FieldRD->hasTrivialDestructor())
RD->setHasTrivialDestructor(false);
// If RD has neither a trivial constructor nor a trivial destructor
// we don't need to continue checking.
if (!RD->hasTrivialConstructor() && !RD->hasTrivialDestructor())
break;
}
}
}
if (!RD->isDependentType())
AddImplicitlyDeclaredMembersToClass(RD);
}
@ -1213,6 +1210,7 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
/*isImplicitlyDeclared=*/true);
DefaultCon->setAccess(AS_public);
DefaultCon->setImplicit();
DefaultCon->setTrivial(ClassDecl->hasTrivialConstructor());
ClassDecl->addDecl(DefaultCon);
}
@ -1283,6 +1281,7 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
/*isImplicitlyDeclared=*/true);
CopyConstructor->setAccess(AS_public);
CopyConstructor->setImplicit();
CopyConstructor->setTrivial(ClassDecl->hasTrivialCopyConstructor());
// Add the parameter to the constructor.
ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyConstructor,
@ -1359,6 +1358,7 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
/*isStatic=*/false, /*isInline=*/true);
CopyAssignment->setAccess(AS_public);
CopyAssignment->setImplicit();
CopyAssignment->setTrivial(ClassDecl->hasTrivialCopyAssignment());
// Add the parameter to the operator.
ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyAssignment,
@ -1388,6 +1388,7 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) {
/*isImplicitlyDeclared=*/true);
Destructor->setAccess(AS_public);
Destructor->setImplicit();
Destructor->setTrivial(ClassDecl->hasTrivialDestructor());
ClassDecl->addDecl(Destructor);
}
}

View File

@ -0,0 +1,105 @@
// RUN: clang-cc -fsyntax-only -verify %s
void abort();
class Okay {
int a_;
};
class Virtual {
virtual void foo() { abort(); } // expected-note 3 {{because type 'class Virtual' has a virtual member function}}
};
class VirtualBase : virtual Okay { // expected-note 3 {{because type 'class VirtualBase' has a virtual base class}}
};
class Ctor {
Ctor() { abort(); } // expected-note 3 {{because type 'class Ctor' has a user-declared constructor}}
};
class CopyCtor {
CopyCtor(CopyCtor &cc) { abort(); } // expected-note 3 {{because type 'class CopyCtor' has a user-declared copy constructor}}
};
// FIXME: this should eventually trigger on the operator's declaration line
class CopyAssign { // expected-note 3 {{because type 'class CopyAssign' has a user-declared copy assignment operator}}
CopyAssign& operator=(CopyAssign& CA) { abort(); }
};
class Dtor {
~Dtor() { abort(); } // expected-note 3 {{because type 'class Dtor' has a user-declared destructor}}
};
union U1 {
Virtual v; // expected-error {{union member 'v' has a non-trivial copy constructor}}
VirtualBase vbase; // expected-error {{union member 'vbase' has a non-trivial copy constructor}}
Ctor ctor; // expected-error {{union member 'ctor' has a non-trivial constructor}}
CopyCtor copyctor; // expected-error {{union member 'copyctor' has a non-trivial copy constructor}}
CopyAssign copyassign; // expected-error {{union member 'copyassign' has a non-trivial copy assignment operator}}
Dtor dtor; // expected-error {{union member 'dtor' has a non-trivial destructor}}
Okay okay;
};
union U2 {
struct {
Virtual v; // expected-note {{because type 'struct U2::<anonymous>' has a member with a non-trivial copy constructor}}
} m1; // expected-error {{union member 'm1' has a non-trivial copy constructor}}
struct {
VirtualBase vbase; // expected-note {{because type 'struct U2::<anonymous>' has a member with a non-trivial copy constructor}}
} m2; // expected-error {{union member 'm2' has a non-trivial copy constructor}}
struct {
Ctor ctor; // expected-note {{because type 'struct U2::<anonymous>' has a member with a non-trivial constructor}}
} m3; // expected-error {{union member 'm3' has a non-trivial constructor}}
struct {
CopyCtor copyctor; // expected-note {{because type 'struct U2::<anonymous>' has a member with a non-trivial copy constructor}}
} m4; // expected-error {{union member 'm4' has a non-trivial copy constructor}}
struct {
CopyAssign copyassign; // expected-note {{because type 'struct U2::<anonymous>' has a member with a non-trivial copy assignment operator}}
} m5; // expected-error {{union member 'm5' has a non-trivial copy assignment operator}}
struct {
Dtor dtor; // expected-note {{because type 'struct U2::<anonymous>' has a member with a non-trivial destructor}}
} m6; // expected-error {{union member 'm6' has a non-trivial destructor}}
struct {
Okay okay;
} m7;
};
union U3 {
struct s1 : Virtual { // expected-note {{because type 'struct U3::s1' has a base class with a non-trivial copy constructor}}
} m1; // expected-error {{union member 'm1' has a non-trivial copy constructor}}
struct s2 : VirtualBase { // expected-note {{because type 'struct U3::s2' has a base class with a non-trivial copy constructor}}
} m2; // expected-error {{union member 'm2' has a non-trivial copy constructor}}
struct s3 : Ctor { // expected-note {{because type 'struct U3::s3' has a base class with a non-trivial constructor}}
} m3; // expected-error {{union member 'm3' has a non-trivial constructor}}
struct s4 : CopyCtor { // expected-note {{because type 'struct U3::s4' has a base class with a non-trivial copy constructor}}
} m4; // expected-error {{union member 'm4' has a non-trivial copy constructor}}
struct s5 : CopyAssign { // expected-note {{because type 'struct U3::s5' has a base class with a non-trivial copy assignment operator}}
} m5; // expected-error {{union member 'm5' has a non-trivial copy assignment operator}}
struct s6 : Dtor { // expected-note {{because type 'struct U3::s6' has a base class with a non-trivial destructor}}
} m6; // expected-error {{union member 'm6' has a non-trivial destructor}}
struct s7 : Okay {
} m7;
};
template <class A, class B> struct Either {
bool tag;
union {
A a;
B b;
};
Either(A& a) : tag(true), a(a) {}
Either(B& b) : tag(false), b(b) {}
};
/* FIXME: this should work, but crashes in template code.
void fred() {
Either<int,Virtual> virt(0);
Either<int,VirtualBase> vbase(0);
Either<int,Ctor> ctor(0);
Either<int,CopyCtor> copyctor(0);
Either<int,CopyAssign> copyassign(0);
Either<int,Dtor> dtor(0);
Either<int,Okay> okay(0);
}
*/