From 8a27391190defd14a24057f3e7c1b9959ac715fa Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 22 Jul 2009 18:25:24 +0000 Subject: [PATCH] "This patch implements the restrictions on union members detailed in [class.union]p1", from John McCall! llvm-svn: 76766 --- clang/include/clang/AST/Decl.h | 12 +- clang/include/clang/AST/DeclCXX.h | 87 ++++++++++- clang/include/clang/Basic/DiagnosticSemaKinds.td | 12 ++ clang/lib/AST/DeclCXX.cpp | 19 ++- clang/lib/Sema/Sema.h | 8 + clang/lib/Sema/SemaDecl.cpp | 181 +++++++++++++++++++++++ clang/lib/Sema/SemaDeclCXX.cpp | 57 +++---- clang/test/CXX/class/class.union/p1.cpp | 105 +++++++++++++ 8 files changed, 443 insertions(+), 38 deletions(-) create mode 100644 clang/test/CXX/class/class.union/p1.cpp diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 48b14a9..d998738 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -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 diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index ffacc0e..6f9eee7 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -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 + /// 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 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 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; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4cd1dca..df62573 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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)">; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index af54fb0..3f5320b 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -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 [...]. diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 7f652f2..7daf3f9 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -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, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index beceeef..2db0725 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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(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(RT->getDecl()); + + if (!RDecl->hasTrivialConstructor()) + cast(Record)->setHasTrivialConstructor(false); + if (!RDecl->hasTrivialCopyConstructor()) + cast(Record)->setHasTrivialCopyConstructor(false); + if (!RDecl->hasTrivialCopyAssignment()) + cast(Record)->setHasTrivialCopyAssignment(false); + if (!RDecl->hasTrivialDestructor()) + cast(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(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(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(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(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 diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 2b8d0a8..3681709 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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(BaseDecl)-> - hasTrivialConstructor()); + if (!cast(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(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(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(BaseDecl)-> - hasTrivialDestructor()); + if (!cast(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(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); } } diff --git a/clang/test/CXX/class/class.union/p1.cpp b/clang/test/CXX/class/class.union/p1.cpp new file mode 100644 index 0000000..0c7eafe --- /dev/null +++ b/clang/test/CXX/class/class.union/p1.cpp @@ -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::' 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::' 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::' 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::' 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::' 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::' 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 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 virt(0); + Either vbase(0); + Either ctor(0); + Either copyctor(0); + Either copyassign(0); + Either dtor(0); + Either okay(0); +} + */ -- 2.7.4