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
