diff options
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 563 |
1 files changed, 409 insertions, 154 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 42c746e60285..c4b27b5d1daa 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -32,8 +32,6 @@ // //===----------------------------------------------------------------------===// -#include <cstring> -#include <functional> #include "Interp/Context.h" #include "Interp/Frame.h" #include "Interp/State.h" @@ -41,6 +39,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTLambda.h" +#include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CurrentSourceLocExprScope.h" @@ -57,6 +56,8 @@ #include "llvm/ADT/SmallBitVector.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" +#include <cstring> +#include <functional> #define DEBUG_TYPE "exprconstant" @@ -107,7 +108,7 @@ namespace { dyn_cast<MaterializeTemporaryExpr>(Base)) { SmallVector<const Expr *, 2> CommaLHSs; SmallVector<SubobjectAdjustment, 2> Adjustments; - const Expr *Temp = MTE->GetTemporaryExpr(); + const Expr *Temp = MTE->getSubExpr(); const Expr *Inner = Temp->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); // Keep any cv-qualifiers from the reference if we generated a temporary @@ -763,11 +764,8 @@ namespace { /// we will evaluate. unsigned StepsLeft; - /// Force the use of the experimental new constant interpreter, bailing out - /// with an error if a feature is not supported. - bool ForceNewConstInterp; - - /// Enable the experimental new constant interpreter. + /// Enable the experimental new constant interpreter. If an expression is + /// not supported by the interpreter, an error is triggered. bool EnableNewConstInterp; /// BottomFrame - The frame in which evaluation started. This must be @@ -921,10 +919,8 @@ namespace { EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), - StepsLeft(getLangOpts().ConstexprStepLimit), - ForceNewConstInterp(getLangOpts().ForceNewConstInterp), - EnableNewConstInterp(ForceNewConstInterp || - getLangOpts().EnableNewConstInterp), + StepsLeft(C.getLangOpts().ConstexprStepLimit), + EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp), BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), EvaluatingDecl((const ValueDecl *)nullptr), EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), @@ -1039,10 +1035,13 @@ namespace { /// cleanups would have had a side-effect, note that as an unmodeled /// side-effect and return false. Otherwise, return true. bool discardCleanups() { - for (Cleanup &C : CleanupStack) - if (C.hasSideEffect()) - if (!noteSideEffect()) - return false; + for (Cleanup &C : CleanupStack) { + if (C.hasSideEffect() && !noteSideEffect()) { + CleanupStack.clear(); + return false; + } + } + CleanupStack.clear(); return true; } @@ -2072,7 +2071,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc, return false; } - APValue *V = Info.Ctx.getMaterializedTemporaryValue(MTE, false); + APValue *V = MTE->getOrCreateValue(false); assert(V && "evasluation result refers to uninitialised temporary"); if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression, Info, MTE->getExprLoc(), TempType, *V, @@ -3676,7 +3675,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, return CompleteObject(); } - BaseVal = Info.Ctx.getMaterializedTemporaryValue(MTE, false); + BaseVal = MTE->getOrCreateValue(false); assert(BaseVal && "got reference to unevaluated temporary"); } else { if (!IsAccess) @@ -5333,9 +5332,16 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr, if (!FD || FD->getType()->isReferenceType()) break; - // ... and also contains A.B if B names a union member - if (FD->getParent()->isUnion()) - UnionPathLengths.push_back({PathLength - 1, FD}); + // ... and also contains A.B if B names a union member ... + if (FD->getParent()->isUnion()) { + // ... of a non-class, non-array type, or of a class type with a + // trivial default constructor that is not deleted, or an array of + // such types. + auto *RD = + FD->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + if (!RD || RD->hasTrivialDefaultConstructor()) + UnionPathLengths.push_back({PathLength - 1, FD}); + } E = ME->getBase(); --PathLength; @@ -6824,6 +6830,36 @@ public: return StmtVisitorTy::Visit(Source); } + bool VisitPseudoObjectExpr(const PseudoObjectExpr *E) { + for (const Expr *SemE : E->semantics()) { + if (auto *OVE = dyn_cast<OpaqueValueExpr>(SemE)) { + // FIXME: We can't handle the case where an OpaqueValueExpr is also the + // result expression: there could be two different LValues that would + // refer to the same object in that case, and we can't model that. + if (SemE == E->getResultExpr()) + return Error(E); + + // Unique OVEs get evaluated if and when we encounter them when + // emitting the rest of the semantic form, rather than eagerly. + if (OVE->isUnique()) + continue; + + LValue LV; + if (!Evaluate(Info.CurrentCall->createTemporary( + OVE, getStorageType(Info.Ctx, OVE), false, LV), + Info, OVE->getSourceExpr())) + return false; + } else if (SemE == E->getResultExpr()) { + if (!StmtVisitorTy::Visit(SemE)) + return false; + } else { + if (!EvaluateIgnoredValue(Info, SemE)) + return false; + } + } + return true; + } + bool VisitCallExpr(const CallExpr *E) { APValue Result; if (!handleCallExpr(E, Result, nullptr)) @@ -7044,6 +7080,31 @@ public: DerivedSuccess(Result, E); } + bool VisitExtVectorElementExpr(const ExtVectorElementExpr *E) { + APValue Val; + if (!Evaluate(Val, Info, E->getBase())) + return false; + + if (Val.isVector()) { + SmallVector<uint32_t, 4> Indices; + E->getEncodedElementAccess(Indices); + if (Indices.size() == 1) { + // Return scalar. + return DerivedSuccess(Val.getVectorElt(Indices[0]), E); + } else { + // Construct new APValue vector. + SmallVector<APValue, 4> Elts; + for (unsigned I = 0; I < Indices.size(); ++I) { + Elts.push_back(Val.getVectorElt(Indices[I])); + } + APValue VecResult(Elts.data(), Indices.size()); + return DerivedSuccess(VecResult, E); + } + } + + return false; + } + bool VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { default: @@ -7082,6 +7143,13 @@ public: return false; return DerivedSuccess(DestValue, E); } + + case CK_AddressSpaceConversion: { + APValue Value; + if (!Evaluate(Value, Info, E->getSubExpr())) + return false; + return DerivedSuccess(Value, E); + } } return Error(E); @@ -7460,8 +7528,8 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( // Walk through the expression to find the materialized temporary itself. SmallVector<const Expr *, 2> CommaLHSs; SmallVector<SubobjectAdjustment, 2> Adjustments; - const Expr *Inner = E->GetTemporaryExpr()-> - skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); + const Expr *Inner = + E->getSubExpr()->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); // If we passed any comma operators, evaluate their LHSs. for (unsigned I = 0, N = CommaLHSs.size(); I != N; ++I) @@ -7473,7 +7541,7 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr( // value for use outside this evaluation. APValue *Value; if (E->getStorageDuration() == SD_Static) { - Value = Info.Ctx.getMaterializedTemporaryValue(E, true); + Value = E->getOrCreateValue(true); *Value = APValue(); Result.set(E); } else { @@ -7856,6 +7924,11 @@ public: // either copied into the closure object's field that represents the '*this' // or refers to '*this'. if (isLambdaCallOperator(Info.CurrentCall->Callee)) { + // Ensure we actually have captured 'this'. (an error will have + // been previously reported if not). + if (!Info.CurrentCall->LambdaThisCaptureField) + return false; + // Update 'Result' to refer to the data member/field of the closure object // that represents the '*this' capture. if (!HandleLValueMember(Info, E, Result, @@ -8102,6 +8175,42 @@ static CharUnits GetAlignOfExpr(EvalInfo &Info, const Expr *E, return GetAlignOfType(Info, E->getType(), ExprKind); } +static CharUnits getBaseAlignment(EvalInfo &Info, const LValue &Value) { + if (const auto *VD = Value.Base.dyn_cast<const ValueDecl *>()) + return Info.Ctx.getDeclAlign(VD); + if (const auto *E = Value.Base.dyn_cast<const Expr *>()) + return GetAlignOfExpr(Info, E, UETT_AlignOf); + return GetAlignOfType(Info, Value.Base.getTypeInfoType(), UETT_AlignOf); +} + +/// Evaluate the value of the alignment argument to __builtin_align_{up,down}, +/// __builtin_is_aligned and __builtin_assume_aligned. +static bool getAlignmentArgument(const Expr *E, QualType ForType, + EvalInfo &Info, APSInt &Alignment) { + if (!EvaluateInteger(E, Alignment, Info)) + return false; + if (Alignment < 0 || !Alignment.isPowerOf2()) { + Info.FFDiag(E, diag::note_constexpr_invalid_alignment) << Alignment; + return false; + } + unsigned SrcWidth = Info.Ctx.getIntWidth(ForType); + APSInt MaxValue(APInt::getOneBitSet(SrcWidth, SrcWidth - 1)); + if (APSInt::compareValues(Alignment, MaxValue) > 0) { + Info.FFDiag(E, diag::note_constexpr_alignment_too_big) + << MaxValue << ForType << Alignment; + return false; + } + // Ensure both alignment and source value have the same bit width so that we + // don't assert when computing the resulting value. + APSInt ExtAlignment = + APSInt(Alignment.zextOrTrunc(SrcWidth), /*isUnsigned=*/true); + assert(APSInt::compareValues(Alignment, ExtAlignment) == 0 && + "Alignment should not be changed by ext/trunc"); + Alignment = ExtAlignment; + assert(Alignment.getBitWidth() == SrcWidth); + return true; +} + // To be clear: this happily visits unsupported builtins. Better name welcomed. bool PointerExprEvaluator::visitNonBuiltinCallExpr(const CallExpr *E) { if (ExprEvaluatorBaseTy::VisitCallExpr(E)) @@ -8140,7 +8249,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, LValue OffsetResult(Result); APSInt Alignment; - if (!EvaluateInteger(E->getArg(1), Alignment, Info)) + if (!getAlignmentArgument(E->getArg(1), E->getArg(0)->getType(), Info, + Alignment)) return false; CharUnits Align = CharUnits::fromQuantity(Alignment.getZExtValue()); @@ -8155,16 +8265,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, // If there is a base object, then it must have the correct alignment. if (OffsetResult.Base) { - CharUnits BaseAlignment; - if (const ValueDecl *VD = - OffsetResult.Base.dyn_cast<const ValueDecl*>()) { - BaseAlignment = Info.Ctx.getDeclAlign(VD); - } else if (const Expr *E = OffsetResult.Base.dyn_cast<const Expr *>()) { - BaseAlignment = GetAlignOfExpr(Info, E, UETT_AlignOf); - } else { - BaseAlignment = GetAlignOfType( - Info, OffsetResult.Base.getTypeInfoType(), UETT_AlignOf); - } + CharUnits BaseAlignment = getBaseAlignment(Info, OffsetResult); if (BaseAlignment < Align) { Result.Designator.setInvalid(); @@ -8193,6 +8294,43 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return true; } + case Builtin::BI__builtin_align_up: + case Builtin::BI__builtin_align_down: { + if (!evaluatePointer(E->getArg(0), Result)) + return false; + APSInt Alignment; + if (!getAlignmentArgument(E->getArg(1), E->getArg(0)->getType(), Info, + Alignment)) + return false; + CharUnits BaseAlignment = getBaseAlignment(Info, Result); + CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Result.Offset); + // For align_up/align_down, we can return the same value if the alignment + // is known to be greater or equal to the requested value. + if (PtrAlign.getQuantity() >= Alignment) + return true; + + // The alignment could be greater than the minimum at run-time, so we cannot + // infer much about the resulting pointer value. One case is possible: + // For `_Alignas(32) char buf[N]; __builtin_align_down(&buf[idx], 32)` we + // can infer the correct index if the requested alignment is smaller than + // the base alignment so we can perform the computation on the offset. + if (BaseAlignment.getQuantity() >= Alignment) { + assert(Alignment.getBitWidth() <= 64 && + "Cannot handle > 64-bit address-space"); + uint64_t Alignment64 = Alignment.getZExtValue(); + CharUnits NewOffset = CharUnits::fromQuantity( + BuiltinOp == Builtin::BI__builtin_align_down + ? llvm::alignDown(Result.Offset.getQuantity(), Alignment64) + : llvm::alignTo(Result.Offset.getQuantity(), Alignment64)); + Result.adjustOffset(NewOffset - Result.Offset); + // TODO: diagnose out-of-bounds values/only allow for arrays? + return true; + } + // Otherwise, we cannot constant-evaluate the result. + Info.FFDiag(E->getArg(0), diag::note_constexpr_alignment_adjust) + << Alignment; + return false; + } case Builtin::BI__builtin_operator_new: return HandleOperatorNewCall(Info, E, Result); case Builtin::BI__builtin_launder: @@ -9021,7 +9159,7 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, if (E->isElidable() && !ZeroInit) if (const MaterializeTemporaryExpr *ME = dyn_cast<MaterializeTemporaryExpr>(E->getArg(0))) - return Visit(ME->GetTemporaryExpr()); + return Visit(ME->getSubExpr()); if (ZeroInit && !ZeroInitialization(E, T)) return false; @@ -9240,6 +9378,7 @@ namespace { bool VisitUnaryImag(const UnaryOperator *E); // FIXME: Missing: unary -, unary ~, binary add/sub/mul/div, // binary comparisons, binary and/or/xor, + // conditional operator (for GNU conditional select), // shufflevector, ExtVectorElementExpr }; } // end anonymous namespace @@ -10170,7 +10309,7 @@ static QualType getObjectType(APValue::LValueBase B) { if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) { if (const VarDecl *VD = dyn_cast<VarDecl>(D)) return VD->getType(); - } else if (const Expr *E = B.get<const Expr*>()) { + } else if (const Expr *E = B.dyn_cast<const Expr*>()) { if (isa<CompoundLiteralExpr>(E)) return E->getType(); } else if (B.is<TypeInfoLValue>()) { @@ -10491,6 +10630,33 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { return ExprEvaluatorBaseTy::VisitCallExpr(E); } +static bool getBuiltinAlignArguments(const CallExpr *E, EvalInfo &Info, + APValue &Val, APSInt &Alignment) { + QualType SrcTy = E->getArg(0)->getType(); + if (!getAlignmentArgument(E->getArg(1), SrcTy, Info, Alignment)) + return false; + // Even though we are evaluating integer expressions we could get a pointer + // argument for the __builtin_is_aligned() case. + if (SrcTy->isPointerType()) { + LValue Ptr; + if (!EvaluatePointer(E->getArg(0), Ptr, Info)) + return false; + Ptr.moveInto(Val); + } else if (!SrcTy->isIntegralOrEnumerationType()) { + Info.FFDiag(E->getArg(0)); + return false; + } else { + APSInt SrcInt; + if (!EvaluateInteger(E->getArg(0), SrcInt, Info)) + return false; + assert(SrcInt.getBitWidth() >= Alignment.getBitWidth() && + "Bit widths must be the same"); + Val = APValue(SrcInt); + } + assert(Val.hasValue()); + return true; +} + bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinOp) { switch (unsigned BuiltinOp = E->getBuiltinCallee()) { @@ -10533,6 +10699,66 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return Success(Layout.size().getQuantity(), E); } + case Builtin::BI__builtin_is_aligned: { + APValue Src; + APSInt Alignment; + if (!getBuiltinAlignArguments(E, Info, Src, Alignment)) + return false; + if (Src.isLValue()) { + // If we evaluated a pointer, check the minimum known alignment. + LValue Ptr; + Ptr.setFrom(Info.Ctx, Src); + CharUnits BaseAlignment = getBaseAlignment(Info, Ptr); + CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Ptr.Offset); + // We can return true if the known alignment at the computed offset is + // greater than the requested alignment. + assert(PtrAlign.isPowerOfTwo()); + assert(Alignment.isPowerOf2()); + if (PtrAlign.getQuantity() >= Alignment) + return Success(1, E); + // If the alignment is not known to be sufficient, some cases could still + // be aligned at run time. However, if the requested alignment is less or + // equal to the base alignment and the offset is not aligned, we know that + // the run-time value can never be aligned. + if (BaseAlignment.getQuantity() >= Alignment && + PtrAlign.getQuantity() < Alignment) + return Success(0, E); + // Otherwise we can't infer whether the value is sufficiently aligned. + // TODO: __builtin_is_aligned(__builtin_align_{down,up{(expr, N), N) + // in cases where we can't fully evaluate the pointer. + Info.FFDiag(E->getArg(0), diag::note_constexpr_alignment_compute) + << Alignment; + return false; + } + assert(Src.isInt()); + return Success((Src.getInt() & (Alignment - 1)) == 0 ? 1 : 0, E); + } + case Builtin::BI__builtin_align_up: { + APValue Src; + APSInt Alignment; + if (!getBuiltinAlignArguments(E, Info, Src, Alignment)) + return false; + if (!Src.isInt()) + return Error(E); + APSInt AlignedVal = + APSInt((Src.getInt() + (Alignment - 1)) & ~(Alignment - 1), + Src.getInt().isUnsigned()); + assert(AlignedVal.getBitWidth() == Src.getInt().getBitWidth()); + return Success(AlignedVal, E); + } + case Builtin::BI__builtin_align_down: { + APValue Src; + APSInt Alignment; + if (!getBuiltinAlignArguments(E, Info, Src, Alignment)) + return false; + if (!Src.isInt()) + return Error(E); + APSInt AlignedVal = + APSInt(Src.getInt() & ~(Alignment - 1), Src.getInt().isUnsigned()); + assert(AlignedVal.getBitWidth() == Src.getInt().getBitWidth()); + return Success(AlignedVal, E); + } + case Builtin::BI__builtin_bswap16: case Builtin::BI__builtin_bswap32: case Builtin::BI__builtin_bswap64: { @@ -10583,8 +10809,24 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return false; } - case Builtin::BI__builtin_is_constant_evaluated: + case Builtin::BI__builtin_is_constant_evaluated: { + const auto *Callee = Info.CurrentCall->getCallee(); + if (Info.InConstantContext && !Info.CheckingPotentialConstantExpression && + (Info.CallStackDepth == 1 || + (Info.CallStackDepth == 2 && Callee->isInStdNamespace() && + Callee->getIdentifier() && + Callee->getIdentifier()->isStr("is_constant_evaluated")))) { + // FIXME: Find a better way to avoid duplicated diagnostics. + if (Info.EvalStatus.Diag) + Info.report((Info.CallStackDepth == 1) ? E->getExprLoc() + : Info.CurrentCall->CallLoc, + diag::warn_is_constant_evaluated_always_true_constexpr) + << (Info.CallStackDepth == 1 ? "__builtin_is_constant_evaluated" + : "std::is_constant_evaluated"); + } + return Success(Info.InConstantContext, E); + } case Builtin::BI__builtin_ctz: case Builtin::BI__builtin_ctzl: @@ -11417,6 +11659,14 @@ public: } } }; + +enum class CmpResult { + Unequal, + Less, + Equal, + Greater, + Unordered, +}; } template <class SuccessCB, class AfterCB> @@ -11432,15 +11682,8 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, return false; }; - using CCR = ComparisonCategoryResult; - bool IsRelational = E->isRelationalOp(); + bool IsRelational = E->isRelationalOp() || E->getOpcode() == BO_Cmp; bool IsEquality = E->isEqualityOp(); - if (E->getOpcode() == BO_Cmp) { - const ComparisonCategoryInfo &CmpInfo = - Info.Ctx.CompCategories.getInfoForType(E->getType()); - IsRelational = CmpInfo.isOrdered(); - IsEquality = CmpInfo.isEquality(); - } QualType LHSTy = E->getLHS()->getType(); QualType RHSTy = E->getRHS()->getType(); @@ -11454,10 +11697,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, if (!EvaluateInteger(E->getRHS(), RHS, Info) || !LHSOK) return false; if (LHS < RHS) - return Success(CCR::Less, E); + return Success(CmpResult::Less, E); if (LHS > RHS) - return Success(CCR::Greater, E); - return Success(CCR::Equal, E); + return Success(CmpResult::Greater, E); + return Success(CmpResult::Equal, E); } if (LHSTy->isFixedPointType() || RHSTy->isFixedPointType()) { @@ -11470,10 +11713,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, if (!EvaluateFixedPointOrInteger(E->getRHS(), RHSFX, Info) || !LHSOK) return false; if (LHSFX < RHSFX) - return Success(CCR::Less, E); + return Success(CmpResult::Less, E); if (LHSFX > RHSFX) - return Success(CCR::Greater, E); - return Success(CCR::Equal, E); + return Success(CmpResult::Greater, E); + return Success(CmpResult::Equal, E); } if (LHSTy->isAnyComplexType() || RHSTy->isAnyComplexType()) { @@ -11509,12 +11752,12 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, APFloat::cmpResult CR_i = LHS.getComplexFloatImag().compare(RHS.getComplexFloatImag()); bool IsEqual = CR_r == APFloat::cmpEqual && CR_i == APFloat::cmpEqual; - return Success(IsEqual ? CCR::Equal : CCR::Nonequal, E); + return Success(IsEqual ? CmpResult::Equal : CmpResult::Unequal, E); } else { assert(IsEquality && "invalid complex comparison"); bool IsEqual = LHS.getComplexIntReal() == RHS.getComplexIntReal() && LHS.getComplexIntImag() == RHS.getComplexIntImag(); - return Success(IsEqual ? CCR::Equal : CCR::Nonequal, E); + return Success(IsEqual ? CmpResult::Equal : CmpResult::Unequal, E); } } @@ -11533,13 +11776,13 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, auto GetCmpRes = [&]() { switch (LHS.compare(RHS)) { case APFloat::cmpEqual: - return CCR::Equal; + return CmpResult::Equal; case APFloat::cmpLessThan: - return CCR::Less; + return CmpResult::Less; case APFloat::cmpGreaterThan: - return CCR::Greater; + return CmpResult::Greater; case APFloat::cmpUnordered: - return CCR::Unordered; + return CmpResult::Unordered; } llvm_unreachable("Unrecognised APFloat::cmpResult enum"); }; @@ -11561,8 +11804,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, if (!HasSameBase(LHSValue, RHSValue)) { // Inequalities and subtractions between unrelated pointers have // unspecified or undefined behavior. - if (!IsEquality) - return Error(E); + if (!IsEquality) { + Info.FFDiag(E, diag::note_constexpr_pointer_comparison_unspecified); + return false; + } // A constant address may compare equal to the address of a symbol. // The one exception is that address of an object cannot compare equal // to a null pointer constant. @@ -11592,7 +11837,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, if ((RHSValue.Base && isZeroSized(LHSValue)) || (LHSValue.Base && isZeroSized(RHSValue))) return Error(E); - return Success(CCR::Nonequal, E); + return Success(CmpResult::Unequal, E); } const CharUnits &LHSOffset = LHSValue.getLValueOffset(); @@ -11676,10 +11921,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, } if (CompareLHS < CompareRHS) - return Success(CCR::Less, E); + return Success(CmpResult::Less, E); if (CompareLHS > CompareRHS) - return Success(CCR::Greater, E); - return Success(CCR::Equal, E); + return Success(CmpResult::Greater, E); + return Success(CmpResult::Equal, E); } if (LHSTy->isMemberPointerType()) { @@ -11700,7 +11945,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, // null, they compare unequal. if (!LHSValue.getDecl() || !RHSValue.getDecl()) { bool Equal = !LHSValue.getDecl() && !RHSValue.getDecl(); - return Success(Equal ? CCR::Equal : CCR::Nonequal, E); + return Success(Equal ? CmpResult::Equal : CmpResult::Unequal, E); } // Otherwise if either is a pointer to a virtual member function, the @@ -11717,7 +11962,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, // they were dereferenced with a hypothetical object of the associated // class type. bool Equal = LHSValue == RHSValue; - return Success(Equal ? CCR::Equal : CCR::Nonequal, E); + return Success(Equal ? CmpResult::Equal : CmpResult::Unequal, E); } if (LHSTy->isNullPtrType()) { @@ -11726,7 +11971,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, // C++11 [expr.rel]p4, [expr.eq]p3: If two operands of type std::nullptr_t // are compared, the result is true of the operator is <=, >= or ==, and // false otherwise. - return Success(CCR::Equal, E); + return Success(CmpResult::Equal, E); } return DoAfter(); @@ -11736,14 +11981,29 @@ bool RecordExprEvaluator::VisitBinCmp(const BinaryOperator *E) { if (!CheckLiteralType(Info, E)) return false; - auto OnSuccess = [&](ComparisonCategoryResult ResKind, - const BinaryOperator *E) { + auto OnSuccess = [&](CmpResult CR, const BinaryOperator *E) { + ComparisonCategoryResult CCR; + switch (CR) { + case CmpResult::Unequal: + llvm_unreachable("should never produce Unequal for three-way comparison"); + case CmpResult::Less: + CCR = ComparisonCategoryResult::Less; + break; + case CmpResult::Equal: + CCR = ComparisonCategoryResult::Equal; + break; + case CmpResult::Greater: + CCR = ComparisonCategoryResult::Greater; + break; + case CmpResult::Unordered: + CCR = ComparisonCategoryResult::Unordered; + break; + } // Evaluation succeeded. Lookup the information for the comparison category // type and fetch the VarDecl for the result. const ComparisonCategoryInfo &CmpInfo = Info.Ctx.CompCategories.getInfoForType(E->getType()); - const VarDecl *VD = - CmpInfo.getValueInfo(CmpInfo.makeWeakResult(ResKind))->VD; + const VarDecl *VD = CmpInfo.getValueInfo(CmpInfo.makeWeakResult(CCR))->VD; // Check and evaluate the result as a constant expression. LValue LV; LV.set(VD); @@ -11771,14 +12031,14 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { "DataRecursiveIntBinOpEvaluator should have handled integral types"); if (E->isComparisonOp()) { - // Evaluate builtin binary comparisons by evaluating them as C++2a three-way + // Evaluate builtin binary comparisons by evaluating them as three-way // comparisons and then translating the result. - auto OnSuccess = [&](ComparisonCategoryResult ResKind, - const BinaryOperator *E) { - using CCR = ComparisonCategoryResult; - bool IsEqual = ResKind == CCR::Equal, - IsLess = ResKind == CCR::Less, - IsGreater = ResKind == CCR::Greater; + auto OnSuccess = [&](CmpResult CR, const BinaryOperator *E) { + assert((CR != CmpResult::Unequal || E->isEqualityOp()) && + "should only produce Unequal for equality comparisons"); + bool IsEqual = CR == CmpResult::Equal, + IsLess = CR == CmpResult::Less, + IsGreater = CR == CmpResult::Greater; auto Op = E->getOpcode(); switch (Op) { default: @@ -11786,10 +12046,14 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { case BO_EQ: case BO_NE: return Success(IsEqual == (Op == BO_EQ), E); - case BO_LT: return Success(IsLess, E); - case BO_GT: return Success(IsGreater, E); - case BO_LE: return Success(IsEqual || IsLess, E); - case BO_GE: return Success(IsEqual || IsGreater, E); + case BO_LT: + return Success(IsLess, E); + case BO_GT: + return Success(IsGreater, E); + case BO_LE: + return Success(IsEqual || IsLess, E); + case BO_GE: + return Success(IsEqual || IsGreater, E); } }; return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, [&]() { @@ -13374,32 +13638,25 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This, /// EvaluateAsRValue - Try to evaluate this expression, performing an implicit /// lvalue-to-rvalue cast if it is an lvalue. static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { - if (Info.EnableNewConstInterp) { - auto &InterpCtx = Info.Ctx.getInterpContext(); - switch (InterpCtx.evaluateAsRValue(Info, E, Result)) { - case interp::InterpResult::Success: - return true; - case interp::InterpResult::Fail: + if (Info.EnableNewConstInterp) { + if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result)) + return false; + } else { + if (E->getType().isNull()) return false; - case interp::InterpResult::Bail: - break; - } - } - - if (E->getType().isNull()) - return false; - - if (!CheckLiteralType(Info, E)) - return false; - if (!::Evaluate(Result, Info, E)) - return false; + if (!CheckLiteralType(Info, E)) + return false; - if (E->isGLValue()) { - LValue LV; - LV.setFrom(Info.Ctx, Result); - if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result)) + if (!::Evaluate(Result, Info, E)) return false; + + if (E->isGLValue()) { + LValue LV; + LV.setFrom(Info.Ctx, Result); + if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result)) + return false; + } } // Check this core constant expression is a constant expression. @@ -13611,46 +13868,36 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, if (Info.EnableNewConstInterp) { auto &InterpCtx = const_cast<ASTContext &>(Ctx).getInterpContext(); - switch (InterpCtx.evaluateAsInitializer(Info, VD, Value)) { - case interp::InterpResult::Fail: - // Bail out if an error was encountered. - return false; - case interp::InterpResult::Success: - // Evaluation succeeded and value was set. - return CheckConstantExpression(Info, DeclLoc, DeclTy, Value); - case interp::InterpResult::Bail: - // Evaluate the value again for the tree evaluator to use. - break; + if (!InterpCtx.evaluateAsInitializer(Info, VD, Value)) + return false; + } else { + LValue LVal; + LVal.set(VD); + + // C++11 [basic.start.init]p2: + // Variables with static storage duration or thread storage duration shall + // be zero-initialized before any other initialization takes place. + // This behavior is not present in C. + if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() && + !DeclTy->isReferenceType()) { + ImplicitValueInitExpr VIE(DeclTy); + if (!EvaluateInPlace(Value, Info, LVal, &VIE, + /*AllowNonLiteralTypes=*/true)) + return false; } - } - LValue LVal; - LVal.set(VD); - - // C++11 [basic.start.init]p2: - // Variables with static storage duration or thread storage duration shall be - // zero-initialized before any other initialization takes place. - // This behavior is not present in C. - if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() && - !DeclTy->isReferenceType()) { - ImplicitValueInitExpr VIE(DeclTy); - if (!EvaluateInPlace(Value, Info, LVal, &VIE, - /*AllowNonLiteralTypes=*/true)) + if (!EvaluateInPlace(Value, Info, LVal, this, + /*AllowNonLiteralTypes=*/true) || + EStatus.HasSideEffects) return false; - } - if (!EvaluateInPlace(Value, Info, LVal, this, - /*AllowNonLiteralTypes=*/true) || - EStatus.HasSideEffects) - return false; - - // At this point, any lifetime-extended temporaries are completely - // initialized. - Info.performLifetimeExtension(); - - if (!Info.discardCleanups()) - llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + // At this point, any lifetime-extended temporaries are completely + // initialized. + Info.performLifetimeExtension(); + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + } return CheckConstantExpression(Info, DeclLoc, DeclTy, Value) && CheckMemoryLeaks(Info); } @@ -14333,27 +14580,41 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, assert(MD && "Don't provide `this` for non-methods."); assert(!MD->isStatic() && "Don't provide `this` for static methods."); #endif - if (EvaluateObjectArgument(Info, This, ThisVal)) + if (!This->isValueDependent() && + EvaluateObjectArgument(Info, This, ThisVal) && + !Info.EvalStatus.HasSideEffects) ThisPtr = &ThisVal; - if (Info.EvalStatus.HasSideEffects) - return false; + + // Ignore any side-effects from a failed evaluation. This is safe because + // they can't interfere with any other argument evaluation. + Info.EvalStatus.HasSideEffects = false; } ArgVector ArgValues(Args.size()); for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end(); I != E; ++I) { if ((*I)->isValueDependent() || - !Evaluate(ArgValues[I - Args.begin()], Info, *I)) + !Evaluate(ArgValues[I - Args.begin()], Info, *I) || + Info.EvalStatus.HasSideEffects) // If evaluation fails, throw away the argument entirely. ArgValues[I - Args.begin()] = APValue(); - if (Info.EvalStatus.HasSideEffects) - return false; + + // Ignore any side-effects from a failed evaluation. This is safe because + // they can't interfere with any other argument evaluation. + Info.EvalStatus.HasSideEffects = false; } + // Parameter cleanups happen in the caller and are not part of this + // evaluation. + Info.discardCleanups(); + Info.EvalStatus.HasSideEffects = false; + // Build fake call to Callee. CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr, ArgValues.data()); - return Evaluate(Value, Info, this) && Info.discardCleanups() && + // FIXME: Missing ExprWithCleanups in enable_if conditions? + FullExpressionRAII Scope(Info); + return Evaluate(Value, Info, this) && Scope.destroy() && !Info.EvalStatus.HasSideEffects; } @@ -14375,14 +14636,8 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, // The constexpr VM attempts to compile all methods to bytecode here. if (Info.EnableNewConstInterp) { - auto &InterpCtx = Info.Ctx.getInterpContext(); - switch (InterpCtx.isPotentialConstantExpr(Info, FD)) { - case interp::InterpResult::Success: - case interp::InterpResult::Fail: - return Diags.empty(); - case interp::InterpResult::Bail: - break; - } + Info.Ctx.getInterpContext().isPotentialConstantExpr(Info, FD); + return Diags.empty(); } const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD); |