From: Richard Smith Date: Thu, 23 May 2013 00:30:41 +0000 (+0000) Subject: PR14772: Support constant expression evaluation for _Atomic types. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5705f211472f19fc38e58d81365f9261024b3ba3;p=clang PR14772: Support constant expression evaluation for _Atomic types. * Treat _Atomic(T) as a literal type if T is a literal type. * Evaluate expressions of this type properly. * Fix a lurking bug where we built completely bogus ASTs for converting to _Atomic types in C++ in some cases, caught by the tests for this change. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@182541 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index fbef525ce4..ade258b96d 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -915,6 +915,7 @@ static bool EvaluateIntegerOrLValue(const Expr *E, APValue &Result, EvalInfo &Info); static bool EvaluateFloat(const Expr *E, APFloat &Result, EvalInfo &Info); static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info); +static bool EvaluateAtomic(const Expr *E, APValue &Result, EvalInfo &Info); //===----------------------------------------------------------------------===// // Misc utilities @@ -3702,8 +3703,13 @@ public: default: break; - case CK_AtomicToNonAtomic: - case CK_NonAtomicToAtomic: + case CK_AtomicToNonAtomic: { + APValue AtomicVal; + if (!EvaluateAtomic(E->getSubExpr(), AtomicVal, Info)) + return false; + return DerivedSuccess(AtomicVal, E); + } + case CK_NoOp: case CK_UserDefinedConversion: return StmtVisitorTy::Visit(E->getSubExpr()); @@ -6469,6 +6475,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_IntegralComplexToFloatingComplex: case CK_BuiltinFnToFnPtr: case CK_ZeroToOCLEvent: + case CK_NonAtomicToAtomic: llvm_unreachable("invalid cast kind for integral value"); case CK_BitCast: @@ -6484,7 +6491,6 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_UserDefinedConversion: case CK_LValueToRValue: case CK_AtomicToNonAtomic: - case CK_NonAtomicToAtomic: case CK_NoOp: return ExprEvaluatorBaseTy::VisitCastExpr(E); @@ -6937,11 +6943,11 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_CopyAndAutoreleaseBlockObject: case CK_BuiltinFnToFnPtr: case CK_ZeroToOCLEvent: + case CK_NonAtomicToAtomic: llvm_unreachable("invalid cast kind for complex value"); case CK_LValueToRValue: case CK_AtomicToNonAtomic: - case CK_NonAtomicToAtomic: case CK_NoOp: return ExprEvaluatorBaseTy::VisitCastExpr(E); @@ -7197,6 +7203,46 @@ bool ComplexExprEvaluator::VisitInitListExpr(const InitListExpr *E) { return ExprEvaluatorBaseTy::VisitInitListExpr(E); } +//===----------------------------------------------------------------------===// +// Atomic expression evaluation, essentially just handling the NonAtomicToAtomic +// implicit conversion. +//===----------------------------------------------------------------------===// + +namespace { +class AtomicExprEvaluator : + public ExprEvaluatorBase { + APValue &Result; +public: + AtomicExprEvaluator(EvalInfo &Info, APValue &Result) + : ExprEvaluatorBaseTy(Info), Result(Result) {} + + bool Success(const APValue &V, const Expr *E) { + Result = V; + return true; + } + + bool ZeroInitialization(const Expr *E) { + ImplicitValueInitExpr VIE( + E->getType()->castAs()->getValueType()); + return Evaluate(Result, Info, &VIE); + } + + bool VisitCastExpr(const CastExpr *E) { + switch (E->getCastKind()) { + default: + return ExprEvaluatorBaseTy::VisitCastExpr(E); + case CK_NonAtomicToAtomic: + return Evaluate(Result, Info, E->getSubExpr()); + } + } +}; +} // end anonymous namespace + +static bool EvaluateAtomic(const Expr *E, APValue &Result, EvalInfo &Info) { + assert(E->isRValue() && E->getType()->isAtomicType()); + return AtomicExprEvaluator(Info, Result).Visit(E); +} + //===----------------------------------------------------------------------===// // Void expression evaluation, primarily for a cast to void on the LHS of a // comma operator @@ -7234,56 +7280,60 @@ static bool EvaluateVoid(const Expr *E, EvalInfo &Info) { static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { // In C, function designators are not lvalues, but we evaluate them as if they // are. - if (E->isGLValue() || E->getType()->isFunctionType()) { + QualType T = E->getType(); + if (E->isGLValue() || T->isFunctionType()) { LValue LV; if (!EvaluateLValue(E, LV, Info)) return false; LV.moveInto(Result); - } else if (E->getType()->isVectorType()) { + } else if (T->isVectorType()) { if (!EvaluateVector(E, Result, Info)) return false; - } else if (E->getType()->isIntegralOrEnumerationType()) { + } else if (T->isIntegralOrEnumerationType()) { if (!IntExprEvaluator(Info, Result).Visit(E)) return false; - } else if (E->getType()->hasPointerRepresentation()) { + } else if (T->hasPointerRepresentation()) { LValue LV; if (!EvaluatePointer(E, LV, Info)) return false; LV.moveInto(Result); - } else if (E->getType()->isRealFloatingType()) { + } else if (T->isRealFloatingType()) { llvm::APFloat F(0.0); if (!EvaluateFloat(E, F, Info)) return false; Result = APValue(F); - } else if (E->getType()->isAnyComplexType()) { + } else if (T->isAnyComplexType()) { ComplexValue C; if (!EvaluateComplex(E, C, Info)) return false; C.moveInto(Result); - } else if (E->getType()->isMemberPointerType()) { + } else if (T->isMemberPointerType()) { MemberPtr P; if (!EvaluateMemberPointer(E, P, Info)) return false; P.moveInto(Result); return true; - } else if (E->getType()->isArrayType()) { + } else if (T->isArrayType()) { LValue LV; LV.set(E, Info.CurrentCall->Index); if (!EvaluateArray(E, LV, Info.CurrentCall->Temporaries[E], Info)) return false; Result = Info.CurrentCall->Temporaries[E]; - } else if (E->getType()->isRecordType()) { + } else if (T->isRecordType()) { LValue LV; LV.set(E, Info.CurrentCall->Index); if (!EvaluateRecord(E, LV, Info.CurrentCall->Temporaries[E], Info)) return false; Result = Info.CurrentCall->Temporaries[E]; - } else if (E->getType()->isVoidType()) { + } else if (T->isVoidType()) { if (!Info.getLangOpts().CPlusPlus11) Info.CCEDiag(E, diag::note_constexpr_nonliteral) << E->getType(); if (!EvaluateVoid(E, Info)) return false; + } else if (T->isAtomicType()) { + if (!EvaluateAtomic(E, Result, Info)) + return false; } else if (Info.getLangOpts().CPlusPlus11) { Info.Diag(E, diag::note_constexpr_nonliteral) << E->getType(); return false; diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 004e31af17..639ae186c1 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1196,6 +1196,10 @@ bool Type::isLiteralType(ASTContext &Ctx) const { return true; } + // We treat _Atomic T as a literal type if T is a literal type. + if (const AtomicType *AT = BaseTy->getAs()) + return AT->getValueType()->isLiteralType(Ctx); + // If this type hasn't been deduced yet, then conservatively assume that // it'll work out to be a literal type. if (isa(BaseTy->getCanonicalTypeInternal())) diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1d2a554ea2..21f00b22fc 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2555,6 +2555,14 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, FromType = From->getType(); } + // If we're converting to an atomic type, first convert to the corresponding + // non-atomic type. + QualType ToAtomicType; + if (const AtomicType *ToAtomic = ToType->getAs()) { + ToAtomicType = ToType; + ToType = ToAtomic->getValueType(); + } + // Perform the first implicit conversion. switch (SCS.First) { case ICK_Identity: @@ -2864,7 +2872,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, << ToType.getNonReferenceType(); break; - } + } default: llvm_unreachable("Improper third standard conversion"); @@ -2872,11 +2880,13 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, // If this conversion sequence involved a scalar -> atomic conversion, perform // that conversion now. - if (const AtomicType *ToAtomic = ToType->getAs()) - if (Context.hasSameType(ToAtomic->getValueType(), From->getType())) - From = ImpCastExprToType(From, ToType, CK_NonAtomicToAtomic, VK_RValue, 0, - CCK).take(); - + if (!ToAtomicType.isNull()) { + assert(Context.hasSameType( + ToAtomicType->castAs()->getValueType(), From->getType())); + From = ImpCastExprToType(From, ToAtomicType, CK_NonAtomicToAtomic, + VK_RValue, 0, CCK).take(); + } + return Owned(From); } diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 97b0b91b99..9beacf887e 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1138,6 +1138,31 @@ namespace ComplexConstexpr { static_assert(&__imag test6 == &__real test6 + 1, ""); } +// _Atomic(T) is exactly like T for the purposes of constant expression +// evaluation.. +namespace Atomic { + constexpr _Atomic int n = 3; + + struct S { _Atomic(double) d; }; + constexpr S s = { 0.5 }; + constexpr double d1 = s.d; + constexpr double d2 = n; + constexpr _Atomic double d3 = n; + + constexpr _Atomic(int) n2 = d3; + static_assert(d1 == 0.5, ""); + static_assert(d3 == 3.0, ""); + + namespace PR16056 { + struct TestVar { + _Atomic(int) value; + constexpr TestVar(int value) : value(value) {} + }; + constexpr TestVar testVar{-1}; + static_assert(testVar.value == -1, ""); + } +} + namespace InstantiateCaseStmt { template constexpr int f() { return x; } template int g(int c) { switch(c) { case f(): return 1; } return 0; }