Instead of having `InterpState` call into its parent `EvalInfo`, just save the state in `interp::State`, where both subclasses can access it.
230 lines
7.7 KiB
C++
230 lines
7.7 KiB
C++
//===--- State.h - State chain for the VM and AST Walker --------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines the base class of the interpreter and evaluator state.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_AST_INTERP_STATE_H
|
|
#define LLVM_CLANG_AST_INTERP_STATE_H
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/ASTDiagnostic.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/OptionalDiagnostic.h"
|
|
|
|
namespace clang {
|
|
class OptionalDiagnostic;
|
|
|
|
/// 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_ReadObjectRepresentation,
|
|
AK_Assign,
|
|
AK_Increment,
|
|
AK_Decrement,
|
|
AK_MemberCall,
|
|
AK_DynamicCast,
|
|
AK_TypeId,
|
|
AK_Construct,
|
|
AK_Destroy,
|
|
AK_IsWithinLifetime,
|
|
AK_Dereference
|
|
};
|
|
|
|
/// 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,
|
|
CSK_VectorElement
|
|
};
|
|
|
|
enum class EvaluationMode {
|
|
/// Evaluate as a constant expression. Stop if we find that the expression
|
|
/// is not a constant expression.
|
|
ConstantExpression,
|
|
|
|
/// Evaluate as a constant expression. Stop if we find that the expression
|
|
/// is not a constant expression. Some expressions can be retried in the
|
|
/// optimizer if we don't constant fold them here, but in an unevaluated
|
|
/// context we try to fold them immediately since the optimizer never
|
|
/// gets a chance to look at it.
|
|
ConstantExpressionUnevaluated,
|
|
|
|
/// Fold the expression to a constant. Stop if we hit a side-effect that
|
|
/// we can't model.
|
|
ConstantFold,
|
|
|
|
/// Evaluate in any way we know how. Don't worry about side-effects that
|
|
/// can't be modeled.
|
|
IgnoreSideEffects,
|
|
};
|
|
|
|
namespace interp {
|
|
class Frame;
|
|
class SourceInfo;
|
|
|
|
/// Interface for the VM to interact with the AST walker's context.
|
|
class State {
|
|
public:
|
|
State(ASTContext &ASTCtx, Expr::EvalStatus &EvalStatus)
|
|
: Ctx(ASTCtx), EvalStatus(EvalStatus) {}
|
|
virtual ~State();
|
|
|
|
virtual const Frame *getCurrentFrame() = 0;
|
|
virtual const Frame *getBottomFrame() const = 0;
|
|
virtual unsigned getCallStackDepth() = 0;
|
|
virtual bool stepsLeft() const = 0;
|
|
|
|
Expr::EvalStatus &getEvalStatus() const { return EvalStatus; }
|
|
ASTContext &getASTContext() const { return Ctx; }
|
|
const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
|
|
|
|
/// Note that we have had a side-effect, and determine whether we should
|
|
/// keep evaluating.
|
|
bool noteSideEffect() const {
|
|
EvalStatus.HasSideEffects = true;
|
|
return keepEvaluatingAfterSideEffect();
|
|
}
|
|
|
|
/// Should we continue evaluation as much as possible after encountering a
|
|
/// construct which can't be reduced to a value?
|
|
bool keepEvaluatingAfterFailure() const;
|
|
/// Should we continue evaluation after encountering a side-effect that we
|
|
/// couldn't model?
|
|
bool keepEvaluatingAfterSideEffect() const;
|
|
|
|
/// 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() const {
|
|
EvalStatus.HasUndefinedBehavior = true;
|
|
return keepEvaluatingAfterUndefinedBehavior();
|
|
}
|
|
|
|
/// Are we checking whether the expression is a potential constant
|
|
/// expression?
|
|
bool checkingPotentialConstantExpression() const {
|
|
return CheckingPotentialConstantExpression;
|
|
}
|
|
/// Are we checking an expression for overflow?
|
|
bool checkingForUndefinedBehavior() const {
|
|
return CheckingForUndefinedBehavior;
|
|
}
|
|
|
|
/// 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);
|
|
|
|
OptionalDiagnostic
|
|
FFDiag(const Expr *E,
|
|
diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
|
|
unsigned ExtraNotes = 0);
|
|
|
|
OptionalDiagnostic
|
|
FFDiag(SourceInfo SI,
|
|
diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
|
|
unsigned ExtraNotes = 0);
|
|
|
|
/// 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);
|
|
|
|
OptionalDiagnostic
|
|
CCEDiag(const Expr *E,
|
|
diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
|
|
unsigned ExtraNotes = 0);
|
|
|
|
OptionalDiagnostic
|
|
CCEDiag(SourceInfo SI,
|
|
diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
|
|
unsigned ExtraNotes = 0);
|
|
|
|
/// Add a note to a prior diagnostic.
|
|
OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId);
|
|
|
|
/// Add a stack of notes to a prior diagnostic.
|
|
void addNotes(ArrayRef<PartialDiagnosticAt> Diags);
|
|
|
|
/// Directly reports a diagnostic message.
|
|
DiagnosticBuilder report(SourceLocation Loc, diag::kind DiagId);
|
|
|
|
/// Whether or not we're in a context where the front end requires a
|
|
/// constant value.
|
|
bool InConstantContext = false;
|
|
|
|
/// Whether we're checking that an expression is a potential constant
|
|
/// expression. If so, do not fail on constructs that could become constant
|
|
/// later on (such as a use of an undefined global).
|
|
bool CheckingPotentialConstantExpression = false;
|
|
|
|
/// Whether we're checking for an expression that has undefined behavior.
|
|
/// If so, we will produce warnings if we encounter an operation that is
|
|
/// always undefined.
|
|
///
|
|
/// Note that we still need to evaluate the expression normally when this
|
|
/// is set; this is used when evaluating ICEs in C.
|
|
bool CheckingForUndefinedBehavior = false;
|
|
|
|
EvaluationMode EvalMode;
|
|
ASTContext &Ctx;
|
|
Expr::EvalStatus &EvalStatus;
|
|
|
|
private:
|
|
/// HasActiveDiagnostic - Was the previous diagnostic stored? If so, further
|
|
/// notes attached to it will also be stored, otherwise they will not be.
|
|
bool HasActiveDiagnostic = false;
|
|
|
|
/// Have we emitted a diagnostic explaining why we couldn't constant
|
|
/// fold (not just why it's not strictly a constant expression)?
|
|
bool HasFoldFailureDiagnostic = false;
|
|
|
|
void addCallStack(unsigned Limit);
|
|
|
|
PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId);
|
|
|
|
OptionalDiagnostic diag(SourceLocation Loc, diag::kind DiagId,
|
|
unsigned ExtraNotes, bool IsCCEDiag);
|
|
|
|
/// Should we continue evaluation after encountering undefined behavior?
|
|
bool keepEvaluatingAfterUndefinedBehavior() const;
|
|
|
|
// 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
|
|
// EvaluationMode::ConstantFold mode.
|
|
bool hasPriorDiagnostic();
|
|
|
|
void setFoldFailureDiagnostic(bool Flag) { HasFoldFailureDiagnostic = Flag; };
|
|
void setActiveDiagnostic(bool Flag) { HasActiveDiagnostic = Flag; };
|
|
bool hasActiveDiagnostic() const { return HasActiveDiagnostic; }
|
|
};
|
|
|
|
} // namespace interp
|
|
} // namespace clang
|
|
|
|
#endif
|