[clang] [clang][Interp] Diagnose reads from non-const global variables (PR #71919)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 27 03:55:12 PST 2023
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/71919 at github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/71919
>From 047249032d70e486380e8790915a8edeb70add56 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 27 Nov 2023 12:10:48 +0100
Subject: [PATCH 1/2] [clang][Interp][NFC] Remove unused include
---
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index f45e8624a7741a5..f7f8e6c73d84e21 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -15,7 +15,6 @@
#include "Function.h"
#include "PrimType.h"
#include "Program.h"
-#include "State.h"
using namespace clang;
using namespace clang::interp;
>From 5f0b63a3cfd65893a4424605cccf1679767e8622 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 9 Nov 2023 15:45:05 +0100
Subject: [PATCH 2/2] [clang][Interp] Diagnose reads from non-const global
variables
---
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 2 +-
clang/lib/AST/Interp/Interp.cpp | 44 ++++++++++++++++++------
clang/lib/AST/Interp/Interp.h | 14 ++++++++
clang/lib/AST/Interp/Opcodes.td | 1 +
clang/test/AST/Interp/arrays.cpp | 32 +++++++++++++++++
clang/test/AST/Interp/cxx23.cpp | 25 +++++++++-----
clang/test/AST/Interp/literals.cpp | 27 +++++++++++++--
7 files changed, 122 insertions(+), 23 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index f7f8e6c73d84e21..537ff39b9f8f953 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -2120,7 +2120,7 @@ bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) {
auto GlobalIndex = P.getGlobal(VD);
assert(GlobalIndex); // visitVarDecl() didn't return false.
if (VarT) {
- if (!this->emitGetGlobal(*VarT, *GlobalIndex, VD))
+ if (!this->emitGetGlobalUnchecked(*VarT, *GlobalIndex, VD))
return false;
} else {
if (!this->emitGetPtrGlobal(*GlobalIndex, VD))
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 13b77e9a87725c7..f531a44a5b05e2f 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -53,6 +53,18 @@ static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
return true;
}
+static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
+ const ValueDecl *VD) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc,
+ VD->getType()->isIntegralOrEnumerationType()
+ ? diag::note_constexpr_ltor_non_const_int
+ : diag::note_constexpr_ltor_non_constexpr,
+ 1)
+ << VD;
+ S.Note(VD->getLocation(), diag::note_declared_at);
+}
+
static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK) {
if (Ptr.isActive())
@@ -159,9 +171,7 @@ bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!S.checkingPotentialConstantExpression() && S.getLangOpts().CPlusPlus) {
const auto *VD = Ptr.getDeclDesc()->asValueDecl();
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
- S.Note(VD->getLocation(), diag::note_declared_at);
+ diagnoseNonConstVariable(S, OpPC, VD);
}
return false;
}
@@ -204,6 +214,24 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return true;
}
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
+ assert(Desc);
+ if (const auto *D = Desc->asValueDecl()) {
+ if (const auto *VD = dyn_cast<VarDecl>(D);
+ VD && VD->hasGlobalStorage() &&
+ !(VD->isConstexpr() || VD->getType().isConstQualified())) {
+ diagnoseNonConstVariable(S, OpPC, VD);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ return CheckConstant(S, OpPC, Ptr.getDeclDesc());
+}
+
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return !Ptr.isZero() && !Ptr.isDummy();
}
@@ -292,6 +320,8 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckDummy(S, OpPC, Ptr))
return false;
+ if (!CheckConstant(S, OpPC, Ptr))
+ return false;
if (!CheckLive(S, OpPC, Ptr, AK_Read))
return false;
if (!CheckExtern(S, OpPC, Ptr))
@@ -590,13 +620,7 @@ bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
}
} else if (const auto *VD = dyn_cast<VarDecl>(D)) {
if (!VD->getType().isConstQualified()) {
- S.FFDiag(E,
- VD->getType()->isIntegralOrEnumerationType()
- ? diag::note_constexpr_ltor_non_const_int
- : diag::note_constexpr_ltor_non_constexpr,
- 1)
- << VD;
- S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
+ diagnoseNonConstVariable(S, OpPC, VD);
return false;
}
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 4f7778bdd2ff333..b00eb6a72ea2354 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -78,6 +78,9 @@ bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
/// Checks if a pointer points to const storage.
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+/// Checks if the Descriptor is of a constexpr or const global variable.
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc);
+
/// Checks if a pointer points to a mutable field.
bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
@@ -1005,12 +1008,23 @@ bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
const Block *B = S.P.getGlobal(I);
+
+ if (!CheckConstant(S, OpPC, B->getDescriptor()))
+ return false;
if (B->isExtern())
return false;
S.Stk.push<T>(B->deref<T>());
return true;
}
+/// Same as GetGlobal, but without the checks.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetGlobalUnchecked(InterpState &S, CodePtr OpPC, uint32_t I) {
+ auto *B = S.P.getGlobal(I);
+ S.Stk.push<T>(B->deref<T>());
+ return true;
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
// TODO: emit warning.
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 69068e87d5720ab..e01b6b9eea7dbb4 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -379,6 +379,7 @@ def CheckGlobalCtor : Opcode {}
// [] -> [Value]
def GetGlobal : AccessOpcode;
+def GetGlobalUnchecked : AccessOpcode;
// [Value] -> []
def InitGlobal : AccessOpcode;
// [Value] -> []
diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp
index 34e0086fb9ee8ca..91d64a9bd5e7d55 100644
--- a/clang/test/AST/Interp/arrays.cpp
+++ b/clang/test/AST/Interp/arrays.cpp
@@ -554,3 +554,35 @@ namespace GH69115 {
static_assert(foo2() == 0, "");
#endif
}
+
+namespace NonConstReads {
+#if __cplusplus >= 202002L
+ void *p = nullptr; // ref-note {{declared here}} \
+ // expected-note {{declared here}}
+
+ int arr[!p]; // ref-error {{not allowed at file scope}} \
+ // expected-error {{not allowed at file scope}} \
+ // ref-warning {{variable length arrays}} \
+ // ref-note {{read of non-constexpr variable 'p'}} \
+ // expected-warning {{variable length arrays}} \
+ // expected-note {{read of non-constexpr variable 'p'}}
+ int z; // ref-note {{declared here}} \
+ // expected-note {{declared here}}
+ int a[z]; // ref-error {{not allowed at file scope}} \
+ // expected-error {{not allowed at file scope}} \
+ // ref-warning {{variable length arrays}} \
+ // ref-note {{read of non-const variable 'z'}} \
+ // expected-warning {{variable length arrays}} \
+ // expected-note {{read of non-const variable 'z'}}
+#else
+ void *p = nullptr;
+ int arr[!p]; // ref-error {{not allowed at file scope}} \
+ // expected-error {{not allowed at file scope}}
+ int z;
+ int a[z]; // ref-error {{not allowed at file scope}} \
+ // expected-error {{not allowed at file scope}}
+#endif
+
+ const int y = 0;
+ int yy[y];
+}
diff --git a/clang/test/AST/Interp/cxx23.cpp b/clang/test/AST/Interp/cxx23.cpp
index e284a66626fb331..4cd511f853571c4 100644
--- a/clang/test/AST/Interp/cxx23.cpp
+++ b/clang/test/AST/Interp/cxx23.cpp
@@ -4,9 +4,6 @@
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23 %s -fexperimental-new-constant-interpreter
-// expected23-no-diagnostics
-
-
/// 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}} \
@@ -28,22 +25,32 @@ constexpr int g(int n) { // ref20-error {{constexpr function never produc
}
constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // ref23-error {{constexpr function never produces a constant expression}}
+ // ref23-error {{constexpr function never produces a constant expression}} \
+ // expected20-error {{constexpr function never produces a constant expression}} \
+ // expected23-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}} \
// ref23-note {{control flows through the definition of a thread_local variable}} \
- // expected20-warning {{is a C++23 extension}}
- return m;
+ // expected20-warning {{is a C++23 extension}} \
+ // expected20-note {{declared here}} \
+ // expected23-note {{declared here}}
+ return m; // expected20-note {{read of non-const variable}} \
+ // expected23-note {{read of non-const variable}}
}
constexpr int gnu_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
- // ref23-error {{constexpr function never produces a constant expression}}
+ // ref23-error {{constexpr function never produces a constant expression}} \
+ // expected20-error {{constexpr function never produces a constant expression}} \
+ // expected23-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}} \
// ref23-note {{control flows through the definition of a thread_local variable}} \
- // expected20-warning {{is a C++23 extension}}
- return m;
+ // expected20-warning {{is a C++23 extension}} \
+ // expected20-note {{declared here}} \
+ // expected23-note {{declared here}}
+ return m; // expected20-note {{read of non-const variable}} \
+ // expected23-note {{read of non-const variable}}
}
constexpr int h(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp
index 85adfe551384d27..5d87ddcebb9115e 100644
--- a/clang/test/AST/Interp/literals.cpp
+++ b/clang/test/AST/Interp/literals.cpp
@@ -1177,8 +1177,29 @@ namespace InvalidDeclRefs {
// expected-error {{not an integral constant expression}} \
// expected-note {{initializer of 'b02' is unknown}}
- /// FIXME: This should also be diagnosed in the new interpreter.
- int b03 = 3; // ref-note {{declared here}}
+ int b03 = 3; // ref-note {{declared here}} \
+ // expected-note {{declared here}}
static_assert(b03, ""); // ref-error {{not an integral constant expression}} \
- // ref-note {{read of non-const variable}}
+ // ref-note {{read of non-const variable}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{read of non-const variable}}
+}
+
+namespace NonConstReads {
+ void *p = nullptr; // ref-note {{declared here}} \
+ // expected-note {{declared here}}
+ static_assert(!p, ""); // ref-error {{not an integral constant expression}} \
+ // ref-note {{read of non-constexpr variable 'p'}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{read of non-constexpr variable 'p'}}
+
+ int arr[!p]; // ref-error {{variable length array}} \
+ // expected-error {{variable length array}}
+
+ int z; // ref-note {{declared here}} \
+ // expected-note {{declared here}}
+ static_assert(z == 0, ""); // ref-error {{not an integral constant expression}} \
+ // ref-note {{read of non-const variable 'z'}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{read of non-const variable 'z'}}
}
More information about the cfe-commits
mailing list