diff options
author | Sebastian Redl <sebastian.redl@getdesigned.at> | 2009-01-05 20:52:13 +0000 |
---|---|---|
committer | Sebastian Redl <sebastian.redl@getdesigned.at> | 2009-01-05 20:52:13 +0000 |
commit | baad4e765f5a03af2cbd5fbccffb8736badd517a (patch) | |
tree | 5bb23acba40adec7aa431f00cdc2713267481a00 /clang/lib | |
parent | 52e5deece5b99ff5d2ca5547c4c894397da1be41 (diff) | |
download | bcm5719-llvm-baad4e765f5a03af2cbd5fbccffb8736badd517a.tar.gz bcm5719-llvm-baad4e765f5a03af2cbd5fbccffb8736badd517a.zip |
PODness and Type Traits
Make C++ classes track the POD property (C++ [class]p4)
Track the existence of a copy assignment operator.
Implicitly declare the copy assignment operator if none is provided.
Implement most of the parsing job for the G++ type traits extension.
Fully implement the low-hanging fruit of the type traits:
__is_pod: Whether a type is a POD.
__is_class: Whether a type is a (non-union) class.
__is_union: Whether a type is a union.
__is_enum: Whether a type is an enum.
__is_polymorphic: Whether a type is polymorphic (C++ [class.virtual]p1).
llvm-svn: 61746
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 86 | ||||
-rw-r--r-- | clang/lib/AST/Expr.cpp | 4 | ||||
-rw-r--r-- | clang/lib/AST/ExprCXX.cpp | 29 | ||||
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 6 | ||||
-rw-r--r-- | clang/lib/AST/StmtPrinter.cpp | 26 | ||||
-rw-r--r-- | clang/lib/AST/StmtSerialization.cpp | 19 | ||||
-rw-r--r-- | clang/lib/AST/Type.cpp | 36 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExpr.cpp | 29 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 48 | ||||
-rw-r--r-- | clang/lib/Sema/Sema.h | 8 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 22 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 118 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 17 |
13 files changed, 424 insertions, 24 deletions
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 66afac8524c..545f18ed056 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -56,9 +56,9 @@ CXXRecordDecl::CXXRecordDecl(TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id) : RecordDecl(CXXRecord, TK, DC, L, Id), UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), - UserDeclaredDestructor(false), Aggregate(true), Polymorphic(false), - Bases(0), NumBases(0), - Conversions(DC, DeclarationName()) { } + UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false), + Aggregate(true), PlainOldData(true), Polymorphic(false), Bases(0), + NumBases(0), Conversions(DC, DeclarationName()) { } CXXRecordDecl *CXXRecordDecl::Create(ASTContext &C, TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, @@ -91,7 +91,8 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, } bool CXXRecordDecl::hasConstCopyConstructor(ASTContext &Context) const { - QualType ClassType = Context.getTypeDeclType(const_cast<CXXRecordDecl*>(this)); + QualType ClassType + = Context.getTypeDeclType(const_cast<CXXRecordDecl*>(this)); DeclarationName ConstructorName = Context.DeclarationNames.getCXXConstructorName( Context.getCanonicalType(ClassType)); @@ -107,7 +108,49 @@ bool CXXRecordDecl::hasConstCopyConstructor(ASTContext &Context) const { return false; } -void +bool CXXRecordDecl::hasConstCopyAssignment(ASTContext &Context) const { + QualType ClassType = Context.getCanonicalType(Context.getTypeDeclType( + const_cast<CXXRecordDecl*>(this))); + DeclarationName OpName =Context.DeclarationNames.getCXXOperatorName(OO_Equal); + + DeclContext::lookup_const_iterator Op, OpEnd; + for (llvm::tie(Op, OpEnd) = this->lookup(Context, OpName); + Op != OpEnd; ++Op) { + // C++ [class.copy]p9: + // A user-declared copy assignment operator is a non-static non-template + // member function of class X with exactly one parameter of type X, X&, + // const X&, volatile X& or const volatile X&. + const CXXMethodDecl* Method = cast<CXXMethodDecl>(*Op); + if (Method->isStatic()) + continue; + // TODO: Skip templates? Or is this implicitly done due to parameter types? + const FunctionTypeProto *FnType = + Method->getType()->getAsFunctionTypeProto(); + assert(FnType && "Overloaded operator has no prototype."); + // Don't assert on this; an invalid decl might have been left in the AST. + if (FnType->getNumArgs() != 1 || FnType->isVariadic()) + continue; + bool AcceptsConst = true; + QualType ArgType = FnType->getArgType(0); + if (const ReferenceType *Ref = ArgType->getAsReferenceType()) { + ArgType = Ref->getPointeeType(); + // Is it a non-const reference? + if (!ArgType.isConstQualified()) + AcceptsConst = false; + } + if (Context.getCanonicalType(ArgType).getUnqualifiedType() != ClassType) + continue; + + // We have a single argument of type cv X or cv X&, i.e. we've found the + // copy assignment operator. Return whether it accepts const arguments. + return AcceptsConst; + } + assert(isInvalidDecl() && + "No copy assignment operator declared in valid code."); + return false; +} + +void CXXRecordDecl::addedConstructor(ASTContext &Context, CXXConstructorDecl *ConDecl) { if (!ConDecl->isImplicitlyDeclared()) { @@ -119,6 +162,10 @@ CXXRecordDecl::addedConstructor(ASTContext &Context, // user-declared constructors (12.1) [...]. Aggregate = false; + // C++ [class]p4: + // A POD-struct is an aggregate class [...] + PlainOldData = false; + // Note when we have a user-declared copy constructor, which will // suppress the implicit declaration of a copy constructor. if (ConDecl->isCopyConstructor(Context)) @@ -126,6 +173,35 @@ CXXRecordDecl::addedConstructor(ASTContext &Context, } } +void CXXRecordDecl::addedAssignmentOperator(ASTContext &Context, + CXXMethodDecl *OpDecl) { + // We're interested specifically in copy assignment operators. + // Unlike addedConstructor, this method is not called for implicit + // declarations. + const FunctionTypeProto *FnType = OpDecl->getType()->getAsFunctionTypeProto(); + assert(FnType && "Overloaded operator has no proto function type."); + assert(FnType->getNumArgs() == 1 && !FnType->isVariadic()); + QualType ArgType = FnType->getArgType(0); + if (const ReferenceType *Ref = ArgType->getAsReferenceType()) + ArgType = Ref->getPointeeType(); + + ArgType = ArgType.getUnqualifiedType(); + QualType ClassType = Context.getCanonicalType(Context.getTypeDeclType( + const_cast<CXXRecordDecl*>(this))); + + if (ClassType != Context.getCanonicalType(ArgType)) + return; + + // This is a copy assignment operator. + // Suppress the implicit declaration of a copy constructor. + UserDeclaredCopyAssignment = true; + + // C++ [class]p4: + // A POD-struct is an aggregate class that [...] has no user-defined copy + // assignment operator [...]. + PlainOldData = false; +} + void CXXRecordDecl::addConversionFunction(ASTContext &Context, CXXConversionDecl *ConvDecl) { Conversions.addOverload(ConvDecl); diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 6cfcdc49144..eafa717d654 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1092,6 +1092,10 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx, case CXXDefaultArgExprClass: return cast<CXXDefaultArgExpr>(this) ->isIntegerConstantExpr(Result, Ctx, Loc, isEvaluated); + + case UnaryTypeTraitExprClass: + Result = cast<UnaryTypeTraitExpr>(this)->Evaluate(); + return true; } // Cases that are valid constant exprs fall through to here. diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 4cc5c741cfb..07d794ea437 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -120,6 +120,35 @@ Stmt::child_iterator CXXDependentNameExpr::child_end() { return child_iterator(); } +// UnaryTypeTraitExpr +Stmt::child_iterator UnaryTypeTraitExpr::child_begin() { + return child_iterator(); +} +Stmt::child_iterator UnaryTypeTraitExpr::child_end() { + return child_iterator(); +} + +bool UnaryTypeTraitExpr::Evaluate() const { + switch(UTT) { + default: assert(false && "Unknown type trait or not implemented"); + case UTT_IsPOD: return QueriedType->isPODType(); + case UTT_IsClass: // Fallthrough + case UTT_IsUnion: + if (const RecordType *Record = QueriedType->getAsRecordType()) { + bool Union = Record->getDecl()->isUnion(); + return UTT == UTT_IsUnion ? Union : !Union; + } + return false; + case UTT_IsEnum: return QueriedType->isEnumeralType(); + case UTT_IsPolymorphic: + if (const RecordType *Record = QueriedType->getAsRecordType()) { + // Type traits are only parsed in C++, so we've got CXXRecords. + return cast<CXXRecordDecl>(Record->getDecl())->isPolymorphic(); + } + return false; + } +} + OverloadedOperatorKind CXXOperatorCallExpr::getOperator() const { // All simple function calls (e.g. func()) are implicitly cast to pointer to // function. As a result, we try and obtain the DeclRefExpr from the diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index db9536633be..de6a14b87b6 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -428,6 +428,12 @@ public: return true; } + bool VisitUnaryTypeTraitExpr(const UnaryTypeTraitExpr *E) { + Result.zextOrTrunc(getIntTypeSizeInBits(E->getType())); + Result = E->Evaluate(); + return true; + } + private: bool HandleCast(CastExpr* E); }; diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 521478953f2..89a185d965d 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1020,6 +1020,32 @@ void StmtPrinter::VisitCXXDependentNameExpr(CXXDependentNameExpr *E) { OS << E->getName()->getName(); } +static const char *getTypeTraitName(UnaryTypeTrait UTT) { + switch (UTT) { + default: assert(false && "Unknown type trait"); + case UTT_HasNothrowAssign: return "__has_nothrow_assign"; + case UTT_HasNothrowCopy: return "__has_nothrow_copy"; + case UTT_HasNothrowConstructor: return "__has_nothrow_constructor"; + case UTT_HasTrivialAssign: return "__has_trivial_assign"; + case UTT_HasTrivialCopy: return "__has_trivial_copy"; + case UTT_HasTrivialConstructor: return "__has_trivial_constructor"; + case UTT_HasTrivialDestructor: return "__has_trivial_destructor"; + case UTT_HasVirtualDestructor: return "__has_virtual_destructor"; + case UTT_IsAbstract: return "__is_abstract"; + case UTT_IsClass: return "__is_class"; + case UTT_IsEmpty: return "__is_empty"; + case UTT_IsEnum: return "__is_enum"; + case UTT_IsPOD: return "__is_pod"; + case UTT_IsPolymorphic: return "__is_polymorphic"; + case UTT_IsUnion: return "__is_union"; + } +} + +void StmtPrinter::VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E) { + OS << getTypeTraitName(E->getTrait()) << "(" + << E->getQueriedType().getAsString() << ")"; +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { diff --git a/clang/lib/AST/StmtSerialization.cpp b/clang/lib/AST/StmtSerialization.cpp index 3f7c0384d73..f7c4cf9b937 100644 --- a/clang/lib/AST/StmtSerialization.cpp +++ b/clang/lib/AST/StmtSerialization.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/TypeTraits.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" @@ -1530,6 +1531,24 @@ CXXDependentNameExpr::CreateImpl(llvm::Deserializer& D, ASTContext& C) { return new CXXDependentNameExpr(N, Ty, L); } +void UnaryTypeTraitExpr::EmitImpl(llvm::Serializer& S) const { + S.EmitInt(UTT); + S.Emit(Loc); + S.Emit(RParen); + S.Emit(QueriedType); + S.Emit(getType()); +} + +UnaryTypeTraitExpr * +UnaryTypeTraitExpr::CreateImpl(llvm::Deserializer& D, ASTContext& C) { + UnaryTypeTrait UTT = static_cast<UnaryTypeTrait>(D.ReadInt()); + SourceLocation Loc = SourceLocation::ReadVal(D); + SourceLocation RParen = SourceLocation::ReadVal(D); + QualType QueriedType = QualType::ReadVal(D); + QualType Ty = QualType::ReadVal(D); + return new UnaryTypeTraitExpr(Loc, UTT, QueriedType, RParen, Ty); +} + void CXXCatchStmt::EmitImpl(llvm::Serializer& S) const { S.Emit(CatchLoc); S.EmitOwnedPtr(ExceptionDecl); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c70ad4af521..1d42b64bb12 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -670,6 +670,42 @@ bool Type::isIncompleteType() const { } } +/// isPODType - Return true if this is a plain-old-data type (C++ 3.9p10) +bool Type::isPODType() const { + // The compiler shouldn't query this for incomplete types, but the user might. + // We return false for that case. + if (isIncompleteType()) + return false; + + switch (CanonicalType->getTypeClass()) { + // Everything not explicitly mentioned is not POD. + default: return false; + case ASQual: + return cast<ASQualType>(CanonicalType)->getBaseType()->isPODType(); + case VariableArray: + case ConstantArray: + // IncompleteArray is caught by isIncompleteType() above. + return cast<ArrayType>(CanonicalType)->getElementType()->isPODType(); + + case Builtin: + case Complex: + case Pointer: + case Vector: + case ExtVector: + // FIXME: pointer-to-member + return true; + + case Tagged: + if (isEnumeralType()) + return true; + if (CXXRecordDecl *RDecl = dyn_cast<CXXRecordDecl>( + cast<TagType>(CanonicalType)->getDecl())) + return RDecl->isPOD(); + // C struct/union is POD. + return true; + } +} + bool Type::isPromotableIntegerType() const { if (const ASQualType *ASQT = dyn_cast<ASQualType>(CanonicalType)) return ASQT->getBaseType()->isPromotableIntegerType(); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index c2bbad3cda1..94e686b6d77 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -381,6 +381,8 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { /// [C++] 'typeid' '(' expression ')' [C++ 5.2p1] /// [C++] 'typeid' '(' type-id ')' [C++ 5.2p1] /// [C++] 'this' [C++ 9.3.2] +/// [G++] unary-type-trait '(' type-id ')' +/// [G++] binary-type-trait '(' type-id ',' type-id ')' [TODO] /// [clang] '^' block-literal /// /// constant: [C99 6.4.4] @@ -410,6 +412,26 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { /// '::'[opt] 'delete' cast-expression /// '::'[opt] 'delete' '[' ']' cast-expression /// +/// [GNU] unary-type-trait: +/// '__has_nothrow_assign' [TODO] +/// '__has_nothrow_copy' [TODO] +/// '__has_nothrow_constructor' [TODO] +/// '__has_trivial_assign' [TODO] +/// '__has_trivial_copy' [TODO] +/// '__has_trivial_constructor' [TODO] +/// '__has_trivial_destructor' [TODO] +/// '__has_virtual_destructor' [TODO] +/// '__is_abstract' [TODO] +/// '__is_class' +/// '__is_empty' [TODO] +/// '__is_enum' +/// '__is_pod' +/// '__is_polymorphic' +/// '__is_union' +/// +/// [GNU] binary-type-trait: +/// '__is_base_of' [TODO] +/// Parser::OwningExprResult Parser::ParseCastExpression(bool isUnaryExpression) { OwningExprResult Res(Actions); tok::TokenKind SavedKind = Tok.getKind(); @@ -650,6 +672,13 @@ Parser::OwningExprResult Parser::ParseCastExpression(bool isUnaryExpression) { case tok::kw_delete: // [C++] delete-expression return ParseCXXDeleteExpression(false, Tok.getLocation()); + case tok::kw___is_pod: // [GNU] unary-type-trait + case tok::kw___is_class: + case tok::kw___is_enum: + case tok::kw___is_union: + case tok::kw___is_polymorphic: + return ParseUnaryTypeTrait(); + case tok::at: { SourceLocation AtLoc = ConsumeToken(); return ParseObjCAtExpression(AtLoc); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index c7c6be91526..5731f1f1c6f 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -839,3 +839,51 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { return Owned(Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.release())); } + +static UnaryTypeTrait UnaryTypeTraitFromTokKind(tok::TokenKind kind) +{ + switch(kind) { + default: assert(false && "Not a known unary type trait."); + case tok::kw___has_nothrow_assign: return UTT_HasNothrowAssign; + case tok::kw___has_nothrow_copy: return UTT_HasNothrowCopy; + case tok::kw___has_nothrow_constructor: return UTT_HasNothrowConstructor; + case tok::kw___has_trivial_assign: return UTT_HasTrivialAssign; + case tok::kw___has_trivial_copy: return UTT_HasTrivialCopy; + case tok::kw___has_trivial_constructor: return UTT_HasTrivialConstructor; + case tok::kw___has_trivial_destructor: return UTT_HasTrivialDestructor; + case tok::kw___has_virtual_destructor: return UTT_HasVirtualDestructor; + case tok::kw___is_abstract: return UTT_IsAbstract; + case tok::kw___is_class: return UTT_IsClass; + case tok::kw___is_empty: return UTT_IsEmpty; + case tok::kw___is_enum: return UTT_IsEnum; + case tok::kw___is_pod: return UTT_IsPOD; + case tok::kw___is_polymorphic: return UTT_IsPolymorphic; + case tok::kw___is_union: return UTT_IsUnion; + } +} + +/// ParseUnaryTypeTrait - Parse the built-in unary type-trait +/// pseudo-functions that allow implementation of the TR1/C++0x type traits +/// templates. +/// +/// primary-expression: +/// [GNU] unary-type-trait '(' type-id ')' +/// +Parser::OwningExprResult Parser::ParseUnaryTypeTrait() +{ + UnaryTypeTrait UTT = UnaryTypeTraitFromTokKind(Tok.getKind()); + SourceLocation Loc = ConsumeToken(); + + SourceLocation LParen = Tok.getLocation(); + if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen)) + return ExprError(); + + // FIXME: Error reporting absolutely sucks! If the this fails to parse a type + // there will be cryptic errors about mismatched parentheses and missing + // specifiers. + TypeTy *Ty = ParseTypeName(); + + SourceLocation RParen = MatchRHSPunctuation(tok::r_paren, LParen); + + return Actions.ActOnUnaryTypeTrait(UTT, Loc, LParen, Ty, RParen); +} diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index d3f373f4949..e35c06dd2f3 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -918,6 +918,14 @@ public: SourceLocation EqualLoc, ExprTy *AssignExprVal); + /// ActOnUnaryTypeTrait - Parsed one of the unary type trait support + /// pseudo-functions. + virtual OwningExprResult ActOnUnaryTypeTrait(UnaryTypeTrait OTT, + SourceLocation KWLoc, + SourceLocation LParen, + TypeTy *Ty, + SourceLocation RParen); + /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f4677f03f41..0396f520a84 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1153,7 +1153,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, FunctionDecl::StorageClass SC = FunctionDecl::None; switch (D.getDeclSpec().getStorageClassSpec()) { default: assert(0 && "Unknown storage class!"); - case DeclSpec::SCS_auto: + case DeclSpec::SCS_auto: case DeclSpec::SCS_register: case DeclSpec::SCS_mutable: Diag(D.getIdentifierLoc(), diag::err_typecheck_sclass_func); @@ -1319,9 +1319,14 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(NewFD)) InvalidDecl = InvalidDecl || CheckConstructor(Constructor); - else if (isa<CXXDestructorDecl>(NewFD)) - cast<CXXRecordDecl>(NewFD->getParent())->setUserDeclaredDestructor(true); - else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD)) + else if (isa<CXXDestructorDecl>(NewFD)) { + CXXRecordDecl *Record = cast<CXXRecordDecl>(NewFD->getParent()); + Record->setUserDeclaredDestructor(true); + // C++ [class]p4: A POD-struct is an aggregate class that has [...] no + // user-defined destructor. + Record->setPOD(false); + } else if (CXXConversionDecl *Conversion = + dyn_cast<CXXConversionDecl>(NewFD)) ActOnConversionDeclarator(Conversion); // Extra checking for C++ overloaded operators (C++ [over.oper]). @@ -2836,7 +2841,7 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD, QualType T = GetTypeForDeclarator(D, S); assert(!T.isNull() && "GetTypeForDeclarator() returned null type"); bool InvalidDecl = false; - + // C99 6.7.2.1p8: A member of a structure or union may have any type other // than a variably modified type. if (T->isVariablyModifiedType()) { @@ -2872,8 +2877,11 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD, DeclSpec::SCS_mutable, /*PrevDecl=*/0); - if (getLangOptions().CPlusPlus) + if (getLangOptions().CPlusPlus) { CheckExtraCXXDefaultArguments(D); + if (!T->isPODType()) + cast<CXXRecordDecl>(Record)->setPOD(false); + } ProcessDeclAttributes(NewFD, D); @@ -3088,7 +3096,7 @@ void Sema::ActOnFields(Scope* S, ++NumNamedMembers; } } - + // Okay, we successfully defined 'Record'. if (Record) { Record->completeDefinition(Context); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 7c05f6b4970..e67b8c03897 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -317,7 +317,7 @@ Sema::BaseResult Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, bool Virtual, AccessSpecifier Access, TypeTy *basetype, SourceLocation BaseLoc) { - RecordDecl *Decl = (RecordDecl*)classdecl; + CXXRecordDecl *Decl = (CXXRecordDecl*)classdecl; QualType BaseType = Context.getTypeDeclType((TypeDecl*)basetype); // Base specifiers must be record types. @@ -347,7 +347,12 @@ Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, BaseDecl = BaseDecl->getDefinition(Context); assert(BaseDecl && "Base type is not incomplete, but has no definition"); if (cast<CXXRecordDecl>(BaseDecl)->isPolymorphic()) - cast<CXXRecordDecl>(Decl)->setPolymorphic(true); + Decl->setPolymorphic(true); + + // C++ [dcl.init.aggr]p1: + // An aggregate is [...] a class with [...] no base classes [...]. + Decl->setAggregate(false); + Decl->setPOD(false); // Create the base specifier. return new CXXBaseSpecifier(SpecifierRange, Virtual, @@ -537,8 +542,12 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, // C++ [dcl.init.aggr]p1: // An aggregate is an array or a class (clause 9) with [...] no // private or protected non-static data members (clause 11). - if (isInstField && (AS == AS_private || AS == AS_protected)) - cast<CXXRecordDecl>(CurContext)->setAggregate(false); + // A POD must be an aggregate. + if (isInstField && (AS == AS_private || AS == AS_protected)) { + CXXRecordDecl *Record = cast<CXXRecordDecl>(CurContext); + Record->setAggregate(false); + Record->setPOD(false); + } if (DS.isVirtualSpecified()) { if (!isFunc || DS.getStorageClassSpec() == DeclSpec::SCS_static) { @@ -547,6 +556,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, } else { CXXRecordDecl *CurClass = cast<CXXRecordDecl>(CurContext); CurClass->setAggregate(false); + CurClass->setPOD(false); CurClass->setPolymorphic(true); } } @@ -827,17 +837,17 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { } } - // Otherwise, the implicitly declared copy constructor will have - // the form + // Otherwise, the implicitly declared copy constructor will have + // the form // // X::X(X&) - QualType ArgType = Context.getTypeDeclType(ClassDecl); + QualType ArgType = ClassType; if (HasConstCopyConstructor) ArgType = ArgType.withConst(); ArgType = Context.getReferenceType(ArgType); - // An implicitly-declared copy constructor is an inline public - // member of its class. + // An implicitly-declared copy constructor is an inline public + // member of its class. DeclarationName Name = Context.DeclarationNames.getCXXConstructorName(ClassType); CXXConstructorDecl *CopyConstructor @@ -862,6 +872,83 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { ClassDecl->addDecl(Context, CopyConstructor); } + if (!ClassDecl->hasUserDeclaredCopyAssignment()) { + // Note: The following rules are largely analoguous to the copy + // constructor rules. Note that virtual bases are not taken into account + // for determining the argument type of the operator. Note also that + // operators taking an object instead of a reference are allowed. + // + // C++ [class.copy]p10: + // If the class definition does not explicitly declare a copy + // assignment operator, one is declared implicitly. + // The implicitly-defined copy assignment operator for a class X + // will have the form + // + // X& X::operator=(const X&) + // + // if + bool HasConstCopyAssignment = true; + + // -- each direct base class B of X has a copy assignment operator + // whose parameter is of type const B&, const volatile B& or B, + // and + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(); + HasConstCopyAssignment && Base != ClassDecl->bases_end(); ++Base) { + const CXXRecordDecl *BaseClassDecl + = cast<CXXRecordDecl>(Base->getType()->getAsRecordType()->getDecl()); + HasConstCopyAssignment = BaseClassDecl->hasConstCopyAssignment(Context); + } + + // -- for all the nonstatic data members of X that are of a class + // type M (or array thereof), each such class type has a copy + // assignment operator whose parameter is of type const M&, + // const volatile M& or M. + for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(); + HasConstCopyAssignment && Field != ClassDecl->field_end(); ++Field) { + QualType FieldType = (*Field)->getType(); + if (const ArrayType *Array = Context.getAsArrayType(FieldType)) + FieldType = Array->getElementType(); + if (const RecordType *FieldClassType = FieldType->getAsRecordType()) { + const CXXRecordDecl *FieldClassDecl + = cast<CXXRecordDecl>(FieldClassType->getDecl()); + HasConstCopyAssignment + = FieldClassDecl->hasConstCopyAssignment(Context); + } + } + + // Otherwise, the implicitly declared copy assignment operator will + // have the form + // + // X& X::operator=(X&) + QualType ArgType = ClassType; + QualType RetType = Context.getReferenceType(ArgType); + if (HasConstCopyAssignment) + ArgType = ArgType.withConst(); + ArgType = Context.getReferenceType(ArgType); + + // An implicitly-declared copy assignment operator is an inline public + // member of its class. + DeclarationName Name = + Context.DeclarationNames.getCXXOperatorName(OO_Equal); + CXXMethodDecl *CopyAssignment = + CXXMethodDecl::Create(Context, ClassDecl, ClassDecl->getLocation(), Name, + Context.getFunctionType(RetType, &ArgType, 1, + false, 0), + /*isStatic=*/false, /*isInline=*/true, 0); + CopyAssignment->setAccess(AS_public); + + // Add the parameter to the operator. + ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyAssignment, + ClassDecl->getLocation(), + /*IdentifierInfo=*/0, + ArgType, VarDecl::None, 0, 0); + CopyAssignment->setParams(&FromParam, 1); + + // Don't call addedAssignmentOperator. There is no way to distinguish an + // implicit from an explicit assignment operator. + ClassDecl->addDecl(Context, CopyAssignment); + } + if (!ClassDecl->hasUserDeclaredDestructor()) { // C++ [class.dtor]p2: // If a class has no user-declared destructor, a destructor is @@ -879,8 +966,6 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { Destructor->setAccess(AS_public); ClassDecl->addDecl(Context, Destructor); } - - // FIXME: Implicit copy assignment operator } void Sema::ActOnFinishCXXClassDef(DeclTy *D) { @@ -1963,7 +2048,7 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { return Diag(FnDecl->getLocation(), diag::err_operator_overload_must_be) << FnDecl->getDeclName() << NumParams << ErrorKind; } - + // Overloaded operators other than operator() cannot be variadic. if (Op != OO_Call && FnDecl->getType()->getAsFunctionTypeProto()->isVariadic()) { @@ -2000,6 +2085,15 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { << LastParam->getType() << (Op == OO_MinusMinus); } + // Notify the class if it got an assignment operator. + if (Op == OO_Equal) { + // Would have returned earlier otherwise. + assert(isa<CXXMethodDecl>(FnDecl) && + "Overloaded = not member, but not filtered."); + CXXMethodDecl *Method = cast<CXXMethodDecl>(FnDecl); + Method->getParent()->addedAssignmentOperator(Context, Method); + } + return false; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index d367a91aa5d..3510b5d1289 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -842,3 +842,20 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, return false; } +Sema::OwningExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait OTT, + SourceLocation KWLoc, + SourceLocation LParen, + TypeTy *Ty, + SourceLocation RParen) { + // FIXME: Some of the type traits have requirements. Interestingly, only the + // __is_base_of requirement is explicitly stated to be diagnosed. Indeed, + // G++ accepts __is_pod(Incomplete) without complaints, and claims that the + // type is indeed a POD. + + // There is no point in eagerly computing the value. The traits are designed + // to be used from type trait templates, so Ty will be a template parameter + // 99% of the time. + return Owned(new UnaryTypeTraitExpr(KWLoc, OTT, + QualType::getFromOpaquePtr(Ty), + RParen, Context.BoolTy)); +} |