diff options
author | Douglas Gregor <dgregor@apple.com> | 2012-02-23 07:33:15 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2012-02-23 07:33:15 +0000 |
commit | 25d0a0f67d9e949ffbfc57bf487012f5cbfd886e (patch) | |
tree | 2c7d3cf3e2b66d16d367e5ac545593d970f89621 | |
parent | 46e021e897b8319b192115954f0b2c82fe0e504d (diff) |
Provide the __is_trivially_assignable type trait, which provides
compiler support for the std::is_trivially_assignable library type
trait.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151240 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/Expr.h | 4 | ||||
-rw-r--r-- | include/clang/Basic/TokenKinds.def | 1 | ||||
-rw-r--r-- | include/clang/Basic/TypeTraits.h | 3 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 55 | ||||
-rw-r--r-- | lib/AST/StmtPrinter.cpp | 11 | ||||
-rw-r--r-- | lib/Lex/PPMacroExpansion.cpp | 1 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 1 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 1 | ||||
-rw-r--r-- | lib/Parse/ParseTentative.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 49 | ||||
-rw-r--r-- | test/SemaCXX/type-traits.cpp | 41 |
11 files changed, 162 insertions, 6 deletions
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index d66ad8fbca..d943aba0d3 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -512,6 +512,10 @@ public: /// variable read. bool HasSideEffects(const ASTContext &Ctx) const; + /// \brief Determine whether this expression involves a call to any function + /// that is not trivial. + bool hasNonTrivialCall(ASTContext &Ctx); + /// EvaluateKnownConstInt - Call EvaluateAsRValue and return the folded /// integer. This must be called on an expression that constant folds to an /// integer. diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 5c9708bc71..014e111033 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -366,6 +366,7 @@ KEYWORD(__is_union , KEYCXX) // Clang-only C++ Type Traits KEYWORD(__is_trivially_copyable , KEYCXX) +KEYWORD(__is_trivially_assignable , KEYCXX) KEYWORD(__underlying_type , KEYCXX) // Embarcadero Expression Traits diff --git a/include/clang/Basic/TypeTraits.h b/include/clang/Basic/TypeTraits.h index 3ae56afae5..6ed2adfaf5 100644 --- a/include/clang/Basic/TypeTraits.h +++ b/include/clang/Basic/TypeTraits.h @@ -68,7 +68,8 @@ namespace clang { BTT_IsConvertible, BTT_IsConvertibleTo, BTT_IsSame, - BTT_TypeCompatible + BTT_TypeCompatible, + BTT_IsTriviallyAssignable }; /// ArrayTypeTrait - Names for the array type traits. diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index f95ca17306..326459046b 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -18,6 +18,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" #include "clang/Lex/LiteralSupport.h" @@ -2664,6 +2665,60 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { return isEvaluatable(Ctx); } +namespace { + /// \brief Look for a call to a non-trivial function within an expression. + class NonTrivialCallFinder : public EvaluatedExprVisitor<NonTrivialCallFinder> + { + typedef EvaluatedExprVisitor<NonTrivialCallFinder> Inherited; + + bool NonTrivial; + + public: + explicit NonTrivialCallFinder(ASTContext &Context) + : EvaluatedExprVisitor(Context), NonTrivial(false) { } + + bool hasNonTrivialCall() const { return NonTrivial; } + + void VisitCallExpr(CallExpr *E) { + if (CXXMethodDecl *Method + = dyn_cast_or_null<CXXMethodDecl>(E->getCalleeDecl())) { + if (Method->isTrivial()) { + // Recurse to children of the call. + Inherited::VisitStmt(E); + return; + } + } + + NonTrivial = true; + } + + void VisitCXXConstructExpr(CXXConstructExpr *E) { + if (E->getConstructor()->isTrivial()) { + // Recurse to children of the call. + Inherited::VisitStmt(E); + return; + } + + NonTrivial = true; + } + + void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) { + if (E->getTemporary()->getDestructor()->isTrivial()) { + Inherited::VisitStmt(E); + return; + } + + NonTrivial = true; + } + }; +} + +bool Expr::hasNonTrivialCall(ASTContext &Ctx) { + NonTrivialCallFinder Finder(Ctx); + Finder.Visit(this); + return Finder.hasNonTrivialCall(); +} + /// isNullPointerConstant - C99 6.3.2.3p3 - Return whether this is a null /// pointer constant or not, as well as the specific kind of constant detected. /// Null pointer constants can be integer constant expressions with the diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index c83bfad1c2..51296ad5f4 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1545,11 +1545,12 @@ static const char *getTypeTraitName(UnaryTypeTrait UTT) { static const char *getTypeTraitName(BinaryTypeTrait BTT) { switch (BTT) { - case BTT_IsBaseOf: return "__is_base_of"; - case BTT_IsConvertible: return "__is_convertible"; - case BTT_IsSame: return "__is_same"; - case BTT_TypeCompatible: return "__builtin_types_compatible_p"; - case BTT_IsConvertibleTo: return "__is_convertible_to"; + case BTT_IsBaseOf: return "__is_base_of"; + case BTT_IsConvertible: return "__is_convertible"; + case BTT_IsSame: return "__is_same"; + case BTT_TypeCompatible: return "__builtin_types_compatible_p"; + case BTT_IsConvertibleTo: return "__is_convertible_to"; + case BTT_IsTriviallyAssignable: return "__is_trivially_assignable"; } llvm_unreachable("Binary type trait not covered by switch"); } diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index a9993f97d4..0e00996410 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -700,6 +700,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { != tok::identifier) .Case("is_polymorphic", LangOpts.CPlusPlus) .Case("is_trivial", LangOpts.CPlusPlus) + .Case("is_trivially_assignable", LangOpts.CPlusPlus) .Case("is_trivially_copyable", LangOpts.CPlusPlus) .Case("is_union", LangOpts.CPlusPlus) .Case("modules", LangOpts.Modules) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index d2e8752468..fdf40e877c 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1140,6 +1140,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw___is_same: case tok::kw___is_convertible: case tok::kw___is_convertible_to: + case tok::kw___is_trivially_assignable: return ParseBinaryTypeTrait(); case tok::kw___array_rank: diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index ccd038cb21..19bc22020c 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -2454,6 +2454,7 @@ static BinaryTypeTrait BinaryTypeTraitFromTokKind(tok::TokenKind kind) { case tok::kw___is_same: return BTT_IsSame; case tok::kw___builtin_types_compatible_p: return BTT_TypeCompatible; case tok::kw___is_convertible_to: return BTT_IsConvertibleTo; + case tok::kw___is_trivially_assignable: return BTT_IsTriviallyAssignable; } } diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 35c418cb7e..011c28aaf7 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -689,6 +689,7 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { case tok::kw___is_pod: case tok::kw___is_polymorphic: case tok::kw___is_trivial: + case tok::kw___is_trivially_assignable: case tok::kw___is_trivially_copyable: case tok::kw___is_union: case tok::kw___uuidof: diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 30f143a825..708db1c9a9 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -3301,6 +3301,54 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, BinaryTypeTrait BTT, ExprResult Result = Init.Perform(Self, To, Kind, MultiExprArg(&FromPtr, 1)); return !Result.isInvalid() && !SFINAE.hasErrorOccurred(); } + + case BTT_IsTriviallyAssignable: { + // C++11 [meta.unary.prop]p3: + // is_trivially_assignable is defined as: + // is_assignable<T, U>::value is true and the assignment, as defined by + // is_assignable, is known to call no operation that is not trivial + // + // is_assignable is defined as: + // The expression declval<T>() = declval<U>() is well-formed when + // treated as an unevaluated operand (Clause 5). + // + // For both, T and U shall be complete types, (possibly cv-qualified) + // void, or arrays of unknown bound. + if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType() && + Self.RequireCompleteType(KeyLoc, LhsT, + diag::err_incomplete_type_used_in_type_trait_expr)) + return false; + if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType() && + Self.RequireCompleteType(KeyLoc, RhsT, + diag::err_incomplete_type_used_in_type_trait_expr)) + return false; + + // cv void is never assignable. + if (LhsT->isVoidType() || RhsT->isVoidType()) + return false; + + // Build expressions that emulate the effect of declval<T>() and + // declval<U>(). + if (LhsT->isObjectType() || LhsT->isFunctionType()) + LhsT = Self.Context.getRValueReferenceType(LhsT); + if (RhsT->isObjectType() || RhsT->isFunctionType()) + RhsT = Self.Context.getRValueReferenceType(RhsT); + OpaqueValueExpr Lhs(KeyLoc, LhsT.getNonLValueExprType(Self.Context), + Expr::getValueKindForType(LhsT)); + OpaqueValueExpr Rhs(KeyLoc, RhsT.getNonLValueExprType(Self.Context), + Expr::getValueKindForType(RhsT)); + + // Attempt the assignment in an unevaluated context within a SFINAE + // trap at translation unit scope. + EnterExpressionEvaluationContext Unevaluated(Self, Sema::Unevaluated); + Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true); + Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl()); + ExprResult Result = Self.BuildBinOp(/*S=*/0, KeyLoc, BO_Assign, &Lhs, &Rhs); + if (Result.isInvalid() || SFINAE.hasErrorOccurred()) + return false; + + return !Result.get()->hasNonTrivialCall(Self.Context); + } } llvm_unreachable("Unknown type trait or not implemented"); } @@ -3333,6 +3381,7 @@ ExprResult Sema::BuildBinaryTypeTrait(BinaryTypeTrait BTT, case BTT_IsSame: ResultType = Context.BoolTy; break; case BTT_TypeCompatible: ResultType = Context.IntTy; break; case BTT_IsConvertibleTo: ResultType = Context.BoolTy; break; + case BTT_IsTriviallyAssignable: ResultType = Context.BoolTy; } return Owned(new (Context) BinaryTypeTraitExpr(KWLoc, BTT, LhsTSInfo, diff --git a/test/SemaCXX/type-traits.cpp b/test/SemaCXX/type-traits.cpp index c1783dab28..50c135d0b7 100644 --- a/test/SemaCXX/type-traits.cpp +++ b/test/SemaCXX/type-traits.cpp @@ -39,6 +39,14 @@ struct DerivesEmpty : Empty {}; struct HasCons { HasCons(int); }; struct HasCopyAssign { HasCopyAssign operator =(const HasCopyAssign&); }; struct HasMoveAssign { HasMoveAssign operator =(const HasMoveAssign&&); }; +struct HasDefaultTrivialCopyAssign { + HasDefaultTrivialCopyAssign &operator =(const HasDefaultTrivialCopyAssign&) + = default; +}; +struct TrivialMoveButNotCopy { + TrivialMoveButNotCopy &operator=(TrivialMoveButNotCopy&&) = default; + TrivialMoveButNotCopy &operator=(const TrivialMoveButNotCopy&); +}; struct HasDest { ~HasDest(); }; class HasPriv { int priv; }; class HasProt { protected: int prot; }; @@ -1637,6 +1645,39 @@ void is_trivially_copyable() { int arr[F(__is_trivially_copyable(DerivesHasVirt))]; } { int arr[F(__is_trivially_copyable(void))]; } { int arr[F(__is_trivially_copyable(cvoid))]; } + + { int arr[T((__is_trivially_assignable(int&, int)))]; } + { int arr[T((__is_trivially_assignable(int&, int&)))]; } + { int arr[T((__is_trivially_assignable(int&, int&&)))]; } + { int arr[T((__is_trivially_assignable(int&, const int&)))]; } + { int arr[T((__is_trivially_assignable(POD&, POD)))]; } + { int arr[T((__is_trivially_assignable(POD&, POD&)))]; } + { int arr[T((__is_trivially_assignable(POD&, POD&&)))]; } + { int arr[T((__is_trivially_assignable(POD&, const POD&)))]; } + { int arr[T((__is_trivially_assignable(int*&, int*)))]; } + + { int arr[F((__is_trivially_assignable(int*&, float*)))]; } + { int arr[F((__is_trivially_assignable(HasCopyAssign&, HasCopyAssign)))]; } + { int arr[F((__is_trivially_assignable(HasCopyAssign&, HasCopyAssign&)))]; } + { int arr[F((__is_trivially_assignable(HasCopyAssign&, const HasCopyAssign&)))]; } + { int arr[F((__is_trivially_assignable(HasCopyAssign&, HasCopyAssign&&)))]; } + { int arr[F((__is_trivially_assignable(TrivialMoveButNotCopy&, + TrivialMoveButNotCopy&)))]; } + { int arr[F((__is_trivially_assignable(TrivialMoveButNotCopy&, + const TrivialMoveButNotCopy&)))]; } + + // FIXME: The following answers are wrong, because we don't properly + // mark user-declared constructors/assignment operators/destructors + // that are defaulted on their first declaration as trivial when we + // can. + { int arr[F((__is_trivially_assignable(HasDefaultTrivialCopyAssign&, + HasDefaultTrivialCopyAssign&)))]; } + { int arr[F((__is_trivially_assignable(HasDefaultTrivialCopyAssign&, + const HasDefaultTrivialCopyAssign&)))]; } + { int arr[F((__is_trivially_assignable(TrivialMoveButNotCopy&, + TrivialMoveButNotCopy)))]; } + { int arr[F((__is_trivially_assignable(TrivialMoveButNotCopy&, + TrivialMoveButNotCopy&&)))]; } } void array_rank() { |