Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class CompoundStmt;
class DependentFunctionTemplateSpecializationInfo;
class EnumDecl;
class Expr;
struct EvalStatus;
class FunctionTemplateDecl;
class FunctionTemplateSpecializationInfo;
class FunctionTypeLoc;
Expand Down Expand Up @@ -1412,7 +1413,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
APValue *evaluateValue() const;

private:
APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
APValue *evaluateValueImpl(EvalStatus &EStatus,
bool IsConstantInitialization) const;

public:
Expand Down Expand Up @@ -1446,7 +1447,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
/// constant initializer. Should only be called once, after completing the
/// definition of the variable.
bool checkForConstantInitialization(
SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
SmallVectorImpl<PartialDiagnosticAt> *Notes) const;

void setInitStyle(InitializationStyle Style) {
VarDeclBits.InitStyle = Style;
Expand Down
80 changes: 42 additions & 38 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,47 @@ namespace clang {
class ValueDecl;
class WarnUnusedResultAttr;

/// EvalStatus is a struct with detailed info about an evaluation in progress.
struct EvalStatus {
/// Whether the evaluated expression has side effects.
/// For example, (f() && 0) can be folded, but it still has side effects.
bool HasSideEffects = false;

/// Whether the evaluation hit undefined behavior.
/// For example, 1.0 / 0.0 can be folded to Inf, but has undefined behavior.
/// Likewise, INT_MAX + 1 can be folded to INT_MIN, but has UB.
bool HasUndefinedBehavior = false;

bool HasFFDiagnostic = false;
bool HasCCEDiagnostic = false;

/// Diag - If this is non-null, it will be filled in with a stack of notes
/// indicating why evaluation failed (or why it failed to produce a constant
/// expression).
/// If the expression is unfoldable, the notes will indicate why it's not
/// foldable. If the expression is foldable, but not a constant expression,
/// the notes will describes why it isn't a constant expression. If the
/// expression *is* a constant expression, no notes will be produced.
///
/// FIXME: this causes significant performance concerns and should be
/// refactored at some point. Not all evaluations of the constant
/// expression interpreter will display the given diagnostics, this means
/// those kinds of uses are paying the expense of generating a diagnostic
/// (which may include expensive operations like converting APValue objects
/// to a string representation).
SmallVectorImpl<PartialDiagnosticAt> *Diag = nullptr;

EvalStatus() = default;

/// Return true if the evaluated expression has
/// side effects.
bool hasSideEffects() const { return HasSideEffects; }

bool hasAnyDiagnostic() const {
return HasFFDiagnostic || HasCCEDiagnostic;
}
};

/// A simple array of base specifiers.
typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath;

Expand Down Expand Up @@ -605,42 +646,6 @@ class Expr : public ValueStmt {
/// expression is a member pointer constant.
const ValueDecl *getAsBuiltinConstantDeclRef(const ASTContext &Context) const;

/// EvalStatus is a struct with detailed info about an evaluation in progress.
struct EvalStatus {
/// Whether the evaluated expression has side effects.
/// For example, (f() && 0) can be folded, but it still has side effects.
bool HasSideEffects = false;

/// Whether the evaluation hit undefined behavior.
/// For example, 1.0 / 0.0 can be folded to Inf, but has undefined behavior.
/// Likewise, INT_MAX + 1 can be folded to INT_MIN, but has UB.
bool HasUndefinedBehavior = false;

/// Diag - If this is non-null, it will be filled in with a stack of notes
/// indicating why evaluation failed (or why it failed to produce a constant
/// expression).
/// If the expression is unfoldable, the notes will indicate why it's not
/// foldable. If the expression is foldable, but not a constant expression,
/// the notes will describes why it isn't a constant expression. If the
/// expression *is* a constant expression, no notes will be produced.
///
/// FIXME: this causes significant performance concerns and should be
/// refactored at some point. Not all evaluations of the constant
/// expression interpreter will display the given diagnostics, this means
/// those kinds of uses are paying the expense of generating a diagnostic
/// (which may include expensive operations like converting APValue objects
/// to a string representation).
SmallVectorImpl<PartialDiagnosticAt> *Diag = nullptr;

EvalStatus() = default;

/// Return true if the evaluated expression has
/// side effects.
bool hasSideEffects() const {
return HasSideEffects;
}
};

/// EvalResult is a struct with detailed info about an evaluated expression.
struct EvalResult : EvalStatus {
/// Val - This is the value the expression can be folded to.
Expand Down Expand Up @@ -735,8 +740,7 @@ class Expr : public ValueStmt {
/// can be folded to a constant, and produces any relevant notes. In C++11,
/// notes will be produced if the expression is not a constant expression.
bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx,
const VarDecl *VD,
SmallVectorImpl<PartialDiagnosticAt> &Notes,
const VarDecl *VD, EvalStatus &Status,
bool IsConstantInitializer) const;

/// EvaluateWithSubstitution - Evaluate an expression as if from the context
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6985,8 +6985,8 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
// here -- we will create a global variable in any case, and that
// will have the state of initializer evaluation attached.
APValue V;
SmallVector<PartialDiagnosticAt> Notes;
(void)Init->EvaluateAsInitializer(V, Ctx.getASTContext(), VD, Notes,
EvalStatus EStatus;
(void)Init->EvaluateAsInitializer(V, Ctx.getASTContext(), VD, EStatus,
true);
return this->visitDeclRef(D, E);
}
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/AST/ByteCode/InterpState.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ class InterpState final : public State, public SourceMapper {
const Frame *getBottomFrame() const override { return &BottomFrame; }

// Access objects from the walker context.
Expr::EvalStatus &getEvalStatus() const override {
return Parent.getEvalStatus();
}
EvalStatus &getEvalStatus() const override { return Parent.getEvalStatus(); }
ASTContext &getASTContext() const override { return Ctx.getASTContext(); }
const LangOptions &getLangOpts() const {
return Ctx.getASTContext().getLangOpts();
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/AST/ByteCode/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {

OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
unsigned ExtraNotes, bool IsCCEDiag) {
Expr::EvalStatus &EvalStatus = getEvalStatus();
EvalStatus &EvalStatus = getEvalStatus();
if (IsCCEDiag)
EvalStatus.HasCCEDiagnostic = true;
else
EvalStatus.HasFFDiagnostic = true;

if (EvalStatus.Diag) {
if (hasPriorDiagnostic()) {
return OptionalDiagnostic();
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ByteCode/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class State {
virtual bool hasActiveDiagnostic() = 0;
virtual void setActiveDiagnostic(bool Flag) = 0;
virtual void setFoldFailureDiagnostic(bool Flag) = 0;
virtual Expr::EvalStatus &getEvalStatus() const = 0;
virtual EvalStatus &getEvalStatus() const = 0;
virtual ASTContext &getASTContext() const = 0;
virtual bool hasPriorDiagnostic() = 0;
virtual unsigned getCallStackDepth() = 0;
Expand Down
16 changes: 9 additions & 7 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2573,11 +2573,11 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const {
}

APValue *VarDecl::evaluateValue() const {
SmallVector<PartialDiagnosticAt, 8> Notes;
return evaluateValueImpl(Notes, hasConstantInitialization());
EvalStatus ES;
return evaluateValueImpl(ES, hasConstantInitialization());
}

APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
APValue *VarDecl::evaluateValueImpl(EvalStatus &EStatus,
bool IsConstantInitialization) const {
EvaluatedStmt *Eval = ensureEvaluatedStmt();

Expand All @@ -2598,7 +2598,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
Eval->IsEvaluating = true;

ASTContext &Ctx = getASTContext();
bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes,
bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, EStatus,
IsConstantInitialization);

// In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't
Expand All @@ -2608,7 +2608,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
if (IsConstantInitialization &&
(Ctx.getLangOpts().CPlusPlus ||
(isConstexpr() && Ctx.getLangOpts().C23)) &&
!Notes.empty())
EStatus.hasAnyDiagnostic())
Result = false;

// Ensure the computed APValue is cleaned up later if evaluation succeeded,
Expand Down Expand Up @@ -2662,7 +2662,7 @@ bool VarDecl::hasConstantInitialization() const {
}

bool VarDecl::checkForConstantInitialization(
SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
SmallVectorImpl<PartialDiagnosticAt> *Notes) const {
EvaluatedStmt *Eval = ensureEvaluatedStmt();
// If we ask for the value before we know whether we have a constant
// initializer, we can compute the wrong value (for example, due to
Expand All @@ -2676,8 +2676,10 @@ bool VarDecl::checkForConstantInitialization(
assert(!getInit()->isValueDependent());

// Evaluate the initializer to check whether it's a constant expression.
EvalStatus EStatus;
EStatus.Diag = Notes;
Eval->HasConstantInitialization =
evaluateValueImpl(Notes, true) && Notes.empty();
evaluateValueImpl(EStatus, true) && !EStatus.hasAnyDiagnostic();

// If evaluation as a constant initializer failed, allow re-evaluation as a
// non-constant initializer if we later find we want the value.
Expand Down
Loading
Loading