[clang] [clang] Implement constexpr DesignatedInitUpdateExpr. (PR #196427)
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)
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)
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)
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)
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)
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)
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) {
