https://github.com/aalmkainzi updated 
https://github.com/llvm/llvm-project/pull/228776

>From dd487bdfdcc50fccb07b93d4d2029e8becdcb3ea Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <[email protected]>
Date: Sun, 4 Oct 2026 00:24:23 +0300
Subject: [PATCH 1/9] made CheckLValueConstantExpression return false when
 initializing constexpr pointer variable with non-null in C23

---
 clang/lib/AST/ExprConstant.cpp                    |  8 ++++++++
 clang/test/Parser/c23-reject-non-null-constexpr.c | 12 ++++++++++++
 2 files changed, 20 insertions(+)
 create mode 100644 clang/test/Parser/c23-reject-non-null-constexpr.c

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3f295f35d1361..f40cf9e8e316b 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2175,6 +2175,14 @@ static bool CheckLValueConstantExpression(EvalInfo 
&Info, SourceLocation Loc,
                                           QualType Type, const LValue &LVal,
                                           ConstantExprKind Kind,
                                           CheckedTemporaries &CheckedTemps) {
+  if (Info.getLangOpts().C23 && !LVal.isNullPointer()) {
+    auto D = Info.EvaluatingDecl;
+    if (auto *ValD = D.dyn_cast<const ValueDecl *>())
+      if (auto *VarD = dyn_cast<VarDecl>(ValD))
+        if (VarD->isConstexpr())
+          return false;
+  }
+
   bool IsReferenceType = Type->isReferenceType();
 
   APValue::LValueBase Base = LVal.getLValueBase();
diff --git a/clang/test/Parser/c23-reject-non-null-constexpr.c 
b/clang/test/Parser/c23-reject-non-null-constexpr.c
new file mode 100644
index 0000000000000..148b08a7ff1f8
--- /dev/null
+++ b/clang/test/Parser/c23-reject-non-null-constexpr.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -std=c23 -verify -pedantic %s
+
+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 variable 
'c2' must be initialized by a constant expression}}
+
+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 
variable 'u2' must be initialized by a constant expression}}
+constexpr union U u3 = {.x = 1};
+constexpr union U u4 = (union U){.x = 1};

>From 65ec1613caf082b3e6f019228bc95987f4a6bb0a Mon Sep 17 00:00:00 2001
From: aalmkainzi <[email protected]>
Date: Sun, 4 Oct 2026 10:41:48 +0300
Subject: [PATCH 2/9] moved test to Sema tests

---
 clang/test/{Parser => Sema}/c23-reject-non-null-constexpr.c | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename clang/test/{Parser => Sema}/c23-reject-non-null-constexpr.c (100%)

diff --git a/clang/test/Parser/c23-reject-non-null-constexpr.c 
b/clang/test/Sema/c23-reject-non-null-constexpr.c
similarity index 100%
rename from clang/test/Parser/c23-reject-non-null-constexpr.c
rename to clang/test/Sema/c23-reject-non-null-constexpr.c

>From 754c698043dbf3dc47b9381bf2d4561a29aa4b88 Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <[email protected]>
Date: Wed, 7 Oct 2026 00:58:57 +0300
Subject: [PATCH 3/9] moved the check to Perform

