From 9ef9875bbe19dc9f73c6c95b803d9a4945168690 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Wed, 27 Mar 2013 01:34:16 +0000 Subject: [PATCH] Implement compiler intrinsics needed for compatibility with MSVC 2012 . Patch by me and Ryan Molden. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@178111 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/TokenKinds.def | 47 ++++++------ include/clang/Basic/TypeTraits.h | 3 + lib/AST/StmtPrinter.cpp | 3 + lib/Parse/ParseExpr.cpp | 3 + lib/Parse/ParseExprCXX.cpp | 4 + lib/Sema/SemaExprCXX.cpp | 107 ++++++++++++++++++-------- test/SemaCXX/type-traits.cpp | 119 ++++++++++++++++++++++++++++- 7 files changed, 230 insertions(+), 56 deletions(-) diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 6064fc1ff9..a254faef76 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -351,31 +351,34 @@ KEYWORD(typeof , KEYGNU) KEYWORD(L__FUNCTION__ , KEYMS) // GNU and MS Type Traits -KEYWORD(__has_nothrow_assign , KEYCXX) -KEYWORD(__has_nothrow_copy , KEYCXX) -KEYWORD(__has_nothrow_constructor , KEYCXX) -KEYWORD(__has_trivial_assign , KEYCXX) -KEYWORD(__has_trivial_copy , KEYCXX) -KEYWORD(__has_trivial_constructor , KEYCXX) -KEYWORD(__has_trivial_destructor , KEYCXX) -KEYWORD(__has_virtual_destructor , KEYCXX) -KEYWORD(__is_abstract , KEYCXX) -KEYWORD(__is_base_of , KEYCXX) -KEYWORD(__is_class , KEYCXX) -KEYWORD(__is_convertible_to , KEYCXX) -KEYWORD(__is_empty , KEYCXX) -KEYWORD(__is_enum , KEYCXX) -KEYWORD(__is_final , KEYCXX) -KEYWORD(__is_interface_class , KEYCXX) +KEYWORD(__has_nothrow_assign , KEYCXX) +KEYWORD(__has_nothrow_move_assign , KEYCXX) +KEYWORD(__has_nothrow_copy , KEYCXX) +KEYWORD(__has_nothrow_constructor , KEYCXX) +KEYWORD(__has_trivial_assign , KEYCXX) +KEYWORD(__has_trivial_move_assign , KEYCXX) +KEYWORD(__has_trivial_copy , KEYCXX) +KEYWORD(__has_trivial_constructor , KEYCXX) +KEYWORD(__has_trivial_move_constructor, KEYCXX) +KEYWORD(__has_trivial_destructor , KEYCXX) +KEYWORD(__has_virtual_destructor , KEYCXX) +KEYWORD(__is_abstract , KEYCXX) +KEYWORD(__is_base_of , KEYCXX) +KEYWORD(__is_class , KEYCXX) +KEYWORD(__is_convertible_to , KEYCXX) +KEYWORD(__is_empty , KEYCXX) +KEYWORD(__is_enum , KEYCXX) +KEYWORD(__is_final , KEYCXX) +KEYWORD(__is_interface_class , KEYCXX) // Tentative name - there's no implementation of std::is_literal_type yet. -KEYWORD(__is_literal , KEYCXX) +KEYWORD(__is_literal , KEYCXX) // Name for GCC 4.6 compatibility - people have already written libraries using // this name unfortunately. -KEYWORD(__is_literal_type , KEYCXX) -KEYWORD(__is_pod , KEYCXX) -KEYWORD(__is_polymorphic , KEYCXX) -KEYWORD(__is_trivial , KEYCXX) -KEYWORD(__is_union , KEYCXX) +KEYWORD(__is_literal_type , KEYCXX) +KEYWORD(__is_pod , KEYCXX) +KEYWORD(__is_polymorphic , KEYCXX) +KEYWORD(__is_trivial , KEYCXX) +KEYWORD(__is_union , KEYCXX) // Clang-only C++ Type Traits KEYWORD(__is_trivially_constructible, KEYCXX) diff --git a/include/clang/Basic/TypeTraits.h b/include/clang/Basic/TypeTraits.h index 882b52d489..1645796986 100644 --- a/include/clang/Basic/TypeTraits.h +++ b/include/clang/Basic/TypeTraits.h @@ -20,11 +20,14 @@ namespace clang { /// \brief Names for the unary type traits. enum UnaryTypeTrait { UTT_HasNothrowAssign, + UTT_HasNothrowMoveAssign, UTT_HasNothrowCopy, UTT_HasNothrowConstructor, UTT_HasTrivialAssign, + UTT_HasTrivialMoveAssign, UTT_HasTrivialCopy, UTT_HasTrivialDefaultConstructor, + UTT_HasTrivialMoveConstructor, UTT_HasTrivialDestructor, UTT_HasVirtualDestructor, UTT_IsAbstract, diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 23506b5e26..7df7fdb92b 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1558,9 +1558,12 @@ void StmtPrinter::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *Node) { static const char *getTypeTraitName(UnaryTypeTrait UTT) { switch (UTT) { case UTT_HasNothrowAssign: return "__has_nothrow_assign"; + case UTT_HasNothrowMoveAssign: return "__has_nothrow_move_assign"; case UTT_HasNothrowConstructor: return "__has_nothrow_constructor"; case UTT_HasNothrowCopy: return "__has_nothrow_copy"; case UTT_HasTrivialAssign: return "__has_trivial_assign"; + case UTT_HasTrivialMoveAssign: return "__has_trivial_move_assign"; + case UTT_HasTrivialMoveConstructor: return "__has_trivial_move_constructor"; case UTT_HasTrivialDefaultConstructor: return "__has_trivial_constructor"; case UTT_HasTrivialCopy: return "__has_trivial_copy"; case UTT_HasTrivialDestructor: return "__has_trivial_destructor"; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 7775909cf5..4bb2bf2eba 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1188,10 +1188,13 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw___is_union: case tok::kw___is_final: case tok::kw___has_trivial_constructor: + case tok::kw___has_trivial_move_constructor: case tok::kw___has_trivial_copy: case tok::kw___has_trivial_assign: + case tok::kw___has_trivial_move_assign: case tok::kw___has_trivial_destructor: case tok::kw___has_nothrow_assign: + case tok::kw___has_nothrow_move_assign: case tok::kw___has_nothrow_copy: case tok::kw___has_nothrow_constructor: case tok::kw___has_virtual_destructor: diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 22938afede..17c4adf7d7 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -2511,11 +2511,15 @@ static UnaryTypeTrait UnaryTypeTraitFromTokKind(tok::TokenKind kind) { switch(kind) { default: llvm_unreachable("Not a known unary type trait."); case tok::kw___has_nothrow_assign: return UTT_HasNothrowAssign; + case tok::kw___has_nothrow_move_assign: return UTT_HasNothrowMoveAssign; case tok::kw___has_nothrow_constructor: return UTT_HasNothrowConstructor; case tok::kw___has_nothrow_copy: return UTT_HasNothrowCopy; case tok::kw___has_trivial_assign: return UTT_HasTrivialAssign; + case tok::kw___has_trivial_move_assign: return UTT_HasTrivialMoveAssign; case tok::kw___has_trivial_constructor: return UTT_HasTrivialDefaultConstructor; + case tok::kw___has_trivial_move_constructor: + return UTT_HasTrivialMoveConstructor; case tok::kw___has_trivial_copy: return UTT_HasTrivialCopy; case tok::kw___has_trivial_destructor: return UTT_HasTrivialDestructor; case tok::kw___has_virtual_destructor: return UTT_HasVirtualDestructor; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1724624fe3..b586fd70b0 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2975,10 +2975,13 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, // type due to the overarching C++0x type predicates being implemented // requiring the complete type. case UTT_HasNothrowAssign: + case UTT_HasNothrowMoveAssign: case UTT_HasNothrowConstructor: case UTT_HasNothrowCopy: case UTT_HasTrivialAssign: + case UTT_HasTrivialMoveAssign: case UTT_HasTrivialDefaultConstructor: + case UTT_HasTrivialMoveConstructor: case UTT_HasTrivialCopy: case UTT_HasTrivialDestructor: case UTT_HasVirtualDestructor: @@ -2997,6 +3000,42 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, llvm_unreachable("Type trait not handled by switch"); } +static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op, + Sema &Self, SourceLocation KeyLoc, ASTContext &C, + bool (CXXRecordDecl::*HasTrivial)() const, + bool (CXXRecordDecl::*HasNonTrivial)() const, + bool (CXXMethodDecl::*IsDesiredOp)() const) +{ + CXXRecordDecl *RD = cast(RT->getDecl()); + if ((RD->*HasTrivial)() && !(RD->*HasNonTrivial)()) + return true; + + DeclarationName Name = C.DeclarationNames.getCXXOperatorName(Op); + DeclarationNameInfo NameInfo(Name, KeyLoc); + LookupResult Res(Self, NameInfo, Sema::LookupOrdinaryName); + if (Self.LookupQualifiedName(Res, RD)) { + bool FoundOperator = false; + Res.suppressDiagnostics(); + for (LookupResult::iterator Op = Res.begin(), OpEnd = Res.end(); + Op != OpEnd; ++Op) { + if (isa(*Op)) + continue; + + CXXMethodDecl *Operator = cast(*Op); + if((Operator->*IsDesiredOp)()) { + FoundOperator = true; + const FunctionProtoType *CPT = + Operator->getType()->getAs(); + CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); + if (!CPT || !CPT->isNothrow(Self.Context)) + return false; + } + } + return FoundOperator; + } + return false; +} + static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, SourceLocation KeyLoc, QualType T) { assert(!T->isDependentType() && "Cannot evaluate traits of dependent type"); @@ -3133,6 +3172,15 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, return RD->hasTrivialDefaultConstructor() && !RD->hasNonTrivialDefaultConstructor(); return false; + case UTT_HasTrivialMoveConstructor: + // This trait is implemented by MSVC 2012 and needed to parse the + // standard library headers. Specifically this is used as the logic + // behind std::is_trivially_move_constructible (20.9.4.3). + if (T.isPODType(Self.Context)) + return true; + if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) + return RD->hasTrivialMoveConstructor() && !RD->hasNonTrivialMoveConstructor(); + 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 @@ -3145,6 +3193,15 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, return RD->hasTrivialCopyConstructor() && !RD->hasNonTrivialCopyConstructor(); return false; + case UTT_HasTrivialMoveAssign: + // This trait is implemented by MSVC 2012 and needed to parse the + // standard library headers. Specifically it is used as the logic + // behind std::is_trivially_move_assignable (20.9.4.3) + if (T.isPODType(Self.Context)) + return true; + if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) + return RD->hasTrivialMoveAssignment() && !RD->hasNonTrivialMoveAssignment(); + 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 @@ -3198,38 +3255,26 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, if (T->isReferenceType()) return false; if (T.isPODType(Self.Context) || T->isObjCLifetimeType()) - return true; - if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) { - if (RD->hasTrivialCopyAssignment() && !RD->hasNonTrivialCopyAssignment()) - return true; + return true; - bool FoundAssign = false; - DeclarationName Name = C.DeclarationNames.getCXXOperatorName(OO_Equal); - LookupResult Res(Self, DeclarationNameInfo(Name, KeyLoc), - Sema::LookupOrdinaryName); - if (Self.LookupQualifiedName(Res, RD)) { - Res.suppressDiagnostics(); - for (LookupResult::iterator Op = Res.begin(), OpEnd = Res.end(); - Op != OpEnd; ++Op) { - if (isa(*Op)) - continue; - - CXXMethodDecl *Operator = cast(*Op); - if (Operator->isCopyAssignmentOperator()) { - FoundAssign = true; - const FunctionProtoType *CPT - = Operator->getType()->getAs(); - CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); - if (!CPT) - return false; - if (!CPT->isNothrow(Self.Context)) - return false; - } - } - } - - return FoundAssign; - } + if (const RecordType *RT = T->getAs()) + return HasNoThrowOperator(RT, OO_Equal, Self, KeyLoc, C, + &CXXRecordDecl::hasTrivialCopyAssignment, + &CXXRecordDecl::hasNonTrivialCopyAssignment, + &CXXMethodDecl::isCopyAssignmentOperator); + return false; + case UTT_HasNothrowMoveAssign: + // This trait is implemented by MSVC 2012 and needed to parse the + // standard library headers. Specifically this is used as the logic + // behind std::is_nothrow_move_assignable (20.9.4.3). + if (T.isPODType(Self.Context)) + return true; + + if (const RecordType *RT = C.getBaseElementType(T)->getAs()) + return HasNoThrowOperator(RT, OO_Equal, Self, KeyLoc, C, + &CXXRecordDecl::hasTrivialMoveAssignment, + &CXXRecordDecl::hasNonTrivialMoveAssignment, + &CXXMethodDecl::isMoveAssignmentOperator); return false; case UTT_HasNothrowCopy: // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: diff --git a/test/SemaCXX/type-traits.cpp b/test/SemaCXX/type-traits.cpp index 4b6c44d257..aa18ff4e67 100644 --- a/test/SemaCXX/type-traits.cpp +++ b/test/SemaCXX/type-traits.cpp @@ -39,9 +39,34 @@ struct DerivesEmpty : Empty {}; struct HasCons { HasCons(int); }; struct HasCopyAssign { HasCopyAssign operator =(const HasCopyAssign&); }; struct HasMoveAssign { HasMoveAssign operator =(const HasMoveAssign&&); }; +struct HasNoThrowMoveAssign { + HasNoThrowMoveAssign& operator=( + const HasNoThrowMoveAssign&&) throw(); }; +struct HasNoExceptNoThrowMoveAssign { + HasNoExceptNoThrowMoveAssign& operator=( + const HasNoExceptNoThrowMoveAssign&&) noexcept; +}; +struct HasThrowMoveAssign { + HasThrowMoveAssign& operator=( + const HasThrowMoveAssign&&) throw(POD); }; +struct HasNoExceptFalseMoveAssign { + HasNoExceptFalseMoveAssign& operator=( + const HasNoExceptFalseMoveAssign&&) noexcept(false); }; +struct HasMoveCtor { HasMoveCtor(const HasMoveCtor&&); }; +struct HasMemberMoveCtor { HasMoveCtor member; }; +struct HasMemberMoveAssign { HasMoveAssign member; }; +struct HasStaticMemberMoveCtor { static HasMoveCtor member; }; +struct HasStaticMemberMoveAssign { static HasMoveAssign member; }; +struct HasMemberThrowMoveAssign { HasThrowMoveAssign member; }; +struct HasMemberNoExceptFalseMoveAssign { + HasNoExceptFalseMoveAssign member; }; +struct HasMemberNoThrowMoveAssign { HasNoThrowMoveAssign member; }; +struct HasMemberNoExceptNoThrowMoveAssign { + HasNoExceptNoThrowMoveAssign member; }; + struct HasDefaultTrivialCopyAssign { - HasDefaultTrivialCopyAssign &operator =(const HasDefaultTrivialCopyAssign&) - = default; + HasDefaultTrivialCopyAssign &operator=( + const HasDefaultTrivialCopyAssign&) = default; }; struct TrivialMoveButNotCopy { TrivialMoveButNotCopy &operator=(TrivialMoveButNotCopy&&) = default; @@ -69,6 +94,7 @@ struct DerivesHasPriv : HasPriv {}; struct DerivesHasProt : HasProt {}; struct DerivesHasRef : HasRef {}; struct DerivesHasVirt : HasVirt {}; +struct DerivesHasMoveCtor : HasMoveCtor {}; struct HasNoThrowCopyAssign { void operator =(const HasNoThrowCopyAssign&) throw(); @@ -165,7 +191,7 @@ typedef Empty EmptyAr[10]; struct Bit0 { int : 0; }; struct Bit0Cons { int : 0; Bit0Cons(); }; struct BitOnly { int x : 3; }; -//struct DerivesVirt : virtual POD {}; +struct DerivesVirt : virtual POD {}; void is_empty() { @@ -941,6 +967,19 @@ struct AllDefaulted { ~AllDefaulted() = default; }; +struct NoDefaultMoveAssignDueToUDCopyCtor { + NoDefaultMoveAssignDueToUDCopyCtor(const NoDefaultMoveAssignDueToUDCopyCtor&); +}; + +struct NoDefaultMoveAssignDueToUDCopyAssign { + NoDefaultMoveAssignDueToUDCopyAssign& operator=( + const NoDefaultMoveAssignDueToUDCopyAssign&); +}; + +struct NoDefaultMoveAssignDueToDtor { + ~NoDefaultMoveAssignDueToDtor(); +}; + struct AllDeleted { AllDeleted() = delete; AllDeleted(const AllDeleted &) = delete; @@ -1203,6 +1242,32 @@ void has_trivial_default_constructor() { { int arr[F(__has_trivial_constructor(ExtDefaulted))]; } } +void has_trivial_move_constructor() { + // n3376 12.8 [class.copy]/12 + // A copy/move constructor for class X is trivial if it is not + // user-provided, its declared parameter type is the same as + // if it had been implicitly declared, and if + // — class X has no virtual functions (10.3) and no virtual + // base classes (10.1), and + // — the constructor selected to copy/move each direct base + // class subobject is trivial, and + // — for each non-static data member of X that is of class + // type (or array thereof), the constructor selected + // to copy/move that member is trivial; + // otherwise the copy/move constructor is non-trivial. + { int arr[T(__has_trivial_move_constructor(POD))]; } + { int arr[T(__has_trivial_move_constructor(Union))]; } + { int arr[T(__has_trivial_move_constructor(HasCons))]; } + { int arr[T(__has_trivial_move_constructor(HasStaticMemberMoveCtor))]; } + { int arr[T(__has_trivial_move_constructor(AllDeleted))]; } + + { int arr[F(__has_trivial_move_constructor(HasVirt))]; } + { int arr[F(__has_trivial_move_constructor(DerivesVirt))]; } + { int arr[F(__has_trivial_move_constructor(HasMoveCtor))]; } + { int arr[F(__has_trivial_move_constructor(DerivesHasMoveCtor))]; } + { int arr[F(__has_trivial_move_constructor(HasMemberMoveCtor))]; } +} + void has_trivial_copy_constructor() { { int arr[T(__has_trivial_copy(Int))]; } { int arr[T(__has_trivial_copy(IntAr))]; } @@ -1355,6 +1420,54 @@ void has_nothrow_assign() { { int arr[F(__has_nothrow_assign(PR11110))]; } } +void has_nothrow_move_assign() { + { int arr[T(__has_nothrow_move_assign(Int))]; } + { int arr[T(__has_nothrow_move_assign(Enum))]; } + { int arr[T(__has_nothrow_move_assign(Int*))]; } + { int arr[T(__has_nothrow_move_assign(Enum POD::*))]; } + { int arr[T(__has_nothrow_move_assign(POD))]; } + { int arr[T(__has_nothrow_move_assign(HasPriv))]; } + { int arr[T(__has_nothrow_move_assign(HasNoThrowMoveAssign))]; } + { int arr[T(__has_nothrow_move_assign(HasNoExceptNoThrowMoveAssign))]; } + { int arr[T(__has_nothrow_move_assign(HasMemberNoThrowMoveAssign))]; } + { int arr[T(__has_nothrow_move_assign(HasMemberNoExceptNoThrowMoveAssign))]; } + { int arr[T(__has_nothrow_move_assign(AllDeleted))]; } + + + { int arr[F(__has_nothrow_move_assign(HasThrowMoveAssign))]; } + { int arr[F(__has_nothrow_move_assign(HasNoExceptFalseMoveAssign))]; } + { int arr[F(__has_nothrow_move_assign(HasMemberThrowMoveAssign))]; } + { int arr[F(__has_nothrow_move_assign(HasMemberNoExceptFalseMoveAssign))]; } + { int arr[F(__has_nothrow_move_assign(NoDefaultMoveAssignDueToUDCopyCtor))]; } + { int arr[F(__has_nothrow_move_assign(NoDefaultMoveAssignDueToUDCopyAssign))]; } + { int arr[F(__has_nothrow_move_assign(NoDefaultMoveAssignDueToDtor))]; } +} + +void has_trivial_move_assign() { + // n3376 12.8 [class.copy]/25 + // A copy/move assignment operator for class X is trivial if it + // is not user-provided, its declared parameter type is the same + // as if it had been implicitly declared, and if: + // — class X has no virtual functions (10.3) and no virtual base + // classes (10.1), and + // — the assignment operator selected to copy/move each direct + // base class subobject is trivial, and + // — for each non-static data member of X that is of class type + // (or array thereof), the assignment operator + // selected to copy/move that member is trivial; + { int arr[T(__has_trivial_move_assign(Int))]; } + { int arr[T(__has_trivial_move_assign(HasStaticMemberMoveAssign))]; } + { int arr[T(__has_trivial_move_assign(AllDeleted))]; } + + { int arr[F(__has_trivial_move_assign(HasVirt))]; } + { int arr[F(__has_trivial_move_assign(DerivesVirt))]; } + { int arr[F(__has_trivial_move_assign(HasMoveAssign))]; } + { int arr[F(__has_trivial_move_assign(DerivesHasMoveAssign))]; } + { int arr[F(__has_trivial_move_assign(HasMemberMoveAssign))]; } + { int arr[F(__has_nothrow_move_assign(NoDefaultMoveAssignDueToUDCopyCtor))]; } + { int arr[F(__has_nothrow_move_assign(NoDefaultMoveAssignDueToUDCopyAssign))]; } +} + void has_nothrow_copy() { { int arr[T(__has_nothrow_copy(Int))]; } { int arr[T(__has_nothrow_copy(IntAr))]; } -- 2.40.0