[clang] 64c3997 - [clang][Interp] Allow initializing static class members
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 11 10:05:47 PDT 2024
Author: Timm Bäder
Date: 2024-04-11T19:05:29+02:00
New Revision: 64c3997939cf2d9b4fd1c24c89724d0b47afcd03
URL: https://github.com/llvm/llvm-project/commit/64c3997939cf2d9b4fd1c24c89724d0b47afcd03
DIFF: https://github.com/llvm/llvm-project/commit/64c3997939cf2d9b4fd1c24c89724d0b47afcd03.diff
LOG: [clang][Interp] Allow initializing static class members
We need to handle this when registering global variables.
Added:
Modified:
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/Interp.cpp
clang/lib/AST/Interp/Program.cpp
clang/test/AST/Interp/cxx23.cpp
clang/test/AST/Interp/records.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 84bacd457c85b5..01ec31e4077f70 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -2778,26 +2778,34 @@ bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
std::optional<PrimType> VarT = classify(VD->getType());
if (Context::shouldBeGloballyIndexed(VD)) {
- // We've already seen and initialized this global.
- if (P.getGlobal(VD))
- return true;
-
- std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init);
-
- if (!GlobalIndex)
- return false;
-
- if (Init) {
+ auto initGlobal = [&](unsigned GlobalIndex) -> bool {
+ assert(Init);
DeclScope<Emitter> LocalScope(this, VD);
if (VarT) {
if (!this->visit(Init))
return false;
- return this->emitInitGlobal(*VarT, *GlobalIndex, VD);
+ return this->emitInitGlobal(*VarT, GlobalIndex, VD);
}
- return this->visitGlobalInitializer(Init, *GlobalIndex);
+ return this->visitGlobalInitializer(Init, GlobalIndex);
+ };
+
+ // We've already seen and initialized this global.
+ if (std::optional<unsigned> GlobalIndex = P.getGlobal(VD)) {
+ if (P.getPtrGlobal(*GlobalIndex).isInitialized())
+ return true;
+
+ // The previous attempt at initialization might've been unsuccessful,
+ // so let's try this one.
+ return Init && initGlobal(*GlobalIndex);
}
- return true;
+
+ std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init);
+
+ if (!GlobalIndex)
+ return false;
+
+ return !Init || initGlobal(*GlobalIndex);
} else {
VariableScope<Emitter> LocalScope(this);
if (VarT) {
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index e5e2c932f500b8..2607e074325167 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -56,22 +56,65 @@ static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
return true;
}
+static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC,
+ const ValueDecl *VD) {
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ S.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) << VD;
+ S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
+}
+
+static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
+ const ValueDecl *VD);
+static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC,
+ const ValueDecl *D) {
+ const SourceInfo &E = S.Current->getSource(OpPC);
+
+ if (isa<ParmVarDecl>(D)) {
+ if (S.getLangOpts().CPlusPlus11) {
+ S.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << D;
+ S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange();
+ } else {
+ S.FFDiag(E);
+ }
+ } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ if (!VD->getType().isConstQualified()) {
+ diagnoseNonConstVariable(S, OpPC, VD);
+ return false;
+ }
+
+ // const, but no initializer.
+ if (!VD->getAnyInitializer()) {
+ diagnoseMissingInitializer(S, OpPC, VD);
+ return false;
+ }
+ }
+ return false;
+}
+
static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
const ValueDecl *VD) {
if (!S.getLangOpts().CPlusPlus)
return;
const SourceInfo &Loc = S.Current->getSource(OpPC);
+ if (const auto *VarD = dyn_cast<VarDecl>(VD);
+ VarD && VarD->getType().isConstQualified() &&
+ !VarD->getAnyInitializer()) {
+ diagnoseMissingInitializer(S, OpPC, VD);
+ return;
+ }
- if (VD->getType()->isIntegralOrEnumerationType())
+ if (VD->getType()->isIntegralOrEnumerationType()) {
S.FFDiag(Loc, diag::note_constexpr_ltor_non_const_int, 1) << VD;
- else
- S.FFDiag(Loc,
- S.getLangOpts().CPlusPlus11
- ? diag::note_constexpr_ltor_non_constexpr
- : diag::note_constexpr_ltor_non_integral,
- 1)
- << VD << VD->getType();
+ S.Note(VD->getLocation(), diag::note_declared_at);
+ return;
+ }
+
+ S.FFDiag(Loc,
+ S.getLangOpts().CPlusPlus11 ? diag::note_constexpr_ltor_non_constexpr
+ : diag::note_constexpr_ltor_non_integral,
+ 1)
+ << VD << VD->getType();
S.Note(VD->getLocation(), diag::note_declared_at);
}
@@ -202,6 +245,9 @@ bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!Ptr.isExtern())
return true;
+ if (Ptr.isInitialized())
+ return true;
+
if (!S.checkingPotentialConstantExpression() && S.getLangOpts().CPlusPlus) {
const auto *VD = Ptr.getDeclDesc()->asValueDecl();
diagnoseNonConstVariable(S, OpPC, VD);
@@ -369,9 +415,15 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
if (const auto *VD = Ptr.getDeclDesc()->asVarDecl();
VD && VD->hasGlobalStorage()) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
- S.Note(VD->getLocation(), diag::note_declared_at);
+ if (VD->getAnyInitializer()) {
+ S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
+ S.Note(VD->getLocation(), diag::note_declared_at);
+ } else {
+ diagnoseMissingInitializer(S, OpPC, VD);
+ }
+ return false;
}
+
if (!S.checkingPotentialConstantExpression()) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit)
<< AK << /*uninitialized=*/true << S.Current->getRange(OpPC);
@@ -598,33 +650,6 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
return true;
}
-static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC,
- const ValueDecl *D) {
- const SourceInfo &E = S.Current->getSource(OpPC);
-
- if (isa<ParmVarDecl>(D)) {
- if (S.getLangOpts().CPlusPlus11) {
- S.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << D;
- S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange();
- } else {
- S.FFDiag(E);
- }
- } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
- if (!VD->getType().isConstQualified()) {
- diagnoseNonConstVariable(S, OpPC, VD);
- return false;
- }
-
- // const, but no initializer.
- if (!VD->getAnyInitializer()) {
- S.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) << VD;
- S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
- return false;
- }
- }
- return false;
-}
-
/// We aleady know the given DeclRefExpr is invalid for some reason,
/// now figure out why and print appropriate diagnostics.
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp
index 82367164743fc3..e6f22e79451e97 100644
--- a/clang/lib/AST/Interp/Program.cpp
+++ b/clang/lib/AST/Interp/Program.cpp
@@ -177,7 +177,7 @@ std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
bool IsStatic, IsExtern;
if (const auto *Var = dyn_cast<VarDecl>(VD)) {
IsStatic = Context::shouldBeGloballyIndexed(VD);
- IsExtern = !Var->getAnyInitializer();
+ IsExtern = Var->hasExternalStorage();
} else if (isa<UnnamedGlobalConstantDecl, MSGuidDecl>(VD)) {
IsStatic = true;
IsExtern = false;
diff --git a/clang/test/AST/Interp/cxx23.cpp b/clang/test/AST/Interp/cxx23.cpp
index 042e29613aa753..f0325eef6d87cf 100644
--- a/clang/test/AST/Interp/cxx23.cpp
+++ b/clang/test/AST/Interp/cxx23.cpp
@@ -5,23 +5,18 @@
/// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics.
-constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // expected20-error {{constexpr function never produces a constant expression}}
+constexpr int f(int n) { // ref20-error {{constexpr function never produces a constant expression}}
static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
// ref20-warning {{is a C++23 extension}} \
- // expected20-warning {{is a C++23 extension}} \
- // expected20-note {{declared here}} \
+ // expected20-warning {{is a C++23 extension}}
- return m; // expected20-note {{initializer of 'm' is not a constant expression}}
+ return m;
}
-constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // expected20-error {{constexpr function never produces a constant expression}}
+constexpr int g(int n) { // ref20-error {{constexpr function never produces a constant expression}}
thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
// ref20-warning {{is a C++23 extension}} \
- // expected20-warning {{is a C++23 extension}} \
- // expected20-note {{declared here}}
- return m; // expected20-note {{initializer of 'm' is not a constant expression}}
-
+ // expected20-warning {{is a C++23 extension}}
+ return m;
}
constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 0f76e0cfe99277..f251497ed70182 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1,11 +1,11 @@
-// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++14 -verify=expected,both %s
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify=expected,both %s
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -triple i686 -verify=expected,both %s
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify=expected,both %s
-// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -triple i686 -verify=expected,both %s
-// RUN: %clang_cc1 -verify=ref,both %s
// RUN: %clang_cc1 -verify=ref,both -std=c++14 %s
+// RUN: %clang_cc1 -verify=ref,both -std=c++17 %s
+// RUN: %clang_cc1 -verify=ref,both -std=c++17 -triple i686 %s
// RUN: %clang_cc1 -verify=ref,both -std=c++20 %s
-// RUN: %clang_cc1 -verify=ref,both -triple i686 %s
/// Used to crash.
struct Empty {};
@@ -1285,3 +1285,27 @@ namespace {
}
}
#endif
+
+namespace pr18633 {
+ struct A1 {
+ static const int sz;
+ static const int sz2;
+ };
+ const int A1::sz2 = 11;
+ template<typename T>
+ void func () {
+ int arr[A1::sz];
+ // both-warning at -1 {{variable length arrays in C++ are a Clang extension}}
+ // both-note at -2 {{initializer of 'sz' is unknown}}
+ // both-note at -9 {{declared here}}
+ }
+ template<typename T>
+ void func2 () {
+ int arr[A1::sz2];
+ }
+ const int A1::sz = 12;
+ void func2() {
+ func<int>();
+ func2<int>();
+ }
+}
More information about the cfe-commits
mailing list