---
 clang/lib/AST/ExprConstant.cpp                |  8 -----
 clang/lib/Sema/SemaInit.cpp                   | 29 +++++++++++++++----
 .../test/Sema/c23-reject-non-null-constexpr.c | 12 --------
 clang/test/Sema/constexpr.c                   | 12 ++++++++
 4 files changed, 36 insertions(+), 25 deletions(-)
 delete mode 100644 clang/test/Sema/c23-reject-non-null-constexpr.c

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f40cf9e8e316b..3f295f35d1361 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2175,14 +2175,6 @@ static bool CheckLValueConstantExpression(EvalInfo 
&Info, SourceLocation Loc,
                                           QualType Type, const LValue &LVal,
                                           ConstantExprKind Kind,
                                           CheckedTemporaries &CheckedTemps) {
-  if (Info.getLangOpts().C23 && !LVal.isNullPointer()) {
-    auto D = Info.EvaluatingDecl;
-    if (auto *ValD = D.dyn_cast<const ValueDecl *>())
-      if (auto *VarD = dyn_cast<VarDecl>(ValD))
-        if (VarD->isConstexpr())
-          return false;
-  }
-
   bool IsReferenceType = Type->isReferenceType();
 
   APValue::LValueBase Base = LVal.getLValueBase();
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 3f30e94aa8976..97f876f02983f 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7922,6 +7922,24 @@ ExprResult Sema::PerformQualificationConversion(Expr *E, 
QualType Ty,
   return ImpCastExprToType(E, Ty, CK, VK, /*BasePath=*/nullptr, CCK);
 }
 
+// If C2y allows non-null constexpr pointers, this function should always 
return
+// false.
+static bool MaybeRejectNullPointers(APValue &Val) {
+  if (Val.isStruct()) {
+    unsigned int N = Val.getStructNumFields();
+    for (unsigned int i = 0; i < N; i++) {
+      if (MaybeRejectNullPointers(Val.getStructField(i)))
+        return true;
+    }
+  } else if (Val.isUnion()) {
+    APValue UV = Val.getUnionValue();
+    return MaybeRejectNullPointers(UV);
+  } else if (Val.isLValue() && !Val.isNullPointer()) {
+    return true;
+  }
+  return false;
+}
+
 ExprResult InitializationSequence::Perform(Sema &S,
                                            const InitializedEntity &Entity,
                                            const InitializationKind &Kind,
@@ -8580,11 +8598,12 @@ ExprResult InitializationSequence::Perform(Sema &S,
         // 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())) {
-          S.Diag(Kind.getLocation(), diag::err_c23_constexpr_pointer_not_null);
-          return ExprError();
+        if (CurInit.get()->EvaluateAsRValue(ER, S.Context)) {
+          if (MaybeRejectNullPointers(ER.Val)) {
+            S.Diag(Kind.getLocation(),
+                   diag::err_c23_constexpr_pointer_not_null);
+            return ExprError();
+          }
         }
       }
 
diff --git a/clang/test/Sema/c23-reject-non-null-constexpr.c 
b/clang/test/Sema/c23-reject-non-null-constexpr.c
deleted file mode 100644
index 148b08a7ff1f8..0000000000000
--- a/clang/test/Sema/c23-reject-non-null-constexpr.c
+++ /dev/null
@@ -1,12 +0,0 @@
-// RUN: %clang_cc1 -std=c23 -verify -pedantic %s
-
-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 variable 
'c2' must be initialized by a constant expression}}
-
-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 
variable 'u2' must be initialized by a constant expression}}
-constexpr union U u3 = {.x = 1};
-constexpr union U u4 = (union U){.x = 1};
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index 0a9b5724a8343..d9d2e535951d6 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -470,3 +470,15 @@ 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}}
+
+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};

>From 3c825ed6c09022398155fd96c19438652b9f636d Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <[email protected]>
Date: Wed, 7 Oct 2026 01:45:43 +0300
Subject: [PATCH 4/9] renamed the helper function

