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

Reply via email to