summaryrefslogtreecommitdiffstats
path: root/clang/lib/AST/ExprConstant.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r--clang/lib/AST/ExprConstant.cpp408
1 files changed, 263 insertions, 145 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 415213704be..cc49409412f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -32,9 +32,6 @@
//
//===----------------------------------------------------------------------===//
-#include "Interp/Context.h"
-#include "Interp/Frame.h"
-#include "Interp/State.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
@@ -49,7 +46,6 @@
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/FixedPoint.h"
-#include "clang/Basic/OptionalDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallBitVector.h"
@@ -70,8 +66,8 @@ static bool IsGlobalLValue(APValue::LValueBase B);
namespace {
struct LValue;
- class CallStackFrame;
- class EvalInfo;
+ struct CallStackFrame;
+ struct EvalInfo;
using SourceLocExprScopeGuard =
CurrentSourceLocExprScope::SourceLocExprScopeGuard;
@@ -226,6 +222,12 @@ namespace {
return MostDerivedLength;
}
+ // The order of this enum is important for diagnostics.
+ enum CheckSubobjectKind {
+ CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex,
+ CSK_Real, CSK_Imag
+ };
+
/// A path from a glvalue to a subobject of that glvalue.
struct SubobjectDesignator {
/// True if the subobject was named in a manner not supported by C++11. Such
@@ -478,8 +480,7 @@ namespace {
};
/// A stack frame in the constexpr call stack.
- class CallStackFrame : public interp::Frame {
- public:
+ struct CallStackFrame {
EvalInfo &Info;
/// Parent - The caller of this stack frame.
@@ -573,12 +574,6 @@ namespace {
}
APValue &createTemporary(const void *Key, bool IsLifetimeExtended);
-
- void describe(llvm::raw_ostream &OS) override;
-
- Frame *getCaller() const override { return Caller; }
- SourceLocation getCallLocation() const override { return CallLoc; }
- const FunctionDecl *getCallee() const override { return Callee; }
};
/// Temporarily override 'this'.
@@ -597,6 +592,59 @@ namespace {
const LValue *OldThis;
};
+ /// A partial diagnostic which we might know in advance that we are not going
+ /// to emit.
+ class OptionalDiagnostic {
+ PartialDiagnostic *Diag;
+
+ public:
+ explicit OptionalDiagnostic(PartialDiagnostic *Diag = nullptr)
+ : Diag(Diag) {}
+
+ template<typename T>
+ OptionalDiagnostic &operator<<(const T &v) {
+ if (Diag)
+ *Diag << v;
+ return *this;
+ }
+
+ OptionalDiagnostic &operator<<(const APSInt &I) {
+ if (Diag) {
+ SmallVector<char, 32> Buffer;
+ I.toString(Buffer);
+ *Diag << StringRef(Buffer.data(), Buffer.size());
+ }
+ return *this;
+ }
+
+ OptionalDiagnostic &operator<<(const APFloat &F) {
+ if (Diag) {
+ // FIXME: Force the precision of the source value down so we don't
+ // print digits which are usually useless (we don't really care here if
+ // we truncate a digit by accident in edge cases). Ideally,
+ // APFloat::toString would automatically print the shortest
+ // representation which rounds to the correct value, but it's a bit
+ // tricky to implement.
+ unsigned precision =
+ llvm::APFloat::semanticsPrecision(F.getSemantics());
+ precision = (precision * 59 + 195) / 196;
+ SmallVector<char, 32> Buffer;
+ F.toString(Buffer, precision);
+ *Diag << StringRef(Buffer.data(), Buffer.size());
+ }
+ return *this;
+ }
+
+ OptionalDiagnostic &operator<<(const APFixedPoint &FX) {
+ if (Diag) {
+ SmallVector<char, 32> Buffer;
+ FX.toString(Buffer);
+ *Diag << StringRef(Buffer.data(), Buffer.size());
+ }
+ return *this;
+ }
+ };
+
/// A cleanup, and a flag indicating whether it is lifetime-extended.
class Cleanup {
llvm::PointerIntPair<APValue*, 1, bool> Value;
@@ -659,8 +707,7 @@ namespace {
/// rules. For example, the RHS of (0 && foo()) is not evaluated. We can
/// evaluate the expression regardless of what the RHS is, but C only allows
/// certain things in certain situations.
- class EvalInfo : public interp::State {
- public:
+ struct EvalInfo {
ASTContext &Ctx;
/// EvalStatus - Contains information about the evaluation.
@@ -680,13 +727,6 @@ 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.
- bool EnableNewConstInterp;
-
/// BottomFrame - The frame in which evaluation started. This must be
/// initialized after CurrentCall and CallStackDepth.
CallStackFrame BottomFrame;
@@ -797,7 +837,7 @@ namespace {
/// Are we checking whether the expression is a potential constant
/// expression?
- bool checkingPotentialConstantExpression() const override {
+ bool checkingPotentialConstantExpression() const {
return EvalMode == EM_PotentialConstantExpression ||
EvalMode == EM_PotentialConstantExpressionUnevaluated;
}
@@ -805,28 +845,25 @@ namespace {
/// Are we checking an expression for overflow?
// FIXME: We should check for any kind of undefined or suspicious behavior
// in such constructs, not just overflow.
- bool checkingForOverflow() const override {
- return EvalMode == EM_EvaluateForOverflow;
- }
+ bool checkingForOverflow() { return EvalMode == EM_EvaluateForOverflow; }
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),
- BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr),
- EvaluatingDecl((const ValueDecl *)nullptr),
- EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
- HasFoldFailureDiagnostic(false), InConstantContext(false),
- EvalMode(Mode) {}
+ : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr),
+ CallStackDepth(0), NextCallIndex(1),
+ StepsLeft(getLangOpts().ConstexprStepLimit),
+ BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr),
+ EvaluatingDecl((const ValueDecl *)nullptr),
+ EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
+ HasFoldFailureDiagnostic(false),
+ InConstantContext(false), EvalMode(Mode) {}
void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
EvaluatingDecl = Base;
EvaluatingDeclValue = &Value;
}
+ const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
+
bool CheckCallLimit(SourceLocation Loc) {
// Don't perform any constexpr calls (other than the call we're checking)
// when checking a potential constant expression.
@@ -870,52 +907,118 @@ namespace {
}
private:
- interp::Frame *getCurrentFrame() override { return CurrentCall; }
- const interp::Frame *getBottomFrame() const override { return &BottomFrame; }
-
- bool hasActiveDiagnostic() override { return HasActiveDiagnostic; }
- void setActiveDiagnostic(bool Flag) override { HasActiveDiagnostic = Flag; }
-
- void setFoldFailureDiagnostic(bool Flag) override {
- HasFoldFailureDiagnostic = Flag;
- }
-
- Expr::EvalStatus &getEvalStatus() const override { return EvalStatus; }
-
- ASTContext &getCtx() const override { return Ctx; }
-
- // If we have a prior diagnostic, it will be noting that the expression
- // isn't a constant expression. This diagnostic is more important,
- // unless we require this evaluation to produce a constant expression.
- //
- // FIXME: We might want to show both diagnostics to the user in
- // EM_ConstantFold mode.
- bool hasPriorDiagnostic() override {
- if (!EvalStatus.Diag->empty()) {
- switch (EvalMode) {
- case EM_ConstantFold:
- case EM_IgnoreSideEffects:
- case EM_EvaluateForOverflow:
- if (!HasFoldFailureDiagnostic)
- break;
- // We've already failed to fold something. Keep that diagnostic.
- LLVM_FALLTHROUGH;
- case EM_ConstantExpression:
- case EM_PotentialConstantExpression:
- case EM_ConstantExpressionUnevaluated:
- case EM_PotentialConstantExpressionUnevaluated:
- setActiveDiagnostic(false);
- return true;
+ /// Add a diagnostic to the diagnostics list.
+ PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) {
+ PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator());
+ EvalStatus.Diag->push_back(std::make_pair(Loc, PD));
+ return EvalStatus.Diag->back().second;
+ }
+
+ /// Add notes containing a call stack to the current point of evaluation.
+ void addCallStack(unsigned Limit);
+
+ private:
+ OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId,
+ unsigned ExtraNotes, bool IsCCEDiag) {
+
+ if (EvalStatus.Diag) {
+ // If we have a prior diagnostic, it will be noting that the expression
+ // isn't a constant expression. This diagnostic is more important,
+ // unless we require this evaluation to produce a constant expression.
+ //
+ // FIXME: We might want to show both diagnostics to the user in
+ // EM_ConstantFold mode.
+ if (!EvalStatus.Diag->empty()) {
+ switch (EvalMode) {
+ case EM_ConstantFold:
+ case EM_IgnoreSideEffects:
+ case EM_EvaluateForOverflow:
+ if (!HasFoldFailureDiagnostic)
+ break;
+ // We've already failed to fold something. Keep that diagnostic.
+ LLVM_FALLTHROUGH;
+ case EM_ConstantExpression:
+ case EM_PotentialConstantExpression:
+ case EM_ConstantExpressionUnevaluated:
+ case EM_PotentialConstantExpressionUnevaluated:
+ HasActiveDiagnostic = false;
+ return OptionalDiagnostic();
+ }
}
+
+ unsigned CallStackNotes = CallStackDepth - 1;
+ unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit();
+ if (Limit)
+ CallStackNotes = std::min(CallStackNotes, Limit + 1);
+ if (checkingPotentialConstantExpression())
+ CallStackNotes = 0;
+
+ HasActiveDiagnostic = true;
+ HasFoldFailureDiagnostic = !IsCCEDiag;
+ EvalStatus.Diag->clear();
+ EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes);
+ addDiag(Loc, DiagId);
+ if (!checkingPotentialConstantExpression())
+ addCallStack(Limit);
+ return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
}
- return false;
+ HasActiveDiagnostic = false;
+ return OptionalDiagnostic();
+ }
+ public:
+ // Diagnose that the evaluation could not be folded (FF => FoldFailure)
+ OptionalDiagnostic
+ FFDiag(SourceLocation Loc,
+ diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0) {
+ return Diag(Loc, DiagId, ExtraNotes, false);
}
- unsigned getCallStackDepth() override {
- return CallStackDepth;
+ OptionalDiagnostic FFDiag(const Expr *E, diag::kind DiagId
+ = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0) {
+ if (EvalStatus.Diag)
+ return Diag(E->getExprLoc(), DiagId, ExtraNotes, /*IsCCEDiag*/false);
+ HasActiveDiagnostic = false;
+ return OptionalDiagnostic();
+ }
+
+ /// Diagnose that the evaluation does not produce a C++11 core constant
+ /// expression.
+ ///
+ /// FIXME: Stop evaluating if we're in EM_ConstantExpression or
+ /// EM_PotentialConstantExpression mode and we produce one of these.
+ OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId
+ = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0) {
+ // Don't override a previous diagnostic. Don't bother collecting
+ // diagnostics if we're evaluating for overflow.
+ if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) {
+ HasActiveDiagnostic = false;
+ return OptionalDiagnostic();
+ }
+ return Diag(Loc, DiagId, ExtraNotes, true);
+ }
+ OptionalDiagnostic CCEDiag(const Expr *E, diag::kind DiagId
+ = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0) {
+ return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);
+ }
+ /// Add a note to a prior diagnostic.
+ OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId) {
+ if (!HasActiveDiagnostic)
+ return OptionalDiagnostic();
+ return OptionalDiagnostic(&addDiag(Loc, DiagId));
+ }
+
+ /// Add a stack of notes to a prior diagnostic.
+ void addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
+ if (HasActiveDiagnostic) {
+ EvalStatus.Diag->insert(EvalStatus.Diag->end(),
+ Diags.begin(), Diags.end());
+ }
}
- public:
/// Should we continue evaluation after encountering a side-effect that we
/// couldn't model?
bool keepEvaluatingAfterSideEffect() {
@@ -961,14 +1064,14 @@ namespace {
/// Note that we hit something that was technically undefined behavior, but
/// that we can evaluate past it (such as signed overflow or floating-point
/// division by zero.)
- bool noteUndefinedBehavior() override {
+ bool noteUndefinedBehavior() {
EvalStatus.HasUndefinedBehavior = true;
return keepEvaluatingAfterUndefinedBehavior();
}
/// Should we continue evaluation as much as possible after encountering a
/// construct which can't be reduced to a value?
- bool keepEvaluatingAfterFailure() const override {
+ bool keepEvaluatingAfterFailure() {
if (!StepsLeft)
return false;
@@ -1218,6 +1321,62 @@ APValue &CallStackFrame::createTemporary(const void *Key,
return Result;
}
+static void describeCall(CallStackFrame *Frame, raw_ostream &Out);
+
+void EvalInfo::addCallStack(unsigned Limit) {
+ // Determine which calls to skip, if any.
+ unsigned ActiveCalls = CallStackDepth - 1;
+ unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
+ if (Limit && Limit < ActiveCalls) {
+ SkipStart = Limit / 2 + Limit % 2;
+ SkipEnd = ActiveCalls - Limit / 2;
+ }
+
+ // Walk the call stack and add the diagnostics.
+ unsigned CallIdx = 0;
+ for (CallStackFrame *Frame = CurrentCall; Frame != &BottomFrame;
+ Frame = Frame->Caller, ++CallIdx) {
+ // Skip this call?
+ if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
+ if (CallIdx == SkipStart) {
+ // Note that we're skipping calls.
+ addDiag(Frame->CallLoc, diag::note_constexpr_calls_suppressed)
+ << unsigned(ActiveCalls - Limit);
+ }
+ continue;
+ }
+
+ // Use a different note for an inheriting constructor, because from the
+ // user's perspective it's not really a function at all.
+ if (auto *CD = dyn_cast_or_null<CXXConstructorDecl>(Frame->Callee)) {
+ if (CD->isInheritingConstructor()) {
+ addDiag(Frame->CallLoc, diag::note_constexpr_inherited_ctor_call_here)
+ << CD->getParent();
+ continue;
+ }
+ }
+
+ SmallVector<char, 128> Buffer;
+ llvm::raw_svector_ostream Out(Buffer);
+ describeCall(Frame, Out);
+ addDiag(Frame->CallLoc, diag::note_constexpr_call_here) << Out.str();
+ }
+}
+
+/// Kinds of access we can perform on an object, for diagnostics. Note that
+/// we consider a member function call to be a kind of access, even though
+/// it is not formally an access of the object, because it has (largely) the
+/// same set of semantic restrictions.
+enum AccessKinds {
+ AK_Read,
+ AK_Assign,
+ AK_Increment,
+ AK_Decrement,
+ AK_MemberCall,
+ AK_DynamicCast,
+ AK_TypeId,
+};
+
static bool isModification(AccessKinds AK) {
switch (AK) {
case AK_Read:
@@ -1585,36 +1744,36 @@ static void negateAsSigned(APSInt &Int) {
}
/// Produce a string describing the given constexpr call.
-void CallStackFrame::describe(raw_ostream &Out) {
+static void describeCall(CallStackFrame *Frame, raw_ostream &Out) {
unsigned ArgIndex = 0;
- bool IsMemberCall = isa<CXXMethodDecl>(Callee) &&
- !isa<CXXConstructorDecl>(Callee) &&
- cast<CXXMethodDecl>(Callee)->isInstance();
+ bool IsMemberCall = isa<CXXMethodDecl>(Frame->Callee) &&
+ !isa<CXXConstructorDecl>(Frame->Callee) &&
+ cast<CXXMethodDecl>(Frame->Callee)->isInstance();
if (!IsMemberCall)
- Out << *Callee << '(';
+ Out << *Frame->Callee << '(';
- if (This && IsMemberCall) {
+ if (Frame->This && IsMemberCall) {
APValue Val;
- This->moveInto(Val);
- Val.printPretty(Out, Info.Ctx,
- This->Designator.MostDerivedType);
+ Frame->This->moveInto(Val);
+ Val.printPretty(Out, Frame->Info.Ctx,
+ Frame->This->Designator.MostDerivedType);
// FIXME: Add parens around Val if needed.
- Out << "->" << *Callee << '(';
+ Out << "->" << *Frame->Callee << '(';
IsMemberCall = false;
}
- for (FunctionDecl::param_const_iterator I = Callee->param_begin(),
- E = Callee->param_end(); I != E; ++I, ++ArgIndex) {
+ for (FunctionDecl::param_const_iterator I = Frame->Callee->param_begin(),
+ E = Frame->Callee->param_end(); I != E; ++I, ++ArgIndex) {
if (ArgIndex > (unsigned)IsMemberCall)
Out << ", ";
const ParmVarDecl *Param = *I;
- const APValue &Arg = Arguments[ArgIndex];
- Arg.printPretty(Out, Info.Ctx, Param->getType());
+ const APValue &Arg = Frame->Arguments[ArgIndex];
+ Arg.printPretty(Out, Frame->Info.Ctx, Param->getType());
if (ArgIndex == 0 && IsMemberCall)
- Out << "->" << *Callee << '(';
+ Out << "->" << *Frame->Callee << '(';
}
Out << ')';
@@ -12117,18 +12276,6 @@ 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:
- return false;
- case interp::InterpResult::Bail:
- break;
- }
- }
-
if (E->getType().isNull())
return false;
@@ -12336,29 +12483,11 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
Expr::EvalStatus EStatus;
EStatus.Diag = &Notes;
- EvalInfo Info(Ctx, EStatus, VD->isConstexpr()
+ EvalInfo InitInfo(Ctx, EStatus, VD->isConstexpr()
? EvalInfo::EM_ConstantExpression
: EvalInfo::EM_ConstantFold);
- Info.setEvaluatingDecl(VD, Value);
- Info.InConstantContext = true;
-
- SourceLocation DeclLoc = VD->getLocation();
- QualType DeclTy = VD->getType();
-
- 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;
- }
- }
+ InitInfo.setEvaluatingDecl(VD, Value);
+ InitInfo.InConstantContext = true;
LValue LVal;
LVal.set(VD);
@@ -12368,19 +12497,20 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
// 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,
+ !VD->getType()->isReferenceType()) {
+ ImplicitValueInitExpr VIE(VD->getType());
+ if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE,
/*AllowNonLiteralTypes=*/true))
return false;
}
- if (!EvaluateInPlace(Value, Info, LVal, this,
+ if (!EvaluateInPlace(Value, InitInfo, LVal, this,
/*AllowNonLiteralTypes=*/true) ||
EStatus.HasSideEffects)
return false;
- return CheckConstantExpression(Info, DeclLoc, DeclTy, Value);
+ return CheckConstantExpression(InitInfo, VD->getLocation(), VD->getType(),
+ Value);
}
/// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
@@ -13055,18 +13185,6 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
EvalInfo::EM_PotentialConstantExpression);
Info.InConstantContext = true;
- // 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;
- }
- }
-
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : nullptr;
OpenPOWER on IntegriCloud