https://github.com/aalmkainzi created
https://github.com/llvm/llvm-project/pull/230477
for example:
```C
typedef struct {
int *a;
} foo;
int b = 42;
constexpr foo bar1 = (foo){.a = &b};
constexpr foo bar2 = {.a = &b};
```
previously, only bar2 is an error, now both are errors.
The fix was to recursively check if the initializer contains any null pointers,
if so, then reject it.
Fixes https://github.com/llvm/llvm-project/issues/121694
>From 6a29aa20a81b4da0ddff1e941847d9ef850750e9 Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <[email protected]>
Date: Fri, 9 Oct 2026 15:14:48 +0300
Subject: [PATCH] Recursively search for non-null pointers when initializing a
constexpr object, reject if found
---
clang/docs/ReleaseNotes.md | 2 ++
clang/lib/Sema/SemaInit.cpp | 39 +++++++++++++++++++++++++++++--------
clang/test/Sema/constexpr.c | 26 +++++++++++++++++++++++++
3 files changed, 59 insertions(+), 8 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 32116077622b58..7527946bed2a98 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -612,6 +612,8 @@ features cannot lower the translation-unit ABI level;
`-Wunsafe-buffer-usage` to control warnings on `main`'s `argv` parameter,
allowing users to suppress them with `-Wno-unsafe-buffer-usage-main-argv`.
+- Clang now rejects initializing a constexpr struct/union variable with a
compound literal that contains non-null pointers in C23. (#GH121694)
+
### Improvements to Clang's time-trace
### Improvements to Coverage Mapping
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index d2aa8691102aa9..dbdfdf6755c94b 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7932,6 +7932,35 @@ ExprResult Sema::PerformQualificationConversion(Expr *E,
QualType Ty,
return ImpCastExprToType(E, Ty, CK, VK, /*BasePath=*/nullptr, CCK);
}
+static bool containsNonNullPointer(const APValue &Val) {
+ if (Val.isStruct()) {
+ unsigned int N = Val.getStructNumFields();
+ for (unsigned int I = 0; I < N; I++) {
+ if (containsNonNullPointer(Val.getStructField(I)))
+ return true;
+ }
+ } else if (Val.isUnion()) {
+ const APValue &UV = Val.getUnionValue();
+ return containsNonNullPointer(UV);
+ } else if (Val.isLValue() && !Val.isNullPointer()) {
+ return true;
+ }
+ return false;
+}
+
+// C23 6.7.1p6: If an object or subobject declared with storage-class
+// specifier constexpr has pointer, integer, or arithmetic type, any
+// explicit initializer value for it shall be null, an integer
+// constant expression, or an arithmetic constant expression,
+// respectively.
+static bool rejectConstexprValueInC(Sema &S, const APValue &Val) {
+ assert(S.getLangOpts().C23);
+ if (containsNonNullPointer(Val)) {
+ return true;
+ }
+ return false;
+}
+
ExprResult InitializationSequence::Perform(Sema &S,
const InitializedEntity &Entity,
const InitializationKind &Kind,
@@ -8584,15 +8613,9 @@ ExprResult InitializationSequence::Perform(Sema &S,
CheckC23ConstexprInitConversion(S, SourceType, Entity.getType(),
CurInit.get());
- // C23 6.7.1p6: If an object or subobject declared with storage-class
- // specifier constexpr has pointer, integer, or arithmetic type, any
- // explicit initializer value for it shall be null, an integer
- // constant expression, or an arithmetic constant expression,
- // respectively.
Expr::EvalResult ER;
- if (Entity.getType()->getAs<PointerType>() &&
- CurInit.get()->EvaluateAsRValue(ER, S.Context) &&
- (ER.Val.isLValue() && !ER.Val.isNullPointer())) {
+ if (CurInit.get()->EvaluateAsRValue(ER, S.Context) &&
+ rejectConstexprValueInC(S, ER.Val)) {
S.Diag(Kind.getLocation(), diag::err_c23_constexpr_pointer_not_null);
return ExprError();
}
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index 0a9b5724a8343e..07fd25ebedc0a7 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -470,3 +470,29 @@ struct S2 {
const struct S2 s2[2] = {{{"foo"}, 1}, [0].L[2] = 'x'}; // expected-warning
{{initializer partially overrides prior initialization of this subobject}} \
// expected-note
{{previous initialization is here}}
static_assert(s2[0].L[2] == 'x');// expected-warning {{folding it to a
constant is a GNU extension}}
+
+// gh121694
+const int a = 0;
+struct C { const int *p; };
+constexpr struct C c1 = {&a}; // expected-error{{constexpr pointer initializer
is not null}}
+constexpr struct C c2 = (struct C){&a}; // expected-error{{constexpr pointer
initializer is not null}}
+constexpr struct C c3 = (struct C){nullptr};
+
+union U { const int *p; int x; };
+constexpr union U u1 = {.p = &a}; // expected-error{{constexpr pointer
initializer is not null}}
+constexpr union U u2 = (union U){.p = &a}; // expected-error{{constexpr
pointer initializer is not null}}
+constexpr union U u3 = {.x = 1};
+constexpr union U u4 = (union U){.x = 1};
+
+void f() {
+ constexpr int *p2 = &a; // expected-error{{constexpr pointer initializer is
not null}}
+}
+
+typedef typeof(nullptr) nullptr_t;
+
+constexpr nullptr_t woo1 = nullptr;
+constexpr struct C c4 = { woo1 };
+
+const nullptr_t woo2 = nullptr;
+constexpr struct C c5 = { woo2 };
+
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits