llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Eli Friedman (efriedma-quic)

<details>
<summary>Changes</summary>

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<Emitter>::visitInitList(ArrayRef<const 
Expr *> 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<NoInitExpr>(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<Emitter>::visitInitList(ArrayRef<const 
Expr *> Inits,
         };
         if (!EmbedS->doForEachDataElement(Eval, ElementIndex))
           return false;
+      } else if (isa<NoInitExpr>(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<Emitter>::visitInitList(ArrayRef<const Expr 
*> Inits,
 
     // Expand the filler expression.
     // FIXME: This should go away.
-    if (ArrayFiller) {
+    if (ArrayFiller && !isa<NoInitExpr>(ArrayFiller)) {
       for (; ElementIndex != NumElems; ++ElementIndex) {
         if (!this->visitArrayElemInit(ElementIndex, ArrayFiller, InitT))
           return false;
@@ -7633,6 +7644,14 @@ bool Compiler<Emitter>::VisitDeclRefExpr(const 
DeclRefExpr *E) {
   return this->visitDeclRef(D, E);
 }
 
+template <class Emitter>
+bool Compiler<Emitter>::VisitDesignatedInitUpdateExpr(const 
DesignatedInitUpdateExpr *E) {
+  assert(E->getType()->isRecordType());
+  if (!this->visitInitializer(E->getBase()))
+    return false;
+  return this->visitInitializer(E->getUpdater());
+}
+
 template <class Emitter> bool Compiler<Emitter>::emitCleanup() {
   for (VariableScope<Emitter> *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<Compiler<Emitter>, 
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<Expr *> 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<NoInitExpr>(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) {
   assert(!E->isValueDependent());
@@ -15074,6 +15087,11 @@ bool 
ArrayExprEvaluator::VisitCXXParenListOrInitListExpr(
     if (Init->isValueDependent())
       return EvaluateDependentExpr(Init, Info);
 
+    // If this is a child of a DesignatedInitUpdateExpr, skip elements which
+    // aren't supposed to be modified.
+    if (isa<NoInitExpr>(Init))
+      return true;
+
     if (!EvaluateInPlace(Result.getArrayInitializedElt(ArrayIndex), Info,
                          Subobject, Init) ||
         !HandleLValueArrayAdjustment(Info, Init, Subobject,
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index 0b8de906e1838..79f9f877af12f 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -431,3 +431,17 @@ int gh173605(int x) {
   static int justincase = justincase; // expected-error {{initializer element 
is not a compile-time constant}}
   return x;
 }
+
+
+struct designated_init_A { int a; int b; int c : 2; double d; int e[5]; };
+struct designated_init_B { struct designated_init_A a; int y; };
+constexpr struct designated_init_A designated_init_a = {1, 2};
+constexpr struct designated_init_B designated_init_b =
+  {designated_init_a, .a.a = 3, .a.c = 4, .a.d = 5.0, .a.e[1] = 6, .y = 7}; // 
expected-warning 4 {{initializer partially overrides prior initialization of 
this subobject}} // expected-note 4 {{previous initialization}}
+static_assert(designated_init_b.a.a == 3);
+static_assert(designated_init_b.a.b == 2);
+static_assert(designated_init_b.a.c == 0);
+static_assert(designated_init_b.a.d == 5.0); // expected-warning {{folding it 
to a constant is a GNU extension}}
+static_assert(designated_init_b.a.e[0] == 0); // expected-warning {{folding it 
to a constant is a GNU extension}}
+static_assert(designated_init_b.a.e[1] == 6); // expected-warning {{folding it 
to a constant is a GNU extension}}
+static_assert(designated_init_b.y == 7);

``````````

</details>


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

Reply via email to