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/5] 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/5] 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/5] 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/5] 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/5] 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();

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

Reply via email to