Skip to content

Commit 7f1d672

Browse files
committed
[clang][Interp] Diagnose static declarations in constexpr functions
1 parent 208a08c commit 7f1d672

File tree

4 files changed

+73
-40
lines changed

4 files changed

+73
-40
lines changed

clang/lib/AST/Interp/Compiler.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,37 +3520,45 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD) {
35203520
const Expr *Init = VD->getInit();
35213521
std::optional<PrimType> VarT = classify(VD->getType());
35223522

3523+
auto checkDecl = [&]() -> bool {
3524+
bool NeedsOp = VD->isLocalVarDecl() && VD->isStaticLocal();
3525+
return !NeedsOp || this->emitCheckDecl(VD, VD);
3526+
};
3527+
35233528
if (Context::shouldBeGloballyIndexed(VD)) {
35243529
auto initGlobal = [&](unsigned GlobalIndex) -> bool {
35253530
assert(Init);
35263531
DeclScope<Emitter> LocalScope(this, VD);
35273532

35283533
if (VarT) {
35293534
if (!this->visit(Init))
3530-
return false;
3531-
return this->emitInitGlobal(*VarT, GlobalIndex, VD);
3535+
return checkDecl() && false;
3536+
3537+
return checkDecl() && this->emitInitGlobal(*VarT, GlobalIndex, VD);
35323538
}
3533-
return this->visitGlobalInitializer(Init, GlobalIndex);
3539+
3540+
return checkDecl() && this->visitGlobalInitializer(Init, GlobalIndex);
35343541
};
35353542

35363543
// We've already seen and initialized this global.
35373544
if (std::optional<unsigned> GlobalIndex = P.getGlobal(VD)) {
35383545
if (P.getPtrGlobal(*GlobalIndex).isInitialized())
3539-
return true;
3546+
return checkDecl();
35403547

35413548
// The previous attempt at initialization might've been unsuccessful,
35423549
// so let's try this one.
3543-
return Init && initGlobal(*GlobalIndex);
3550+
return Init && checkDecl() && initGlobal(*GlobalIndex);
35443551
}
35453552

35463553
std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init);
35473554

35483555
if (!GlobalIndex)
35493556
return false;
35503557

3551-
return !Init || initGlobal(*GlobalIndex);
3558+
return !Init || (checkDecl() && initGlobal(*GlobalIndex));
35523559
} else {
35533560
VariableScope<Emitter> LocalScope(this, VD);
3561+
35543562
if (VarT) {
35553563
unsigned Offset = this->allocateLocalPrimitive(
35563564
VD, *VarT, VD->getType().isConstQualified());

clang/lib/AST/Interp/Interp.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2689,6 +2689,25 @@ inline bool DecayPtr(InterpState &S, CodePtr OpPC) {
26892689
return true;
26902690
}
26912691

2692+
inline bool CheckDecl(InterpState &S, CodePtr OpPC, const VarDecl *VD) {
2693+
// An expression E is a core constant expression unless the evaluation of E
2694+
// would evaluate one of the following: [C++23] - a control flow that passes
2695+
// through a declaration of a variable with static or thread storage duration
2696+
// unless that variable is usable in constant expressions.
2697+
assert(VD->isLocalVarDecl() &&
2698+
VD->isStaticLocal()); // Checked before emitting this.
2699+
2700+
if (VD == S.EvaluatingDecl)
2701+
return true;
2702+
2703+
if (!VD->isUsableInConstantExpressions(S.getCtx())) {
2704+
S.CCEDiag(VD->getLocation(), diag::note_constexpr_static_local)
2705+
<< (VD->getTSCSpec() == TSCS_unspecified ? 0 : 1) << VD;
2706+
return false;
2707+
}
2708+
return true;
2709+
}
2710+
26922711
//===----------------------------------------------------------------------===//
26932712
// Read opcode arguments
26942713
//===----------------------------------------------------------------------===//

clang/lib/AST/Interp/Opcodes.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def ArgDeclRef : ArgType { let Name = "const DeclRefExpr *"; }
6363
def ArgDesc : ArgType { let Name = "const Descriptor *"; }
6464
def ArgCCI : ArgType { let Name = "const ComparisonCategoryInfo *"; }
6565
def ArgDecl : ArgType { let Name = "const Decl*"; }
66+
def ArgVarDecl : ArgType { let Name = "const VarDecl*"; }
6667

6768
//===----------------------------------------------------------------------===//
6869
// Classes of types instructions operate on.
@@ -382,6 +383,10 @@ def GetLocal : AccessOpcode { let HasCustomEval = 1; }
382383
// [] -> [Pointer]
383384
def SetLocal : AccessOpcode { let HasCustomEval = 1; }
384385

386+
def CheckDecl : Opcode {
387+
let Args = [ArgVarDecl];
388+
}
389+
385390
// [] -> [Value]
386391
def GetGlobal : AccessOpcode;
387392
def GetGlobalUnchecked : AccessOpcode;

clang/test/AST/Interp/cxx23.cpp

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,56 @@
11
// UNSUPPORTED: target={{.*}}-zos{{.*}}
22
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref,ref20,all,all20 %s
3-
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref,ref23,all %s
3+
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref,ref23,all,all23 %s
44
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all,all20 %s -fexperimental-new-constant-interpreter
5-
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all %s -fexperimental-new-constant-interpreter
5+
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all,all23 %s -fexperimental-new-constant-interpreter
66

7-
/// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics.
8-
9-
constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}}
10-
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
11-
// ref20-warning {{is a C++23 extension}} \
12-
// expected20-warning {{is a C++23 extension}}
7+
constexpr int f(int n) { // all20-error {{constexpr function never produces a constant expression}}
8+
static const int m = n; // all-note {{control flows through the definition of a static variable}} \
9+
// all20-note {{control flows through the definition of a static variable}} \
10+
// all20-warning {{is a C++23 extension}}
1311

1412
return m;
1513
}
16-
constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}}
17-
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
18-
// ref20-warning {{is a C++23 extension}} \
19-
// expected20-warning {{is a C++23 extension}}
14+
static_assert(f(0) == 0, ""); // all-error {{not an integral constant expression}} \
15+
// all-note {{in call to}}
16+
17+
constexpr int g(int n) { // all20-error {{constexpr function never produces a constant expression}}
18+
thread_local const int m = n; // all-note {{control flows through the definition of a thread_local variable}} \
19+
// all20-note {{control flows through the definition of a thread_local variable}} \
20+
// all20-warning {{is a C++23 extension}}
2021
return m;
2122
}
23+
static_assert(g(0) == 0, ""); // all-error {{not an integral constant expression}} \
24+
// all-note {{in call to}}
2225

