[clang][bytecode] Override InConstantContext flag for immediate calls (#109967)
And fix the diagnostics for __builtin_is_constant_evaluated(). We can be in a non-constant context, but calling an immediate function always makes the context constant for the duration of that call.
This commit is contained in:
@@ -1136,6 +1136,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
InterpFrame *FrameBefore = S.Current;
|
||||
S.Current = NewFrame.get();
|
||||
|
||||
InterpStateCCOverride CCOverride(S, Func->getDecl()->isImmediateFunction());
|
||||
APValue CallResult;
|
||||
// Note that we cannot assert(CallResult.hasValue()) here since
|
||||
// Ret() above only sets the APValue if the curent frame doesn't
|
||||
|
||||
@@ -136,16 +136,17 @@ static bool retPrimValue(InterpState &S, CodePtr OpPC, APValue &Result,
|
||||
static bool interp__builtin_is_constant_evaluated(InterpState &S, CodePtr OpPC,
|
||||
const InterpFrame *Frame,
|
||||
const CallExpr *Call) {
|
||||
unsigned Depth = S.Current->getDepth();
|
||||
auto isStdCall = [](const FunctionDecl *F) -> bool {
|
||||
return F && F->isInStdNamespace() && F->getIdentifier() &&
|
||||
F->getIdentifier()->isStr("is_constant_evaluated");
|
||||
};
|
||||
const InterpFrame *Caller = Frame->Caller;
|
||||
// The current frame is the one for __builtin_is_constant_evaluated.
|
||||
// The one above that, potentially the one for std::is_constant_evaluated().
|
||||
if (S.inConstantContext() && !S.checkingPotentialConstantExpression() &&
|
||||
Frame->Caller && S.getEvalStatus().Diag) {
|
||||
auto isStdCall = [](const FunctionDecl *F) -> bool {
|
||||
return F && F->isInStdNamespace() && F->getIdentifier() &&
|
||||
F->getIdentifier()->isStr("is_constant_evaluated");
|
||||
};
|
||||
const InterpFrame *Caller = Frame->Caller;
|
||||
|
||||
S.getEvalStatus().Diag &&
|
||||
(Depth == 1 || (Depth == 2 && isStdCall(Caller->getCallee())))) {
|
||||
if (Caller->Caller && isStdCall(Caller->getCallee())) {
|
||||
const Expr *E = Caller->Caller->getExpr(Caller->getRetPC());
|
||||
S.report(E->getExprLoc(),
|
||||
|
||||
@@ -19,6 +19,13 @@ InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
|
||||
Context &Ctx, SourceMapper *M)
|
||||
: Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr) {}
|
||||
|
||||
bool InterpState::inConstantContext() const {
|
||||
if (ConstantContextOverride)
|
||||
return *ConstantContextOverride;
|
||||
|
||||
return Parent.InConstantContext;
|
||||
}
|
||||
|
||||
InterpState::~InterpState() {
|
||||
while (Current) {
|
||||
InterpFrame *Next = Current->Caller;
|
||||
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
bool noteUndefinedBehavior() override {
|
||||
return Parent.noteUndefinedBehavior();
|
||||
}
|
||||
bool inConstantContext() const { return Parent.InConstantContext; }
|
||||
bool inConstantContext() const;
|
||||
bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); }
|
||||
void setActiveDiagnostic(bool Flag) override {
|
||||
Parent.setActiveDiagnostic(Flag);
|
||||
@@ -116,6 +116,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class EvaluationResult;
|
||||
friend class InterpStateCCOverride;
|
||||
/// AST Walker state.
|
||||
State &Parent;
|
||||
/// Dead block chain.
|
||||
@@ -124,6 +125,7 @@ private:
|
||||
SourceMapper *M;
|
||||
/// Allocator used for dynamic allocations performed via the program.
|
||||
DynamicAllocator Alloc;
|
||||
std::optional<bool> ConstantContextOverride;
|
||||
|
||||
public:
|
||||
/// Reference to the module containing all bytecode.
|
||||
@@ -144,6 +146,26 @@ public:
|
||||
SeenGlobalTemporaries;
|
||||
};
|
||||
|
||||
class InterpStateCCOverride final {
|
||||
public:
|
||||
InterpStateCCOverride(InterpState &Ctx, bool Value)
|
||||
: Ctx(Ctx), OldCC(Ctx.ConstantContextOverride) {
|
||||
// We only override this if the new value is true.
|
||||
Enabled = Value;
|
||||
if (Enabled)
|
||||
Ctx.ConstantContextOverride = Value;
|
||||
}
|
||||
~InterpStateCCOverride() {
|
||||
if (Enabled)
|
||||
Ctx.ConstantContextOverride = OldCC;
|
||||
}
|
||||
|
||||
private:
|
||||
bool Enabled;
|
||||
InterpState &Ctx;
|
||||
std::optional<bool> OldCC;
|
||||
};
|
||||
|
||||
} // namespace interp
|
||||
} // namespace clang
|
||||
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
// RUN: %clang_cc1 -emit-llvm %s -Dconsteval="" -std=c++2a -triple x86_64-unknown-linux-gnu -o %t.ll
|
||||
// RUN: FileCheck -check-prefix=EXPR -input-file=%t.ll %s
|
||||
|
||||
// RUN: %clang_cc1 -emit-llvm %s -std=c++2a -triple x86_64-unknown-linux-gnu -o %t.ll -fexperimental-new-constant-interpreter
|
||||
// RUN: FileCheck -check-prefix=EVAL -input-file=%t.ll %s
|
||||
// RUN: FileCheck -check-prefix=EVAL-STATIC -input-file=%t.ll %s
|
||||
// RUN: FileCheck -check-prefix=EVAL-FN -input-file=%t.ll %s
|
||||
//
|
||||
// RUN: %clang_cc1 -emit-llvm %s -Dconsteval="" -std=c++2a -triple x86_64-unknown-linux-gnu -o %t.ll -fexperimental-new-constant-interpreter
|
||||
// RUN: FileCheck -check-prefix=EXPR -input-file=%t.ll %s
|
||||
|
||||
// there is two version of symbol checks to ensure
|
||||
// that the symbol we are looking for are correct
|
||||
// EVAL-NOT: @__cxx_global_var_init()
|
||||
|
||||
Reference in New Issue
Block a user