diff options
Diffstat (limited to 'lib/Sema/SemaExprCXX.cpp')
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 217 |
1 files changed, 209 insertions, 8 deletions
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index db287515ce..76d6f496e6 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1989,7 +1989,7 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, return false; } -ExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait OTT, +ExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait UTT, SourceLocation KWLoc, ParsedType Ty, SourceLocation RParen) { @@ -1998,10 +1998,210 @@ ExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait OTT, if (!TSInfo) TSInfo = Context.getTrivialTypeSourceInfo(T); - return BuildUnaryTypeTrait(OTT, KWLoc, TSInfo, RParen); + return BuildUnaryTypeTrait(UTT, KWLoc, TSInfo, RParen); } -ExprResult Sema::BuildUnaryTypeTrait(UnaryTypeTrait OTT, +static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T) { + assert(!T->isDependentType() && + "Cannot evaluate traits for dependent types."); + ASTContext &C = Self.Context; + switch(UTT) { + default: assert(false && "Unknown type trait or not implemented"); + case UTT_IsPOD: return T->isPODType(); + case UTT_IsLiteral: return T->isLiteralType(); + case UTT_IsClass: // Fallthrough + case UTT_IsUnion: + if (const RecordType *Record = T->getAs<RecordType>()) { + bool Union = Record->getDecl()->isUnion(); + return UTT == UTT_IsUnion ? Union : !Union; + } + return false; + case UTT_IsEnum: return T->isEnumeralType(); + case UTT_IsPolymorphic: + if (const RecordType *Record = T->getAs<RecordType>()) { + // Type traits are only parsed in C++, so we've got CXXRecords. + return cast<CXXRecordDecl>(Record->getDecl())->isPolymorphic(); + } + return false; + case UTT_IsAbstract: + if (const RecordType *RT = T->getAs<RecordType>()) + return cast<CXXRecordDecl>(RT->getDecl())->isAbstract(); + return false; + case UTT_IsEmpty: + if (const RecordType *Record = T->getAs<RecordType>()) { + return !Record->getDecl()->isUnion() + && cast<CXXRecordDecl>(Record->getDecl())->isEmpty(); + } + return false; + case UTT_HasTrivialConstructor: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __is_pod (type) is true then the trait is true, else if type is + // a cv class or union type (or array thereof) with a trivial default + // constructor ([class.ctor]) then the trait is true, else it is false. + if (T->isPODType()) + return true; + if (const RecordType *RT = + C.getBaseElementType(T)->getAs<RecordType>()) + return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialConstructor(); + return false; + case UTT_HasTrivialCopy: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __is_pod (type) is true or type is a reference type then + // the trait is true, else if type is a cv class or union type + // with a trivial copy constructor ([class.copy]) then the trait + // is true, else it is false. + if (T->isPODType() || T->isReferenceType()) + return true; + if (const RecordType *RT = T->getAs<RecordType>()) + return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialCopyConstructor(); + return false; + case UTT_HasTrivialAssign: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If type is const qualified or is a reference type then the + // trait is false. Otherwise if __is_pod (type) is true then the + // trait is true, else if type is a cv class or union type with + // a trivial copy assignment ([class.copy]) then the trait is + // true, else it is false. + // Note: the const and reference restrictions are interesting, + // given that const and reference members don't prevent a class + // from having a trivial copy assignment operator (but do cause + // errors if the copy assignment operator is actually used, q.v. + // [class.copy]p12). + + if (C.getBaseElementType(T).isConstQualified()) + return false; + if (T->isPODType()) + return true; + if (const RecordType *RT = T->getAs<RecordType>()) + return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialCopyAssignment(); + return false; + case UTT_HasTrivialDestructor: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __is_pod (type) is true or type is a reference type + // then the trait is true, else if type is a cv class or union + // type (or array thereof) with a trivial destructor + // ([class.dtor]) then the trait is true, else it is + // false. + if (T->isPODType() || T->isReferenceType()) + return true; + if (const RecordType *RT = + C.getBaseElementType(T)->getAs<RecordType>()) + return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialDestructor(); + return false; + // TODO: Propagate nothrowness for implicitly declared special members. + case UTT_HasNothrowAssign: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If type is const qualified or is a reference type then the + // trait is false. Otherwise if __has_trivial_assign (type) + // is true then the trait is true, else if type is a cv class + // or union type with copy assignment operators that are known + // not to throw an exception then the trait is true, else it is + // false. + if (C.getBaseElementType(T).isConstQualified()) + return false; + if (T->isReferenceType()) + return false; + if (T->isPODType()) + return true; + if (const RecordType *RT = T->getAs<RecordType>()) { + CXXRecordDecl* RD = cast<CXXRecordDecl>(RT->getDecl()); + if (RD->hasTrivialCopyAssignment()) + return true; + + bool FoundAssign = false; + bool AllNoThrow = true; + DeclarationName Name = C.DeclarationNames.getCXXOperatorName(OO_Equal); + DeclContext::lookup_const_iterator Op, OpEnd; + for (llvm::tie(Op, OpEnd) = RD->lookup(Name); + Op != OpEnd; ++Op) { + CXXMethodDecl *Operator = cast<CXXMethodDecl>(*Op); + if (Operator->isCopyAssignmentOperator()) { + FoundAssign = true; + const FunctionProtoType *CPT + = Operator->getType()->getAs<FunctionProtoType>(); + if (!CPT->hasEmptyExceptionSpec()) { + AllNoThrow = false; + break; + } + } + } + + return FoundAssign && AllNoThrow; + } + return false; + case UTT_HasNothrowCopy: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __has_trivial_copy (type) is true then the trait is true, else + // if type is a cv class or union type with copy constructors that are + // known not to throw an exception then the trait is true, else it is + // false. + if (T->isPODType() || T->isReferenceType()) + return true; + if (const RecordType *RT = T->getAs<RecordType>()) { + CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); + if (RD->hasTrivialCopyConstructor()) + return true; + + bool FoundConstructor = false; + bool AllNoThrow = true; + unsigned FoundTQs; + DeclarationName ConstructorName + = C.DeclarationNames.getCXXConstructorName(C.getCanonicalType(T)); + DeclContext::lookup_const_iterator Con, ConEnd; + for (llvm::tie(Con, ConEnd) = RD->lookup(ConstructorName); + Con != ConEnd; ++Con) { + CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con); + if (Constructor->isCopyConstructor(FoundTQs)) { + FoundConstructor = true; + const FunctionProtoType *CPT + = Constructor->getType()->getAs<FunctionProtoType>(); + if (!CPT->hasEmptyExceptionSpec()) { + AllNoThrow = false; + break; + } + } + } + + return FoundConstructor && AllNoThrow; + } + return false; + case UTT_HasNothrowConstructor: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If __has_trivial_constructor (type) is true then the trait is + // true, else if type is a cv class or union type (or array + // thereof) with a default constructor that is known not to + // throw an exception then the trait is true, else it is false. + if (T->isPODType()) + return true; + if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>()) { + CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); + if (RD->hasTrivialConstructor()) + return true; + + if (CXXConstructorDecl *Constructor = RD->getDefaultConstructor()) { + const FunctionProtoType *CPT + = Constructor->getType()->getAs<FunctionProtoType>(); + // TODO: check whether evaluating default arguments can throw. + // For now, we'll be conservative and assume that they can throw. + if (CPT->hasEmptyExceptionSpec() && CPT->getNumArgs() == 0) + return true; + } + } + return false; + case UTT_HasVirtualDestructor: + // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: + // If type is a class type with a virtual destructor ([class.dtor]) + // then the trait is true, else it is false. + if (const RecordType *Record = T->getAs<RecordType>()) { + CXXRecordDecl *RD = cast<CXXRecordDecl>(Record->getDecl()); + if (CXXDestructorDecl *Destructor = RD->getDestructor()) + return Destructor->isVirtual(); + } + return false; + } +} + +ExprResult Sema::BuildUnaryTypeTrait(UnaryTypeTrait UTT, SourceLocation KWLoc, TypeSourceInfo *TSInfo, SourceLocation RParen) { @@ -2010,7 +2210,7 @@ ExprResult Sema::BuildUnaryTypeTrait(UnaryTypeTrait OTT, // According to http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html // all traits except __is_class, __is_enum and __is_union require a the type // to be complete, an array of unknown bound, or void. - if (OTT != UTT_IsClass && OTT != UTT_IsEnum && OTT != UTT_IsUnion) { + if (UTT != UTT_IsClass && UTT != UTT_IsEnum && UTT != UTT_IsUnion) { QualType E = T; if (T->isIncompleteArrayType()) E = Context.getAsArrayType(T)->getElementType(); @@ -2020,10 +2220,11 @@ ExprResult Sema::BuildUnaryTypeTrait(UnaryTypeTrait OTT, return ExprError(); } - // 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 (Context) UnaryTypeTraitExpr(KWLoc, OTT, TSInfo, + bool Value = false; + if (!T->isDependentType()) + Value = EvaluateUnaryTypeTrait(*this, UTT, T); + + return Owned(new (Context) UnaryTypeTraitExpr(KWLoc, UTT, TSInfo, Value, RParen, Context.BoolTy)); } |