23-
constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
24-
// expected20-error {{constexpr function never produces a constant expression}}
25-
static _Thread_local int m = 0; // ref20-note {{control flows through the definition of a thread_local variable}} \
26-
// ref20-warning {{is a C++23 extension}} \
27-
// expected20-warning {{is a C++23 extension}} \
28-
// expected20-note {{declared here}}
29-
return m; // expected20-note {{read of non-const variable}}
26+
constexpr int c_thread_local(int n) { // all20-error {{constexpr function never produces a constant expression}}
27+
static _Thread_local int m = 0; // all20-note 2{{control flows through the definition of a thread_local variable}} \
28+
// all23-note {{control flows through the definition of a thread_local variable}} \
29+
// all20-warning {{is a C++23 extension}}
30+
return m;
3031
}
32+
static_assert(c_thread_local(0) == 0, ""); // all-error {{not an integral constant expression}} \
33+
// all-note {{in call to}}
3134

3235

33-
constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
34-
// expected20-error {{constexpr function never produces a constant expression}}
35-
static __thread int m = 0; // ref20-note {{control flows through the definition of a thread_local variable}} \
36-
// ref20-warning {{is a C++23 extension}} \
37-
// expected20-warning {{is a C++23 extension}} \
38-
// expected20-note {{declared here}}
39-
return m; // expected20-note {{read of non-const variable}}
36+
constexpr int gnu_thread_local(int n) { // all20-error {{constexpr function never produces a constant expression}}
37+
static __thread int m = 0; // all20-note 2{{control flows through the definition of a thread_local variable}} \
38+
// all23-note {{control flows through the definition of a thread_local variable}} \
39+
// all20-warning {{is a C++23 extension}}
40+
return m;
4041
}
42+
static_assert(gnu_thread_local(0) == 0, ""); // all-error {{not an integral constant expression}} \
43+
// all-note {{in call to}}
4144

42-
constexpr int h(int n) { // ref20-error {{constexpr function never produces a constant expression}}
43-
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
44-
// ref20-warning {{is a C++23 extension}} \
45-
// expected20-warning {{is a C++23 extension}}
45+
constexpr int h(int n) { // all20-error {{constexpr function never produces a constant expression}}
46+
static const int m = n; // all20-note {{control flows through the definition of a static variable}} \
47+
// all20-warning {{is a C++23 extension}}
4648
return &m - &m;
4749
}
4850

49-
constexpr int i(int n) { // ref20-error {{constexpr function never produces a constant expression}}
50-
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
51-
// ref20-warning {{is a C++23 extension}} \
52-
// expected20-warning {{is a C++23 extension}}
51+
constexpr int i(int n) { // all20-error {{constexpr function never produces a constant expression}}
52+
thread_local const int m = n; // all20-note {{control flows through the definition of a thread_local variable}} \
53+
// all20-warning {{is a C++23 extension}}
5354
return &m - &m;
5455
}
5556

0 commit comments

Comments
 (0)