summaryrefslogtreecommitdiff
path: root/clang/lib/AST/ExprConstant.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
commit706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch)
tree4adf86a776049cbf7f69a1929c4babcbbef925eb /clang/lib/AST/ExprConstant.cpp
parent7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff)
Notes
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r--clang/lib/AST/ExprConstant.cpp563
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);