Provide the __is_trivially_assignable type trait, which provides

compiler support for the std::is_trivially_assignable library type
trait.

llvm-svn: 151240
This commit is contained in:
Douglas Gregor 2012-02-23 07:33:15 +00:00
parent 64bfb2e88e
commit 1be329d838
11 changed files with 162 additions and 6 deletions

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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");
}

View File

@ -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)

View File

@ -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:

View File

@ -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;
}
}

View File

@ -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:

View File

@ -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,

View File

@ -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() {