---
 clang/lib/Sema/SemaInit.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 97f876f02983f..35261a865a4c4 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7924,16 +7924,16 @@ ExprResult Sema::PerformQualificationConversion(Expr 
*E, QualType Ty,
 
 // If C2y allows non-null constexpr pointers, this function should always 
return
 // false.
-static bool MaybeRejectNullPointers(APValue &Val) {
+static bool MaybeRejectNonNullPointers(APValue &Val) {
   if (Val.isStruct()) {
     unsigned int N = Val.getStructNumFields();
     for (unsigned int i = 0; i < N; i++) {
-      if (MaybeRejectNullPointers(Val.getStructField(i)))
+      if (MaybeRejectNonNullPointers(Val.getStructField(i)))
         return true;
     }
   } else if (Val.isUnion()) {
     APValue UV = Val.getUnionValue();
-    return MaybeRejectNullPointers(UV);
+    return MaybeRejectNonNullPointers(UV);
   } else if (Val.isLValue() && !Val.isNullPointer()) {
     return true;
   }
@@ -8599,7 +8599,7 @@ ExprResult InitializationSequence::Perform(Sema &S,
         // respectively.
         Expr::EvalResult ER;
         if (CurInit.get()->EvaluateAsRValue(ER, S.Context)) {
-          if (MaybeRejectNullPointers(ER.Val)) {
+          if (MaybeRejectNonNullPointers(ER.Val)) {
             S.Diag(Kind.getLocation(),
                    diag::err_c23_constexpr_pointer_not_null);
             return ExprError();

>From 011d3d7c8647c76e834c18c39329cd920e8ea090 Mon Sep 17 00:00:00 2001
From: aalmkainzi <[email protected]>
Date: Wed, 7 Oct 2026 09:29:16 +0300
Subject: [PATCH 5/9] renamed the constexpr checking function to
 'MaybeRejectNonNullPointersInConstexprContext', and moved the C23 standard
 quote to it

---
 clang/lib/Sema/SemaInit.cpp | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 35261a865a4c4..23e6f0323ef34 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7922,18 +7922,21 @@ ExprResult Sema::PerformQualificationConversion(Expr 
*E, QualType Ty,
   return ImpCastExprToType(E, Ty, CK, VK, /*BasePath=*/nullptr, CCK);
 }
 
-// If C2y allows non-null constexpr pointers, this function should always 
return
-// false.
-static bool MaybeRejectNonNullPointers(APValue &Val) {
+// 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 MaybeRejectNonNullPointersInConstexprContext(APValue &Val) {
   if (Val.isStruct()) {
     unsigned int N = Val.getStructNumFields();
     for (unsigned int i = 0; i < N; i++) {
-      if (MaybeRejectNonNullPointers(Val.getStructField(i)))
+      if (MaybeRejectNonNullPointersInConstexprContext(Val.getStructField(i)))
         return true;
     }
   } else if (Val.isUnion()) {
     APValue UV = Val.getUnionValue();
-    return MaybeRejectNonNullPointers(UV);
+    return MaybeRejectNonNullPointersInConstexprContext(UV);
   } else if (Val.isLValue() && !Val.isNullPointer()) {
     return true;
   }
@@ -8592,14 +8595,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 (CurInit.get()->EvaluateAsRValue(ER, S.Context)) {
-          if (MaybeRejectNonNullPointers(ER.Val)) {
+          if (MaybeRejectNonNullPointersInConstexprContext(ER.Val)) {
             S.Diag(Kind.getLocation(),
                    diag::err_c23_constexpr_pointer_not_null);
             return ExprError();

>From 954e88ef04978b3077eb1229cb25ce9a0a64c76a Mon Sep 17 00:00:00 2001
From: aalmkainzi <[email protected]>
Date: Wed, 7 Oct 2026 09:40:28 +0300
Subject: [PATCH 6/9] added more test cases

---
 clang/test/Sema/constexpr.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index d9d2e535951d6..29dab2e3e304f 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -476,9 +476,14 @@ 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}}
+}

>From 4eeedd22311d177f05a8ee30aada3a3d8cabd1f2 Mon Sep 17 00:00:00 2001
From: aalmkainzi <[email protected]>
Date: Wed, 7 Oct 2026 09:53:39 +0300
Subject: [PATCH 7/9] take reference instead of copy

---
 clang/lib/Sema/SemaInit.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 23e6f0323ef34..c22fa05a31a46 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7935,7 +7935,7 @@ static bool 
MaybeRejectNonNullPointersInConstexprContext(APValue &Val) {
         return true;
     }
   } else if (Val.isUnion()) {
-    APValue UV = Val.getUnionValue();
+    APValue &UV = Val.getUnionValue();
     return MaybeRejectNonNullPointersInConstexprContext(UV);
   } else if (Val.isLValue() && !Val.isNullPointer()) {
     return true;

>From 9e343bf7c358af245eb3aba3a9eb6e80d11258bb Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <[email protected]>
Date: Wed, 7 Oct 2026 18:20:54 +0300
Subject: [PATCH 8/9] checking if isValueDependent before evaluating

---
 clang/lib/Sema/SemaInit.cpp | 3 ++-
 clang/test/Sema/constexpr.c | 6 ++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index c22fa05a31a46..f37cfa860f0e7 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8596,7 +8596,8 @@ ExprResult InitializationSequence::Perform(Sema &S,
                                         CurInit.get());
 
         Expr::EvalResult ER;
-        if (CurInit.get()->EvaluateAsRValue(ER, S.Context)) {
+        if (!CurInit.get()->isValueDependent() &&
+            CurInit.get()->EvaluateAsRValue(ER, S.Context)) {
           if (MaybeRejectNonNullPointersInConstexprContext(ER.Val)) {
             S.Diag(Kind.getLocation(),
                    diag::err_c23_constexpr_pointer_not_null);
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index 29dab2e3e304f..4bd4de99a38f5 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -478,6 +478,12 @@ constexpr struct C c1 = {&a}; // expected-error{{constexpr 
pointer initializer i
 constexpr struct C c2 = (struct C){&a}; // expected-error{{constexpr pointer 
initializer is not null}}
 constexpr struct C c3 = (struct C){nullptr};
 
+#include <stddef.h>
+
+nullptr_t woo = nullptr;
+constexpr struct C c4 = {woo};
+constexpr struct C c5 = (struct C){woo};
+
 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}}

>From 1d4bb394d6d220dd11f30eda3f62e0dbe38652e0 Mon Sep 17 00:00:00 2001
From: Abdulmalek Almkainzi <[email protected]>
Date: Wed, 7 Oct 2026 19:49:43 +0300
Subject: [PATCH 9/9] Update clang/lib/Sema/SemaInit.cpp

Co-authored-by: Timm Baeder <[email protected]>
---
 clang/lib/Sema/SemaInit.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index f37cfa860f0e7..0b297a991ed0b 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7927,7 +7927,7 @@ ExprResult Sema::PerformQualificationConversion(Expr *E, 
QualType Ty,
 // explicit initializer value for it shall be null, an integer
 // constant expression, or an arithmetic constant expression,
 // respectively.
-static bool MaybeRejectNonNullPointersInConstexprContext(APValue &Val) {
+static bool maybeRejectNonNullPointersInConstexprContext(const APValue &Val) {
   if (Val.isStruct()) {
     unsigned int N = Val.getStructNumFields();
     for (unsigned int i = 0; i < N; i++) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to