[clang] 7f1d672 - [clang][Interp] Diagnose static declarations in constexpr functions
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 1 02:59:05 PDT 2024
Author: Timm Bäder
Date: 2024-07-01T11:58:56+02:00
New Revision: 7f1d672d70eabe010567fcd8c365d27549736c6d
URL: https://github.com/llvm/llvm-project/commit/7f1d672d70eabe010567fcd8c365d27549736c6d
DIFF: https://github.com/llvm/llvm-project/commit/7f1d672d70eabe010567fcd8c365d27549736c6d.diff
LOG: [clang][Interp] Diagnose static declarations in constexpr functions
Added:
Modified:
clang/lib/AST/Interp/Compiler.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/Opcodes.td
clang/test/AST/Interp/cxx23.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/Compiler.cpp b/clang/lib/AST/Interp/Compiler.cpp
index 424f4f84a0167..ff755d503f871 100644
--- a/clang/lib/AST/Interp/Compiler.cpp
+++ b/clang/lib/AST/Interp/Compiler.cpp
@@ -3520,6 +3520,11 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD) {
const Expr *Init = VD->getInit();
std::optional<PrimType> VarT = classify(VD->getType());
+ auto checkDecl = [&]() -> bool {
+ bool NeedsOp = VD->isLocalVarDecl() && VD->isStaticLocal();
+ return !NeedsOp || this->emitCheckDecl(VD, VD);
+ };
+
if (Context::shouldBeGloballyIndexed(VD)) {
auto initGlobal = [&](unsigned GlobalIndex) -> bool {
assert(Init);
@@ -3527,20 +3532,22 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD) {
if (VarT) {
if (!this->visit(Init))
- return false;
- return this->emitInitGlobal(*VarT, GlobalIndex, VD);
+ return checkDecl() && false;
+
+ return checkDecl() && this->emitInitGlobal(*VarT, GlobalIndex, VD);
}
- return this->visitGlobalInitializer(Init, GlobalIndex);
+
+ return checkDecl() && 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;
+ return checkDecl();
// The previous attempt at initialization might've been unsuccessful,
// so let's try this one.
- return Init && initGlobal(*GlobalIndex);
+ return Init && checkDecl() && initGlobal(*GlobalIndex);
}
std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init);
@@ -3548,9 +3555,10 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD) {
if (!GlobalIndex)
return false;
- return !Init || initGlobal(*GlobalIndex);
+ return !Init || (checkDecl() && initGlobal(*GlobalIndex));
} else {
VariableScope<Emitter> LocalScope(this, VD);
+
if (VarT) {
unsigned Offset = this->allocateLocalPrimitive(
VD, *VarT, VD->getType().isConstQualified());
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 866593b9af094..ff6d50ab9b6f8 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -2689,6 +2689,25 @@ inline bool DecayPtr(InterpState &S, CodePtr OpPC) {
return true;
}
+inline bool CheckDecl(InterpState &S, CodePtr OpPC, const VarDecl *VD) {
+ // An expression E is a core constant expression unless the evaluation of E
+ // would evaluate one of the following: [C++23] - a control flow that passes
+ // through a declaration of a variable with static or thread storage duration
+ // unless that variable is usable in constant expressions.
+ assert(VD->isLocalVarDecl() &&
+ VD->isStaticLocal()); // Checked before emitting this.
+
+ if (VD == S.EvaluatingDecl)
+ return true;
+
+ if (!VD->isUsableInConstantExpressions(S.getCtx())) {
+ S.CCEDiag(VD->getLocation(), diag::note_constexpr_static_local)
+ << (VD->getTSCSpec() == TSCS_unspecified ? 0 : 1) << VD;
+ return false;
+ }
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index ddd955fc4cfa4..81e7b812da237 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -63,6 +63,7 @@ def ArgDeclRef : ArgType { let Name = "const DeclRefExpr *"; }
def ArgDesc : ArgType { let Name = "const Descriptor *"; }
def ArgCCI : ArgType { let Name = "const ComparisonCategoryInfo *"; }
def ArgDecl : ArgType { let Name = "const Decl*"; }
+def ArgVarDecl : ArgType { let Name = "const VarDecl*"; }
//===----------------------------------------------------------------------===//
// Classes of types instructions operate on.
@@ -382,6 +383,10 @@ def GetLocal : AccessOpcode { let HasCustomEval = 1; }
// [] -> [Pointer]
def SetLocal : AccessOpcode { let HasCustomEval = 1; }
+def CheckDecl : Opcode {
+ let Args = [ArgVarDecl];
+}
+
// [] -> [Value]
def GetGlobal : AccessOpcode;
def GetGlobalUnchecked : AccessOpcode;
diff --git a/clang/test/AST/Interp/cxx23.cpp b/clang/test/AST/Interp/cxx23.cpp
index b36299c3f8aee..eb05a9fda0dfb 100644
--- a/clang/test/AST/Interp/cxx23.cpp
+++ b/clang/test/AST/Interp/cxx23.cpp
@@ -1,55 +1,56 @@
// UNSUPPORTED: target={{.*}}-zos{{.*}}
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=ref,ref20,all,all20 %s
-// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref,ref23,all %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=ref,ref23,all,all23 %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20,all,all20 %s -fexperimental-new-constant-interpreter
-// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23,all,all23 %s -fexperimental-new-constant-interpreter
-/// 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}}
- 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}}
+constexpr int f(int n) { // all20-error {{constexpr function never produces a constant expression}}
+ static const int m = n; // all-note {{control flows through the definition of a static variable}} \
+ // all20-note {{control flows through the definition of a static variable}} \
+ // all20-warning {{is a C++23 extension}}
return m;
}
-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}}
+static_assert(f(0) == 0, ""); // all-error {{not an integral constant expression}} \
+ // all-note {{in call to}}
+
+constexpr int g(int n) { // all20-error {{constexpr function never produces a constant expression}}
+ thread_local const int m = n; // all-note {{control flows through the definition of a thread_local variable}} \
+ // all20-note {{control flows through the definition of a thread_local variable}} \
+ // all20-warning {{is a C++23 extension}}
return m;
}
+static_assert(g(0) == 0, ""); // all-error {{not an integral constant expression}} \
+ // all-note {{in call to}}
-constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // expected20-error {{constexpr function never produces a constant expression}}
- static _Thread_local int m = 0; // 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 {{read of non-const variable}}
+constexpr int c_thread_local(int n) { // all20-error {{constexpr function never produces a constant expression}}
+ static _Thread_local int m = 0; // all20-note 2{{control flows through the definition of a thread_local variable}} \
+ // all23-note {{control flows through the definition of a thread_local variable}} \
+ // all20-warning {{is a C++23 extension}}
+ return m;
}
+static_assert(c_thread_local(0) == 0, ""); // all-error {{not an integral constant expression}} \
+ // all-note {{in call to}}
-constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // expected20-error {{constexpr function never produces a constant expression}}
- static __thread int m = 0; // 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 {{read of non-const variable}}
+constexpr int gnu_thread_local(int n) { // all20-error {{constexpr function never produces a constant expression}}
+ static __thread int m = 0; // all20-note 2{{control flows through the definition of a thread_local variable}} \
+ // all23-note {{control flows through the definition of a thread_local variable}} \
+ // all20-warning {{is a C++23 extension}}
+ return m;
}
+static_assert(gnu_thread_local(0) == 0, ""); // all-error {{not an integral constant expression}} \
+ // all-note {{in call to}}
-constexpr int h(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}}
+constexpr int h(int n) { // all20-error {{constexpr function never produces a constant expression}}
+ static const int m = n; // all20-note {{control flows through the definition of a static variable}} \
+ // all20-warning {{is a C++23 extension}}
return &m - &m;
}
-constexpr int i(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}}
+constexpr int i(int n) { // all20-error {{constexpr function never produces a constant expression}}
+ thread_local const int m = n; // all20-note {{control flows through the definition of a thread_local variable}} \
+ // all20-warning {{is a C++23 extension}}
return &m - &m;
}
More information about the cfe-commits
mailing list