Skip to content

Commit 64c3997

Browse files
committed
[clang][Interp] Allow initializing static class members
We need to handle this when registering global variables.
1 parent 2ea7ec9 commit 64c3997

File tree

5 files changed

+118
-66
lines changed

5 files changed

+118
-66
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2778,26 +2778,34 @@ bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
27782778
std::optional<PrimType> VarT = classify(VD->getType());
27792779

27802780
if (Context::shouldBeGloballyIndexed(VD)) {
2781-
// We've already seen and initialized this global.
2782-
if (P.getGlobal(VD))
2783-
return true;
2784-
2785-
std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init);
2786-
2787-
if (!GlobalIndex)
2788-
return false;
2789-
2790-
if (Init) {
2781+
auto initGlobal = [&](unsigned GlobalIndex) -> bool {
2782+
assert(Init);
27912783
DeclScope<Emitter> LocalScope(this, VD);
27922784

27932785
if (VarT) {
27942786
if (!this->visit(Init))
27952787
return false;
2796-
return this->emitInitGlobal(*VarT, *GlobalIndex, VD);
2788+
return this->emitInitGlobal(*VarT, GlobalIndex, VD);
27972789
}
2798-
return this->visitGlobalInitializer(Init, *GlobalIndex);
2790+
return this->visitGlobalInitializer(Init, GlobalIndex);
2791+
};
2792+
2793+
// We've already seen and initialized this global.
2794+
if (std::optional<unsigned> GlobalIndex = P.getGlobal(VD)) {
2795+
if (P.getPtrGlobal(*GlobalIndex).isInitialized())
2796+
return true;
2797+
2798+
// The previous attempt at initialization might've been unsuccessful,
2799+
// so let's try this one.
2800+
return Init && initGlobal(*GlobalIndex);
27992801
}
2800-
return true;
2802+
2803+
std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init);
2804+
2805+
if (!GlobalIndex)
2806+
return false;
2807+
2808+
return !Init || initGlobal(*GlobalIndex);
28012809
} else {
28022810
VariableScope<Emitter> LocalScope(this);
28032811
if (VarT) {

clang/lib/AST/Interp/Interp.cpp

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,65 @@ static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
5656
return true;
5757
}
5858

59+
static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC,
60+
const ValueDecl *VD) {
61+
const SourceInfo &E = S.Current->getSource(OpPC);
62+
S.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) << VD;
63+
S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
64+
}
65+
66+
static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
67+
const ValueDecl *VD);
68+
static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC,
69+
const ValueDecl *D) {
70+
const SourceInfo &E = S.Current->getSource(OpPC);
71+
72+
if (isa<ParmVarDecl>(D)) {
73+
if (S.getLangOpts().CPlusPlus11) {
74+
S.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << D;
75+
S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange();
76+
} else {
77+
S.FFDiag(E);
78+
}
79+
} else if (const auto *VD = dyn_cast<VarDecl>(D)) {
80+
if (!VD->getType().isConstQualified()) {
81+
diagnoseNonConstVariable(S, OpPC, VD);
82+
return false;
83+
}
84+
85+
// const, but no initializer.
86+
if (!VD->getAnyInitializer()) {
87+
diagnoseMissingInitializer(S, OpPC, VD);
88+
return false;
89+
}
90+
}
91+
return false;
92+
}
93+
5994
static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
6095
const ValueDecl *VD) {
6196
if (!S.getLangOpts().CPlusPlus)
6297
return;
6398

6499
const SourceInfo &Loc = S.Current->getSource(OpPC);
100+
if (const auto *VarD = dyn_cast<VarDecl>(VD);
101+
VarD && VarD->getType().isConstQualified() &&
102+
!VarD->getAnyInitializer()) {
103+
diagnoseMissingInitializer(S, OpPC, VD);
104+
return;
105+
}
65106

66-
if (VD->getType()->isIntegralOrEnumerationType())
107+
if (VD->getType()->isIntegralOrEnumerationType()) {
67108
S.FFDiag(Loc, diag::note_constexpr_ltor_non_const_int, 1) << VD;
68-
else
69-
S.FFDiag(Loc,
70-
S.getLangOpts().CPlusPlus11
71-
? diag::note_constexpr_ltor_non_constexpr
72-
: diag::note_constexpr_ltor_non_integral,
73-
1)
74-
<< VD << VD->getType();
109+
S.Note(VD->getLocation(), diag::note_declared_at);
110+
return;
111+
}
112+
113+
S.FFDiag(Loc,
114+
S.getLangOpts().CPlusPlus11 ? diag::note_constexpr_ltor_non_constexpr
115+
: diag::note_constexpr_ltor_non_integral,
116+
1)
117+
<< VD << VD->getType();
75118
S.Note(VD->getLocation(), diag::note_declared_at);
76119
}
77120

@@ -202,6 +245,9 @@ bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
202245
if (!Ptr.isExtern())
203246
return true;
204247

