[clang] [clang] Implement constexpr DesignatedInitUpdateExpr. (PR #196427)

2026-05-13 Thread Corentin Jabot via cfe-commits

https://github.com/cor3ntin approved this pull request.

The ExprConstant.cpp changes look good to me.
However, can you add a changelog entry? Thanks

https://github.com/llvm/llvm-project/pull/196427
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang] Implement constexpr DesignatedInitUpdateExpr. (PR #196427)

2026-05-09 Thread Eli Friedman via cfe-commits

efriedma-quic wrote:

Ran into an issue I wasn't expecting to run into... pushed revised patch.  Let 
me know if you want me to split the fix for #196450 into a separate patch.

https://github.com/llvm/llvm-project/pull/196427
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang] Implement constexpr DesignatedInitUpdateExpr. (PR #196427)

2026-05-09 Thread Eli Friedman via cfe-commits

https://github.com/efriedma-quic created 
https://github.com/llvm/llvm-project/pull/196427

DesignatedInitUpdateExpr exists to handle some obscure edge cases in C, where 
the usual InitListExpr canonicalization can't be performed. Previously, we 
didn't need constant evaluation for this, but C23 constexpr means we need to 
evaluate this before codegen.

Implementation is straightforward: just need to evaluate the two 
subexpressions, in order, and skip any NoInitExprs.

Fixes #193373.

>From db10d77410fa485c9b47b7178cd9780961fb3c28 Mon Sep 17 00:00:00 2001
From: Eli Friedman 
Date: Thu, 7 May 2026 14:44:28 -0700
Subject: [PATCH] [clang] Implement constexpr DesignatedInitUpdateExpr.

DesignatedInitUpdateExpr exists to handle some obscure edge cases in C,
where the usual InitListExpr canonicalization can't be performed.
Previously, we didn't need constant evaluation for this, but C23
constexpr means we need to evaluate this before codegen.

Implementation is straightforward: just need to evaluate the two
subexperssions, in order, and skip any NoInitExprs.

Fixes #193373.
---
 clang/lib/AST/ByteCode/Compiler.cpp | 21 -
 clang/lib/AST/ByteCode/Compiler.h   |  1 +
 clang/lib/AST/ExprConstant.cpp  | 18 ++
 clang/test/Sema/constexpr.c | 14 ++
 4 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 59510612d9617..d747dff081ef4 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2193,6 +2193,13 @@ bool Compiler::visitInitList(ArrayRef Inits,
  R->getField(InitIndex)->isUnnamedBitField())
 ++InitIndex;
 
+  // If this is a child of a DesignatedInitUpdateExpr, skip elements which
+  // aren't supposed to be modified.
+  if (isa(Init)) {
+++InitIndex;
+continue;
+  }
+
   if (OptPrimType T = classify(Init)) {
 const Record::Field *FieldToInit = R->getField(InitIndex);
 if (!initPrimitiveField(FieldToInit, Init, *T))
@@ -2256,6 +2263,10 @@ bool Compiler::visitInitList(ArrayRef Inits,
 };
 if (!EmbedS->doForEachDataElement(Eval, ElementIndex))
   return false;
+  } else if (isa(Init)) {
+// If this is a child of a DesignatedInitUpdateExpr, skip elements 
which
+// aren't supposed to be modified.
+++ElementIndex;
   } else {
 if (!this->visitArrayElemInit(ElementIndex, Init, InitT))
   return false;
@@ -2265,7 +2276,7 @@ bool Compiler::visitInitList(ArrayRef Inits,
 
 // Expand the filler expression.
 // FIXME: This should go away.
-if (ArrayFiller) {
+if (ArrayFiller && !isa(ArrayFiller)) {
   for (; ElementIndex != NumElems; ++ElementIndex) {
 if (!this->visitArrayElemInit(ElementIndex, ArrayFiller, InitT))
   return false;
@@ -7633,6 +7644,14 @@ bool Compiler::VisitDeclRefExpr(const 
DeclRefExpr *E) {
   return this->visitDeclRef(D, E);
 }
 
+template 
+bool Compiler::VisitDesignatedInitUpdateExpr(const 
DesignatedInitUpdateExpr *E) {
+  assert(E->getType()->isRecordType());
+  if (!this->visitInitializer(E->getBase()))
+return false;
+  return this->visitInitializer(E->getUpdater());
+}
+
 template  bool Compiler::emitCleanup() {
   for (VariableScope *C = VarScope; C; C = C->getParent()) {
 if (!C->destroyLocals())
diff --git a/clang/lib/AST/ByteCode/Compiler.h 
b/clang/lib/AST/ByteCode/Compiler.h
index 4a70db89dba74..45cda575b000e 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -232,6 +232,7 @@ class Compiler : public ConstStmtVisitor, 
bool>,
   bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
   bool VisitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E);
   bool VisitObjCArrayLiteral(const ObjCArrayLiteral *E);
+  bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
 
   // Statements.
   bool visitCompoundStmt(const CompoundStmt *S);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4f45fa728c605..b26afb4449484 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11065,6 +11065,7 @@ namespace {
 bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E);
 bool VisitCXXParenListOrInitListExpr(const Expr *ExprToVisit,
  ArrayRef Args);
+bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
   };
 }
 
@@ -11325,6 +11326,11 @@ bool 
RecordExprEvaluator::VisitCXXParenListOrInitListExpr(
 ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType());
 const Expr *Init = HaveInit ? Args[ElementNo++] : &VIE;
 
+// If this is a child of a DesignatedInitUpdateExpr, skip elements which
+// aren't supposed to be modified.
+if (isa(Init))
+  continue;
+
 if (Field->getType()->isIncompleteArrayType()) {
   if 

[clang] [clang] Implement constexpr DesignatedInitUpdateExpr. (PR #196427)

2026-05-08 Thread Eli Friedman via cfe-commits

https://github.com/efriedma-quic updated 
https://github.com/llvm/llvm-project/pull/196427

>From db10d77410fa485c9b47b7178cd9780961fb3c28 Mon Sep 17 00:00:00 2001
From: Eli Friedman 
Date: Thu, 7 May 2026 14:44:28 -0700
Subject: [PATCH 1/2] [clang] Implement constexpr DesignatedInitUpdateExpr.

DesignatedInitUpdateExpr exists to handle some obscure edge cases in C,
where the usual InitListExpr canonicalization can't be performed.
Previously, we didn't need constant evaluation for this, but C23
constexpr means we need to evaluate this before codegen.

Implementation is straightforward: just need to evaluate the two
subexperssions, in order, and skip any NoInitExprs.

Fixes #193373.
---
 clang/lib/AST/ByteCode/Compiler.cpp | 21 -
 clang/lib/AST/ByteCode/Compiler.h   |  1 +
 clang/lib/AST/ExprConstant.cpp  | 18 ++
 clang/test/Sema/constexpr.c | 14 ++
 4 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 59510612d9617..d747dff081ef4 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2193,6 +2193,13 @@ bool Compiler::visitInitList(ArrayRef Inits,
  R->getField(InitIndex)->isUnnamedBitField())
 ++InitIndex;
 
+  // If this is a child of a DesignatedInitUpdateExpr, skip elements which
+  // aren't supposed to be modified.
+  if (isa(Init)) {
+++InitIndex;
+continue;
+  }
+
   if (OptPrimType T = classify(Init)) {
 const Record::Field *FieldToInit = R->getField(InitIndex);
 if (!initPrimitiveField(FieldToInit, Init, *T))
@@ -2256,6 +2263,10 @@ bool Compiler::visitInitList(ArrayRef Inits,
 };
 if (!EmbedS->doForEachDataElement(Eval, ElementIndex))
   return false;
+  } else if (isa(Init)) {
+// If this is a child of a DesignatedInitUpdateExpr, skip elements 
which
+// aren't supposed to be modified.
+++ElementIndex;
   } else {
 if (!this->visitArrayElemInit(ElementIndex, Init, InitT))
   return false;
@@ -2265,7 +2276,7 @@ bool Compiler::visitInitList(ArrayRef Inits,
 
 // Expand the filler expression.
 // FIXME: This should go away.
-if (ArrayFiller) {
+if (ArrayFiller && !isa(ArrayFiller)) {
   for (; ElementIndex != NumElems; ++ElementIndex) {
 if (!this->visitArrayElemInit(ElementIndex, ArrayFiller, InitT))
   return false;
@@ -7633,6 +7644,14 @@ bool Compiler::VisitDeclRefExpr(const 
DeclRefExpr *E) {
   return this->visitDeclRef(D, E);
 }
 
+template 
+bool Compiler::VisitDesignatedInitUpdateExpr(const 
DesignatedInitUpdateExpr *E) {
+  assert(E->getType()->isRecordType());
+  if (!this->visitInitializer(E->getBase()))
+return false;
+  return this->visitInitializer(E->getUpdater());
+}
+
 template  bool Compiler::emitCleanup() {
   for (VariableScope *C = VarScope; C; C = C->getParent()) {
 if (!C->destroyLocals())
diff --git a/clang/lib/AST/ByteCode/Compiler.h 
b/clang/lib/AST/ByteCode/Compiler.h
index 4a70db89dba74..45cda575b000e 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -232,6 +232,7 @@ class Compiler : public ConstStmtVisitor, 
bool>,
   bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
   bool VisitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E);
   bool VisitObjCArrayLiteral(const ObjCArrayLiteral *E);
+  bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
 
   // Statements.
   bool visitCompoundStmt(const CompoundStmt *S);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4f45fa728c605..b26afb4449484 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11065,6 +11065,7 @@ namespace {
 bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E);
 bool VisitCXXParenListOrInitListExpr(const Expr *ExprToVisit,
  ArrayRef Args);
+bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
   };
 }
 
@@ -11325,6 +11326,11 @@ bool 
RecordExprEvaluator::VisitCXXParenListOrInitListExpr(
 ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType());
 const Expr *Init = HaveInit ? Args[ElementNo++] : &VIE;
 
+// If this is a child of a DesignatedInitUpdateExpr, skip elements which
+// aren't supposed to be modified.
+if (isa(Init))
+  continue;
+
 if (Field->getType()->isIncompleteArrayType()) {
   if (auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType())) {
 if (!CAT->isZeroSize()) {
@@ -11522,6 +11528,13 @@ bool RecordExprEvaluator::VisitLambdaExpr(const 
LambdaExpr *E) {
   return Success;
 }
 
+bool RecordExprEvaluator::VisitDesignatedInitUpdateExpr(
+const DesignatedInitUpdateExpr *E) {
+  if (!Visit(E->getBase()))
+return false;
+  return Visit(E->getUpdater());

[clang] [clang] Implement constexpr DesignatedInitUpdateExpr. (PR #196427)

2026-05-08 Thread Eli Friedman via cfe-commits

https://github.com/efriedma-quic edited 
https://github.com/llvm/llvm-project/pull/196427
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang] Implement constexpr DesignatedInitUpdateExpr. (PR #196427)

2026-05-08 Thread via cfe-commits

github-actions[bot] wrote:




:warning: C/C++ code formatter, clang-format found issues in your code. 
:warning:



You can test this locally with the following command:


``bash
git-clang-format --diff origin/main HEAD --extensions cpp,h,c -- 
clang/lib/AST/ByteCode/Compiler.cpp clang/lib/AST/ByteCode/Compiler.h 
clang/lib/AST/ExprConstant.cpp clang/test/Sema/constexpr.c 
--diff_from_common_commit
``

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:





View the diff from clang-format here.


``diff
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 44fd3d878..d30020cc2 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7754,7 +7754,8 @@ bool Compiler::VisitDeclRefExpr(const 
DeclRefExpr *E) {
 }
 
 template 
-bool Compiler::VisitDesignatedInitUpdateExpr(const 
DesignatedInitUpdateExpr *E) {
+bool Compiler::VisitDesignatedInitUpdateExpr(
+const DesignatedInitUpdateExpr *E) {
   assert(E->getType()->isRecordType());
   if (!this->visitInitializer(E->getBase()))
 return false;

``




https://github.com/llvm/llvm-project/pull/196427
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang] Implement constexpr DesignatedInitUpdateExpr. (PR #196427)

2026-05-08 Thread via cfe-commits

llvmorg-github-actions[bot] wrote:




@llvm/pr-subscribers-clang

Author: Eli Friedman (efriedma-quic)


Changes

DesignatedInitUpdateExpr exists to handle some obscure edge cases in C, where 
the usual InitListExpr canonicalization can't be performed. Previously, we 
didn't need constant evaluation for this, but C23 constexpr means we need to 
evaluate this before codegen.

Implementation is straightforward: just need to evaluate the two 
subexpressions, in order, and skip any NoInitExprs.

Fixes #193373.

---
Full diff: https://github.com/llvm/llvm-project/pull/196427.diff


4 Files Affected:

- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+20-1) 
- (modified) clang/lib/AST/ByteCode/Compiler.h (+1) 
- (modified) clang/lib/AST/ExprConstant.cpp (+18) 
- (modified) clang/test/Sema/constexpr.c (+14) 


``diff
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 59510612d9617..d747dff081ef4 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2193,6 +2193,13 @@ bool Compiler::visitInitList(ArrayRef Inits,
  R->getField(InitIndex)->isUnnamedBitField())
 ++InitIndex;
 
+  // If this is a child of a DesignatedInitUpdateExpr, skip elements which
+  // aren't supposed to be modified.
+  if (isa(Init)) {
+++InitIndex;
+continue;
+  }
+
   if (OptPrimType T = classify(Init)) {
 const Record::Field *FieldToInit = R->getField(InitIndex);
 if (!initPrimitiveField(FieldToInit, Init, *T))
@@ -2256,6 +2263,10 @@ bool Compiler::visitInitList(ArrayRef Inits,
 };
 if (!EmbedS->doForEachDataElement(Eval, ElementIndex))
   return false;
+  } else if (isa(Init)) {
+// If this is a child of a DesignatedInitUpdateExpr, skip elements 
which
+// aren't supposed to be modified.
+++ElementIndex;
   } else {
 if (!this->visitArrayElemInit(ElementIndex, Init, InitT))
   return false;
@@ -2265,7 +2276,7 @@ bool Compiler::visitInitList(ArrayRef Inits,
 
 // Expand the filler expression.
 // FIXME: This should go away.
-if (ArrayFiller) {
+if (ArrayFiller && !isa(ArrayFiller)) {
   for (; ElementIndex != NumElems; ++ElementIndex) {
 if (!this->visitArrayElemInit(ElementIndex, ArrayFiller, InitT))
   return false;
@@ -7633,6 +7644,14 @@ bool Compiler::VisitDeclRefExpr(const 
DeclRefExpr *E) {
   return this->visitDeclRef(D, E);
 }
 
+template 
+bool Compiler::VisitDesignatedInitUpdateExpr(const 
DesignatedInitUpdateExpr *E) {
+  assert(E->getType()->isRecordType());
+  if (!this->visitInitializer(E->getBase()))
+return false;
+  return this->visitInitializer(E->getUpdater());
+}
+
 template  bool Compiler::emitCleanup() {
   for (VariableScope *C = VarScope; C; C = C->getParent()) {
 if (!C->destroyLocals())
diff --git a/clang/lib/AST/ByteCode/Compiler.h 
b/clang/lib/AST/ByteCode/Compiler.h
index 4a70db89dba74..45cda575b000e 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -232,6 +232,7 @@ class Compiler : public ConstStmtVisitor, 
bool>,
   bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
   bool VisitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E);
   bool VisitObjCArrayLiteral(const ObjCArrayLiteral *E);
+  bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
 
   // Statements.
   bool visitCompoundStmt(const CompoundStmt *S);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4f45fa728c605..b26afb4449484 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11065,6 +11065,7 @@ namespace {
 bool VisitCXXParenListInitExpr(const CXXParenListInitExpr *E);
 bool VisitCXXParenListOrInitListExpr(const Expr *ExprToVisit,
  ArrayRef Args);
+bool VisitDesignatedInitUpdateExpr(const DesignatedInitUpdateExpr *E);
   };
 }
 
@@ -11325,6 +11326,11 @@ bool 
RecordExprEvaluator::VisitCXXParenListOrInitListExpr(
 ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType());
 const Expr *Init = HaveInit ? Args[ElementNo++] : &VIE;
 
+// If this is a child of a DesignatedInitUpdateExpr, skip elements which
+// aren't supposed to be modified.
+if (isa(Init))
+  continue;
+
 if (Field->getType()->isIncompleteArrayType()) {
   if (auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType())) {
 if (!CAT->isZeroSize()) {
@@ -11522,6 +11528,13 @@ bool RecordExprEvaluator::VisitLambdaExpr(const 
LambdaExpr *E) {
   return Success;
 }
 
+bool RecordExprEvaluator::VisitDesignatedInitUpdateExpr(
+const DesignatedInitUpdateExpr *E) {
+  if (!Visit(E->getBase()))
+return false;
+  return Visit(E->getUpdater());
+}
+
 static bool EvaluateRecord(const Expr *E, const LValue &This,
APValue &Result, EvalInfo &Info) {