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; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits