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
