Author: Oleksandr Tarasiuk
Date: 2026-02-27T13:36:06+02:00
New Revision: 6b91049f44d21c5810703ccee23672875001d5ad

URL: 
https://github.com/llvm/llvm-project/commit/6b91049f44d21c5810703ccee23672875001d5ad
DIFF: 
https://github.com/llvm/llvm-project/commit/6b91049f44d21c5810703ccee23672875001d5ad.diff

LOG: [Clang] support C23 constexpr struct member access in constant expressions 
(#182770)

Fixes #178349

--- 

This patch resolves an issue where accessing C23 `constexpr` struct
members using the dot operator was not recognized as a constant
expression.

According to C23 spec:

> 6.6p7:
> 
> An identifier that is:
> — an enumeration constant,
> — a predefined constant, or
> — declared with storage-class specifier constexpr and has an object
type,
> is a named constant, as is a postfix expression that applies the .
member access operator to a named
> constant of structure or union type, even recursively. For enumeration
and predefined constants,
> their value and type are defined in the respective clauses; for
constexpr objects, such a named
> constant is a constant expression with the type and value of the
declared object.
> 
> § 6.6p13:
> 
> A structure or union constant is a named constant or compound literal
constant with structure or
> union type, respectively
> 
> § 6.6p15:
> 
> Starting from a structure or union constant, the member-access .
operator may be used to form a
> named constant or compound literal constant as described previously in
this subclause

Added: 
    clang/test/CodeGen/c23-constexpr-member-access.c
    clang/test/Sema/constexpr-member-access.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/AST/ExprConstant.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 88023779e4bb7..7921d09e33cbe 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -140,6 +140,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 c7805f7a2dbcd..feea97cd67534 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -21101,7 +21101,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:
@@ -21179,6 +21178,24 @@ 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) {
+        if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+            VD && VD->isConstexpr())
+          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/CodeGen/c23-constexpr-member-access.c 
b/clang/test/CodeGen/c23-constexpr-member-access.c
new file mode 100644
index 0000000000000..529cbd1bb2512
--- /dev/null
+++ b/clang/test/CodeGen/c23-constexpr-member-access.c
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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 16
+void t1(void) {
+    int arr[10];
+    f(arr);
+}
+
+// CHECK-LABEL: define{{.*}} void @t2()
+// CHECK: %arr = alloca [512 x i32], align 16
+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);
+}

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, "");
+}


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

Reply via email to