[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:
Timm Baeder
2024-09-25 16:46:46 +02:00
committed by GitHub
parent 3be8e3ad0c
commit a024a0ceed
5 changed files with 47 additions and 8 deletions

View File

@@ -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

View File

@@ -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(),

View File

@@ -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;

View File

@@ -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

View File

@@ -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()