[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