https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/182770
>From 5b51c2654d2bb8f859ff3fa61efb9ce2a00563a7 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Sun, 22 Feb 2026 18:23:55 +0200 Subject: [PATCH 1/5] [Clang] support C23 constexpr struct member access in constant expressions --- clang/docs/ReleaseNotes.rst | 1 + clang/lib/AST/ExprConstant.cpp | 21 ++++++++- clang/test/C/drs/dr4xx.c | 2 +- clang/test/Sema/constexpr-member-access.c | 54 +++++++++++++++++++++++ 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 clang/test/Sema/constexpr-member-access.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6e9e5baea2921..ed555627977ee 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -130,6 +130,7 @@ C2y Feature Support C23 Feature Support ^^^^^^^^^^^^^^^^^^^ +- Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349) Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 2c13befec02f2..c249624ff4cda 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -21009,7 +21009,6 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::ArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: - case Expr::MemberExprClass: case Expr::CompoundAssignOperatorClass: case Expr::CompoundLiteralExprClass: case Expr::ExtVectorElementExprClass: @@ -21087,6 +21086,26 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::HLSLOutArgExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); + case Expr::MemberExprClass: { + if (Ctx.getLangOpts().C23) { + const Expr *ME = E->IgnoreParenImpCasts(); + while (const auto *M = dyn_cast<MemberExpr>(ME)) { + if (M->isArrow()) + return ICEDiag(IK_NotICE, E->getBeginLoc()); + ME = M->getBase()->IgnoreParenImpCasts(); + } + const auto *DRE = dyn_cast<DeclRefExpr>(ME); + if (DRE) { + const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (VD && VD->isConstexpr()) + return CheckEvalInICE(E, Ctx); + } + if (isa<CompoundLiteralExpr>(ME)) + return CheckEvalInICE(E, Ctx); + } + return ICEDiag(IK_NotICE, E->getBeginLoc()); + } + case Expr::InitListExprClass: { // C++03 [dcl.init]p13: If T is a scalar type, then a declaration of the // form "T x = { a };" is equivalent to "T x = a;". diff --git a/clang/test/C/drs/dr4xx.c b/clang/test/C/drs/dr4xx.c index 83d7b94cd6795..fda5e437b8759 100644 --- a/clang/test/C/drs/dr4xx.c +++ b/clang/test/C/drs/dr4xx.c @@ -106,7 +106,7 @@ void dr413(void) { * not 0. */ _Static_assert((S){ /* c89only-warning {{compound literals are a C99-specific feature}} - expected-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} + pre-c23-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} */ 1, .t = { /* c89only-warning {{designated initializers are a C99 feature}} */ diff --git a/clang/test/Sema/constexpr-member-access.c b/clang/test/Sema/constexpr-member-access.c new file mode 100644 index 0000000000000..a2cf3cb56f5d5 --- /dev/null +++ b/clang/test/Sema/constexpr-member-access.c @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -std=c23 -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion -Wno-div-by-zero %s +// RUN: %clang_cc1 -std=c23 -verify -triple x86_64 -pedantic -Wno-conversion -Wno-constant-conversion -Wno-div-by-zero -fexperimental-new-constant-interpreter %s + +#define comptime_if(predicate, on_true, on_false) \ + _Generic((char (*)[1 + !!(predicate)]){0}, \ + char (*)[2]: (on_true), \ + char (*)[1]: (on_false)) + +enum E { E_A = 0, E_B = 1 }; +union U { + int a; + char b; +}; +struct S1 { + int a; +}; +struct S2 { + int a; + int b; + _Bool c; + char d; + enum E e; + struct S1 f; + double g; + int h; +}; + +constexpr struct S2 V1 = {0, 1, 1, 'c', E_B, {3}, 1.0, -1}; +constexpr union U V2 = {5}; +constexpr int V3 = V1.f.a; +constexpr int V4 = ((struct S2){0, 4, 0, 0, E_A, {6}, 0.0, 0}).f.a; + +void gh178349() { + int a[V1.b] = {}; + int b[V1.g] = {}; // expected-error {{size of array has non-integer type 'double'}} + int c[V1.h] = {}; // expected-error {{'c' declared as an array with a negative size}} + + const struct S2 *P1 = &V1; + _Static_assert(P1->b, ""); // expected-error {{static assertion expression is not an integral constant expression}} + _Static_assert(V1.b, ""); + + _Static_assert(comptime_if(V1.a, 1, 0) == 0, ""); + _Static_assert(comptime_if(V1.a, 0, 1) == 1, ""); + _Static_assert(comptime_if(V2.a, 1, 0) == 1, ""); + + _Static_assert(V1.c, ""); + _Static_assert(V1.d == 'c', ""); + _Static_assert(V1.e == E_B, ""); + _Static_assert(V1.f.a == 3, ""); + + _Static_assert(V2.a == 5, ""); + _Static_assert(V3 == 3, ""); + _Static_assert(V4 == 6, ""); +} >From c0fb8706faf2ca13c6292a45412254548e819029 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Mon, 23 Feb 2026 15:45:13 +0200 Subject: [PATCH 2/5] remove compound literal --- clang/lib/AST/ExprConstant.cpp | 6 ++---- clang/test/C/drs/dr4xx.c | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c249624ff4cda..ff5daef24c0a5 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -21096,12 +21096,10 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { } const auto *DRE = dyn_cast<DeclRefExpr>(ME); if (DRE) { - const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (VD && VD->isConstexpr()) + if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); + VD && VD->isConstexpr()) return CheckEvalInICE(E, Ctx); } - if (isa<CompoundLiteralExpr>(ME)) - return CheckEvalInICE(E, Ctx); } return ICEDiag(IK_NotICE, E->getBeginLoc()); } diff --git a/clang/test/C/drs/dr4xx.c b/clang/test/C/drs/dr4xx.c index fda5e437b8759..83d7b94cd6795 100644 --- a/clang/test/C/drs/dr4xx.c +++ b/clang/test/C/drs/dr4xx.c @@ -106,7 +106,7 @@ void dr413(void) { * not 0. */ _Static_assert((S){ /* c89only-warning {{compound literals are a C99-specific feature}} - pre-c23-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} + expected-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}} */ 1, .t = { /* c89only-warning {{designated initializers are a C99 feature}} */ >From 901e762f8d17a20da355f386d760784b6d9a0576 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Mon, 23 Feb 2026 15:45:34 +0200 Subject: [PATCH 3/5] add codegen test --- .../CodeGen/c23-constexpr-member-access.c | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 clang/test/CodeGen/c23-constexpr-member-access.c diff --git a/clang/test/CodeGen/c23-constexpr-member-access.c b/clang/test/CodeGen/c23-constexpr-member-access.c new file mode 100644 index 0000000000000..2ae0a306cfb6e --- /dev/null +++ b/clang/test/CodeGen/c23-constexpr-member-access.c @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c23 -emit-llvm -o - %s | FileCheck %s +struct S { + int s; + int e; +}; + +static constexpr struct S V = { + .s = 9, + .e = (1ULL << V.s), +}; + +extern void f(int *); + +// CHECK-LABEL: define{{.*}} void @t1() +// CHECK: %arr = alloca [10 x i32], align 4 +void t1(void) { + int arr[10]; + f(arr); +} + +// CHECK-LABEL: define{{.*}} void @t2() +// CHECK: %arr = alloca [512 x i32], align 4 +void t2(void) { + int arr[V.e]; + f(arr); +} + +// CHECK-LABEL: define{{.*}} void @t3( +// CHECK: %saved_stack = alloca ptr +// CHECK: %vla = alloca i32, i64 +void t3(int n) { + int arr[n]; + f(arr); +} >From 5df427ea0a54f09bdc2f1c589b615976640af65e Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Mon, 23 Feb 2026 16:49:31 +0200 Subject: [PATCH 4/5] specify -triple --- clang/test/CodeGen/c23-constexpr-member-access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/CodeGen/c23-constexpr-member-access.c b/clang/test/CodeGen/c23-constexpr-member-access.c index 2ae0a306cfb6e..a1d2e3f39c29d 100644 --- a/clang/test/CodeGen/c23-constexpr-member-access.c +++ b/clang/test/CodeGen/c23-constexpr-member-access.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c23 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -emit-llvm -o - %s | FileCheck %s struct S { int s; int e; >From 598c1ae649716c97f0364cabb68e67b95d26ae54 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Mon, 23 Feb 2026 17:09:08 +0200 Subject: [PATCH 5/5] update test baseline --- clang/test/CodeGen/c23-constexpr-member-access.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/CodeGen/c23-constexpr-member-access.c b/clang/test/CodeGen/c23-constexpr-member-access.c index a1d2e3f39c29d..529cbd1bb2512 100644 --- a/clang/test/CodeGen/c23-constexpr-member-access.c +++ b/clang/test/CodeGen/c23-constexpr-member-access.c @@ -12,14 +12,14 @@ static constexpr struct S V = { extern void f(int *); // CHECK-LABEL: define{{.*}} void @t1() -// CHECK: %arr = alloca [10 x i32], align 4 +// CHECK: %arr = alloca [10 x i32], align 16 void t1(void) { int arr[10]; f(arr); } // CHECK-LABEL: define{{.*}} void @t2() -// CHECK: %arr = alloca [512 x i32], align 4 +// CHECK: %arr = alloca [512 x i32], align 16 void t2(void) { int arr[V.e]; f(arr); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
