aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2012-02-23 07:33:15 +0000
committerDouglas Gregor <dgregor@apple.com>2012-02-23 07:33:15 +0000
commit25d0a0f67d9e949ffbfc57bf487012f5cbfd886e (patch)
tree2c7d3cf3e2b66d16d367e5ac545593d970f89621
parent46e021e897b8319b192115954f0b2c82fe0e504d (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.h4
-rw-r--r--include/clang/Basic/TokenKinds.def1
-rw-r--r--include/clang/Basic/TypeTraits.h3
-rw-r--r--lib/AST/Expr.cpp55
-rw-r--r--lib/AST/StmtPrinter.cpp11
-rw-r--r--lib/Lex/PPMacroExpansion.cpp1
-rw-r--r--lib/Parse/ParseExpr.cpp1
-rw-r--r--lib/Parse/ParseExprCXX.cpp1
-rw-r--r--lib/Parse/ParseTentative.cpp1
-rw-r--r--lib/Sema/SemaExprCXX.cpp49
-rw-r--r--test/SemaCXX/type-traits.cpp41
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() {