248+
if (Ptr.isInitialized())
249+
return true;
250+
205251
if (!S.checkingPotentialConstantExpression() && S.getLangOpts().CPlusPlus) {
206252
const auto *VD = Ptr.getDeclDesc()->asValueDecl();
207253
diagnoseNonConstVariable(S, OpPC, VD);
@@ -369,9 +415,15 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
369415
if (const auto *VD = Ptr.getDeclDesc()->asVarDecl();
370416
VD && VD->hasGlobalStorage()) {
371417
const SourceInfo &Loc = S.Current->getSource(OpPC);
372-
S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
373-
S.Note(VD->getLocation(), diag::note_declared_at);
418+
if (VD->getAnyInitializer()) {
419+
S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
420+
S.Note(VD->getLocation(), diag::note_declared_at);
421+
} else {
422+
diagnoseMissingInitializer(S, OpPC, VD);
423+
}
424+
return false;
374425
}
426+
375427
if (!S.checkingPotentialConstantExpression()) {
376428
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit)
377429
<< AK << /*uninitialized=*/true << S.Current->getRange(OpPC);
@@ -598,33 +650,6 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
598650
return true;
599651
}
600652

601-
static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC,
602-
const ValueDecl *D) {
603-
const SourceInfo &E = S.Current->getSource(OpPC);
604-
605-
if (isa<ParmVarDecl>(D)) {
606-
if (S.getLangOpts().CPlusPlus11) {
607-
S.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << D;
608-
S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange();
609-
} else {
610-
S.FFDiag(E);
611-
}
612-
} else if (const auto *VD = dyn_cast<VarDecl>(D)) {
613-
if (!VD->getType().isConstQualified()) {
614-
diagnoseNonConstVariable(S, OpPC, VD);
615-
return false;
616-
}
617-
618-
// const, but no initializer.
619-
if (!VD->getAnyInitializer()) {
620-
S.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) << VD;
621-
S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
622-
return false;
623-
}
624-
}
625-
return false;
626-
}
627-
628653
/// We aleady know the given DeclRefExpr is invalid for some reason,
629654
/// now figure out why and print appropriate diagnostics.
630655
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {

clang/lib/AST/Interp/Program.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
177177
bool IsStatic, IsExtern;
178178
if (const auto *Var = dyn_cast<VarDecl>(VD)) {
179179
IsStatic = Context::shouldBeGloballyIndexed(VD);
180-
IsExtern = !Var->getAnyInitializer();
180+
IsExtern = Var->hasExternalStorage();
181181
} else if (isa<UnnamedGlobalConstantDecl, MSGuidDecl>(VD)) {
182182
IsStatic = true;
183183
IsExtern = false;

clang/test/AST/Interp/cxx23.cpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,18 @@
55

66
/// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics.
77

8-
constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
9-
// expected20-error {{constexpr function never produces a constant expression}}
8+
constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}}
109
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
1110
// ref20-warning {{is a C++23 extension}} \
12-
// expected20-warning {{is a C++23 extension}} \
13-
// expected20-note {{declared here}} \
11+
// expected20-warning {{is a C++23 extension}}
1412

15-
return m; // expected20-note {{initializer of 'm' is not a constant expression}}
13+
return m;
1614
}
17-
constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
18-
// expected20-error {{constexpr function never produces a constant expression}}
15+
constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}}
1916
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
2017
// ref20-warning {{is a C++23 extension}} \
21-
// expected20-warning {{is a C++23 extension}} \
22-
// expected20-note {{declared here}}
23-
return m; // expected20-note {{initializer of 'm' is not a constant expression}}
24-
18+
// expected20-warning {{is a C++23 extension}}
19+
return m;
2520
}
2621

2722
constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \

clang/test/AST/Interp/records.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s
21
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify=expected,both %s
2+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify=expected,both %s
3+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -triple i686 -verify=expected,both %s
34
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify=expected,both %s
4-
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -triple i686 -verify=expected,both %s
5-
// RUN: %clang_cc1 -verify=ref,both %s
65
// RUN: %clang_cc1 -verify=ref,both -std=c++14 %s
6+
// RUN: %clang_cc1 -verify=ref,both -std=c++17 %s
7+
// RUN: %clang_cc1 -verify=ref,both -std=c++17 -triple i686 %s
78
// RUN: %clang_cc1 -verify=ref,both -std=c++20 %s
8-
// RUN: %clang_cc1 -verify=ref,both -triple i686 %s
99

1010
/// Used to crash.
1111
struct Empty {};
@@ -1285,3 +1285,27 @@ namespace {
12851285
}
12861286
}
12871287
#endif
1288+
1289+
namespace pr18633 {
1290+
struct A1 {
1291+
static const int sz;
1292+
static const int sz2;
1293+
};
1294+
const int A1::sz2 = 11;
1295+
template<typename T>
1296+
void func () {
1297+
int arr[A1::sz];
1298+
// both-warning@-1 {{variable length arrays in C++ are a Clang extension}}
1299+
// both-note@-2 {{initializer of 'sz' is unknown}}
1300+
// both-note@-9 {{declared here}}
1301+
}
1302+
template<typename T>
1303+
void func2 () {
1304+
int arr[A1::sz2];
1305+
}
1306+
const int A1::sz = 12;
1307+
void func2() {
1308+
func<int>();
1309+
func2<int>();
1310+
}
1311+
}

0 commit comments

Comments
 (0)