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

Reply via email to