https://github.com/akash-manna-sky updated 
https://github.com/llvm/llvm-project/pull/218710

>From 5c5c2d0f21935270897454527af98be6506df75a Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Tue, 25 Aug 2026 20:49:51 +0530
Subject: [PATCH 1/2] [Clang] Fix crash on expansion-init-list elements that
 need cleanups

The parser wrapped the syntactic expansion-init-list in an
ExprWithCleanups whenever an element needed cleanups (e.g. a temporary
bound to a reference parameter). The list has no type, so the wrapper
had none either, and ActOnCXXExpansionStmtPattern no longer recognised
it as an init list and dereferenced the null type.

Discard those cleanups instead: the elements are only evaluated as the
initializer of the expansion variable in each expansion, where they are
rebuilt anyway. Do the same when building the dependent
CXXExpansionSelectExpr so it can't get wrapped during instantiation
either, which HasDependentSize/ComputeExpansionSize don't expect.

Fixes #212630
---
 clang/docs/ReleaseNotes.md           |  6 ++++
 clang/lib/Parse/ParseStmt.cpp        | 11 +++++--
 clang/lib/Sema/SemaExpand.cpp        |  6 +++-
 clang/test/SemaTemplate/GH212630.cpp | 49 ++++++++++++++++++++++++++++
 4 files changed, 68 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/SemaTemplate/GH212630.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6694f510952..c49428a2a95d0 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -531,6 +531,12 @@ features cannot lower the translation-unit ABI level;
   parameter that follows a parameter pack (e.g.
   `template <typename... T> S::S(T..., int = 10) {}`).  (#GH216211)
 
+- Fixed an assertion failure in an enumerating expansion statement
+  (`template for`) when an element of the expansion-init-list needed cleanups,
+  e.g. a temporary bound to a reference parameter such as `{g(1), g(2)}` with
+  `int g(const int&)`, or a temporary of a type with a non-trivial destructor.
+  (#GH212630)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 219bcd980e860..9f5a37e840c1b 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1965,9 +1965,14 @@ void 
Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
     assert(Actions.CurContext->isExpansionStmt());
     Sema::ContextRAII CtxGuard(Actions, Actions.CurContext->getParent(),
                                /*NewThis=*/false);
-    FRI.RangeExpr =
-        Tok.is(tok::l_brace) ? ParseExpansionInitList() : ParseExpression();
-    FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(FRI.RangeExpr);
+    if (Tok.is(tok::l_brace)) {
+      // The elements are only evaluated as the initializer of the expansion
+      // variable in each expansion, so their cleanups belong there.
+      FRI.RangeExpr = ParseExpansionInitList();
+      Actions.DiscardCleanupsInEvaluationContext();
+    } else {
+      FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(ParseExpression());
+    }
   } else if (Tok.is(tok::l_brace)) {
     FRI.RangeExpr = ParseBraceInitializer();
   } else {
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 779b7add08344..77e7cb282c5a0 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -589,8 +589,12 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
 }
 
 ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) {
-  if (Idx->isValueDependent() || InitListContainsPack(Range))
+  if (Idx->isValueDependent() || InitListContainsPack(Range)) {
+    // The elements are only evaluated by the expansion that selects them, so
+    // their cleanups must not wrap this expression.
+    DiscardCleanupsInEvaluationContext();
     return new (Context) CXXExpansionSelectExpr(Context, Range, Idx);
+  }
 
   // The index is a DRE to a template parameter; we should never
   // fail to evaluate it.
diff --git a/clang/test/SemaTemplate/GH212630.cpp 
b/clang/test/SemaTemplate/GH212630.cpp
new file mode 100644
index 0000000000000..aab9d9d7fd5ad
--- /dev/null
+++ b/clang/test/SemaTemplate/GH212630.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -std=c++26 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+namespace GH212630 {
+
+void f(int g(const int&)) {
+  template for (auto x : {g(1), g(2), g(3)})
+    g(0);
+}
+
+struct M {
+  int m(const int &x) const { return x; }
+};
+
+int overloaded(const int &);
+long overloaded(const long &);
+
+void related(int (*fp)(const int &), int (&fr)(const int &), M m) {
+  template for (auto x : {fp(1), fr(2), m.m(3), overloaded(4), 
overloaded(5L)}) {}
+}
+
+constexpr int h(const int &x) { return x * 2; }
+
+struct S {
+  int v;
+  constexpr S(int v) : v(v) {}
+  constexpr ~S() {}
+};
+
+constexpr int direct() {
+  int sum = 0;
+  template for (auto x : {h(1), h(2), h(3)}) { sum += x; }
+  template for (constexpr auto x : {h(1), h(2), h(3)}) { sum += x; }
+  template for (auto s : {S(1), S(2)}) { sum += s.v; }
+  return sum;
+}
+static_assert(direct() == 27);
+
+// With a pack, the elements are rebuilt when the template is instantiated.
+template <typename... Ts>
+constexpr int pack(Ts... ts) {
+  int sum = 0;
+  template for (auto x : {h(1), h(ts)...}) { sum += x; }
+  template for (auto s : {S(ts)...}) { sum += s.v; }
+  return sum;
+}
+static_assert(pack(2, 3) == 17);
+
+} // namespace GH212630

>From 9e2625031a63d111a906d9ad8c89261748a3d4c4 Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Fri, 28 Aug 2026 12:35:13 +0530
Subject: [PATCH 2/2] [Clang] Implement CWG3043 and fix assertion on
 expansion-init-list cleanups

Each element of an expansion-init-list is now a full-expression of its
own: ParseExpansionInitList() has its own loop again (undoing the
ParseExpressionList() changes) and finishes every element with
MaybeCreateExprWithCleanups, so no cleanups are left pending after it.
Previously the whole (typeless) init list was wrapped in an
ExprWithCleanups, which made ActOnCXXExpansionStmtPattern dereference a
null type. When the pattern is instantiated, its elements are rebuilt
and finished the same way.

Per CWG3043, temporaries in an element persist for the lifetime of the
expansion variable initialized from it. When an expansion is built, only
the selected element is instantiated, in a lifetime-extending context,
and its temporaries are extended to the variable; the same is redone
when an already-expanded statement is instantiated again.

Fixes #212630
---
 clang/docs/ReleaseNotes.md                    |  6 +-
 clang/include/clang/Parse/Parser.h            |  6 +-
 clang/include/clang/Sema/Sema.h               |  6 ++
 clang/lib/Parse/ParseExpr.cpp                 | 14 +---
 clang/lib/Parse/ParseInit.cpp                 | 36 ++++++--
 clang/lib/Parse/ParseStmt.cpp                 | 13 ++-
 clang/lib/Sema/SemaExpand.cpp                 | 69 ++++++++++-----
 clang/lib/Sema/TreeTransform.h                | 84 +++++++++++++++++--
 ...2c-enumerating-expansion-stmt-lifetime.cpp | 79 +++++++++++++++++
 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp  | 62 +++++++++++++-
 10 files changed, 315 insertions(+), 60 deletions(-)
 create mode 100644 
clang/test/CodeGenCXX/cxx2c-enumerating-expansion-stmt-lifetime.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c49428a2a95d0..d9ac19c4d12b5 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -154,6 +154,10 @@ features cannot lower the translation-unit ABI level;
 - Clang now falls back to alignment-aware allocation functions for
   non-overaligned types, implementing [CWG2282](https://wg21.link/cwg2282).
 
+- Implemented [CWG3043](https://wg21.link/cwg3043): temporaries in an element
+  of the expansion-init-list of an enumerating expansion statement now persist
+  for the lifetime of the expansion variable initialized from that element.
+
 ### C Language Changes
 
 #### C2y Feature Support
@@ -535,7 +539,7 @@ features cannot lower the translation-unit ABI level;
   (`template for`) when an element of the expansion-init-list needed cleanups,
   e.g. a temporary bound to a reference parameter such as `{g(1), g(2)}` with
   `int g(const int&)`, or a temporary of a type with a non-trivial destructor.
-  (#GH212630)
+  Each element is now a full-expression of its own. (#GH212630)
 
 #### Bug Fixes to AST Handling
 
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 163aa483a84e3..d63f633c30dc8 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -4232,8 +4232,7 @@ class Parser : public CodeCompletionHandler {
   bool ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
                            llvm::function_ref<void()> ExpressionStarts =
                                llvm::function_ref<void()>(),
-                           bool FailImmediatelyOnInvalidExpr = false,
-                           bool ParsingExpansionStmtInitList = false);
+                           bool FailImmediatelyOnInvalidExpr = false);
 
   /// ParseSimpleExpressionList - A simple comma-separated list of expressions,
   /// used for misc language extensions.
@@ -5320,7 +5319,8 @@ class Parser : public CodeCompletionHandler {
   ExprResult ParseBraceInitializer();
 
   /// ParseExpansionInitList - Called when the initializer of an expansion
-  /// statement starts with an open brace.
+  /// statement starts with an open brace. Each element of the list is a
+  /// full-expression of its own.
   ///
   /// \verbatim
   ///       expansion-init-list: [C++26 [stmt.expand]]
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 418f9b38fa11f..720073f401edc 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15837,6 +15837,12 @@ class Sema final : public SemaBase {
 
   StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
 
+  /// Build the expansion variable of an enumerating expansion statement for
+  /// one expansion using \p Build, extending the lifetime of the temporaries
+  /// in its initializer to the variable.
+  StmtResult
+  BuildEnumeratingExpansionVar(llvm::function_ref<StmtResult()> Build);
+
   StmtResult BuildCXXEnumeratingExpansionStmtPattern(Decl *ESD, Stmt *Init,
                                                      Stmt *ExpansionVar,
                                                      SourceLocation LParenLoc,
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 87cd7a01451cf..0f0502ac0e253 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -3215,8 +3215,7 @@ void Parser::injectEmbedTokens() {
 
 bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
                                  llvm::function_ref<void()> ExpressionStarts,
-                                 bool FailImmediatelyOnInvalidExpr,
-                                 bool ParsingExpansionStmtInitList) {
+                                 bool FailImmediatelyOnInvalidExpr) {
   bool SawError = false;
   while (true) {
     if (ExpressionStarts)
@@ -3245,11 +3244,7 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> 
&Exprs,
       SawError = true;
       if (FailImmediatelyOnInvalidExpr)
         break;
-
-      // We expect '}' rather than ')' at the end of an expansion-init-list.
-      SkipUntil(tok::comma,
-                ParsingExpansionStmtInitList ? tok::r_brace : tok::r_paren,
-                StopAtSemi | StopBeforeMatch);
+      SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
     } else {
       Exprs.push_back(Expr.get());
     }
@@ -3259,11 +3254,6 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> 
&Exprs,
     // Move to the next argument, remember where the comma was.
     Token Comma = Tok;
     ConsumeToken();
-
-    // CWG 3061: Trailing commas are allowed in expansion-init-lists.
-    if (ParsingExpansionStmtInitList && Tok.is(tok::r_brace))
-      break;
-
     checkPotentialAngleBracketDelimiter(Comma);
   }
   return SawError;
diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp
index 40d78b5d3d2a6..b797e4326edb6 100644
--- a/clang/lib/Parse/ParseInit.cpp
+++ b/clang/lib/Parse/ParseInit.cpp
@@ -521,16 +521,40 @@ ExprResult Parser::ParseExpansionInitList() {
   T.consumeOpen();
 
   ExprVector InitExprs;
+  bool SawError = false;
+  while (Tok.isNot(tok::r_brace)) {
+    ExprResult Elem = Tok.is(tok::l_brace) ? ParseBraceInitializer()
+                                           : ParseAssignmentExpression();
+
+    if (Tok.is(tok::code_completion)) {
+      cutOffParsing();
+      SawError = true;
+      break;
+    }
 
-  if (!Tok.is(tok::r_brace) &&
-      ParseExpressionList(InitExprs, /*ExpressionStarts=*/{},
-                          /*FailImmediatelyOnInvalidExpr=*/false,
-                          /*ParsingExpansionStmtInitList=*/true)) {
-    T.consumeClose();
-    return ExprError();
+    // Each element is a full-expression of its own.
+    Elem = Actions.MaybeCreateExprWithCleanups(Elem);
+    if (Tok.is(tok::ellipsis))
+      Elem = Actions.ActOnPackExpansion(Elem.get(), ConsumeToken());
+
+    if (Elem.isInvalid()) {
+      SawError = true;
+      SkipUntil(tok::comma, tok::r_brace, StopAtSemi | StopBeforeMatch);
+    } else {
+      InitExprs.push_back(Elem.get());
+    }
+
+    if (Tok.isNot(tok::comma))
+      break;
+
+    // CWG 3061: A trailing comma is allowed.
+    ConsumeToken();
   }
 
   T.consumeClose();
+  if (SawError)
+    return ExprError();
+
   return Actions.ActOnCXXExpansionInitList(InitExprs, T.getOpenLocation(),
                                            T.getCloseLocation());
 }
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 9f5a37e840c1b..583d420d1def5 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1965,14 +1965,11 @@ void 
Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
     assert(Actions.CurContext->isExpansionStmt());
     Sema::ContextRAII CtxGuard(Actions, Actions.CurContext->getParent(),
                                /*NewThis=*/false);
-    if (Tok.is(tok::l_brace)) {
-      // The elements are only evaluated as the initializer of the expansion
-      // variable in each expansion, so their cleanups belong there.
-      FRI.RangeExpr = ParseExpansionInitList();
-      Actions.DiscardCleanupsInEvaluationContext();
-    } else {
-      FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(ParseExpression());
-    }
+    // The elements of an expansion-init-list are already full-expressions.
+    FRI.RangeExpr =
+        Tok.is(tok::l_brace)
+            ? ParseExpansionInitList()
+            : Actions.MaybeCreateExprWithCleanups(ParseExpression());
   } else if (Tok.is(tok::l_brace)) {
     FRI.RangeExpr = ParseBraceInitializer();
   } else {
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 77e7cb282c5a0..cb24fe1b46e81 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -339,7 +339,6 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
     if (FinalizeExpansionVar(*this, ExpansionVar, Initializer))
       return StmtError();
 
-    // TODO: CWG3043 (lifetime extension in enumerating expansion statements).
     return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc,
                                                    ColonLoc, RParenLoc);
   }
@@ -493,6 +492,25 @@ StmtResult 
Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
       Context, ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, 
RParenLoc);
 }
 
+StmtResult
+Sema::BuildEnumeratingExpansionVar(llvm::function_ref<StmtResult()> Build) {
+  // CWG3043: Temporaries in the element persist for the lifetime of the
+  // expansion variable.
+  EnterExpressionEvaluationContext Ctx(*this,
+                                       currentEvaluationContext().Context);
+  currentEvaluationContext().InLifetimeExtendingContext = true;
+  currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
+
+  StmtResult Var = Build();
+  if (Var.isInvalid())
+    return StmtError();
+
+  ApplyForRangeOrExpansionStatementLifetimeExtension(
+      cast<VarDecl>(cast<DeclStmt>(Var.get())->getSingleDecl()),
+      currentEvaluationContext().ForRangeLifetimeExtendTemps);
+  return Var;
+}
+
 StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
   if (!Exp || !Body)
     return StmtError();
@@ -544,17 +562,6 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
     return Expansion;
   }
 
-  // Create a compound statement binding the expansion variable and body,
-  // as well as the 'iter' variable if this is an iterating expansion 
statement.
-  SmallVector<Stmt *, 3> StmtsToInstantiate;
-  if (Expansion->isIterating())
-    StmtsToInstantiate.push_back(Expansion->getIterVarStmt());
-  StmtsToInstantiate.push_back(Expansion->getExpansionVarStmt());
-  StmtsToInstantiate.push_back(Body);
-  Stmt *CombinedBody =
-      CompoundStmt::Create(Context, StmtsToInstantiate, FPOptionsOverride(),
-                           Body->getBeginLoc(), Body->getEndLoc());
-
   // Expand the body for each instantiation.
   SmallVector<Stmt *, 4> Instantiations;
   CXXExpansionStmtDecl *ESD = Expansion->getDecl();
@@ -574,10 +581,38 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
     InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg,
                                Body->getSourceRange());
 
-    StmtResult Instantiation = SubstStmt(CombinedBody, MTArgList);
+    // Create a compound statement binding the expansion variable and body,
+    // as well as the 'iter' variable if this is an iterating expansion
+    // statement.
+    CompoundScopeRAII CompoundScope(*this);
+    SmallVector<Stmt *, 3> Stmts;
+    if (Expansion->isIterating()) {
+      StmtResult Iter = SubstStmt(Expansion->getIterVarStmt(), MTArgList);
+      if (Iter.isInvalid())
+        return StmtError();
+      Stmts.push_back(Iter.get());
+    }
+
+    auto SubstExpansionVar = [&] {
+      return SubstStmt(Expansion->getExpansionVarStmt(), MTArgList);
+    };
+    StmtResult ExpansionVar =
+        Expansion->isEnumerating()
+            ? BuildEnumeratingExpansionVar(SubstExpansionVar)
+            : SubstExpansionVar();
+    if (ExpansionVar.isInvalid())
+      return StmtError();
+    Stmts.push_back(ExpansionVar.get());
+
+    StmtResult Instantiation = SubstStmt(Body, MTArgList);
     if (Instantiation.isInvalid())
       return StmtError();
-    Instantiations.push_back(Instantiation.get());
+    Stmts.push_back(Instantiation.get());
+
+    Instantiations.push_back(ActOnCompoundStmt(Body->getBeginLoc(),
+                                               Body->getEndLoc(), Stmts,
+                                               /*isStmtExpr=*/false)
+                                 .get());
   }
 
   auto *InstantiationsStmt = CXXExpansionStmtInstantiation::Create(
@@ -589,12 +624,8 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
 }
 
 ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) {
-  if (Idx->isValueDependent() || InitListContainsPack(Range)) {
-    // The elements are only evaluated by the expansion that selects them, so
-    // their cleanups must not wrap this expression.
-    DiscardCleanupsInEvaluationContext();
+  if (Idx->isValueDependent() || InitListContainsPack(Range))
     return new (Context) CXXExpansionSelectExpr(Context, Range, Idx);
-  }
 
   // The index is a DRE to a template parameter; we should never
   // fail to evaluate it.
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4799f72dd6177..2ad6013441359 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9602,8 +9602,36 @@ StmtResult 
TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
     }
   }
 
-  if (TransformStmts(Instantiations, S->getInstantiations()))
-    return StmtError();
+  // The expansion variable's initializer is rebuilt, so redo its lifetime
+  // extension.
+  bool IsEnumerating = S->getParent()->getExpansionPattern()->isEnumerating();
+  for (Stmt *OldInst : S->getInstantiations()) {
+    StmtResult NewInst;
+    if (IsEnumerating) {
+      auto *CS = cast<CompoundStmt>(OldInst);
+      Sema::CompoundScopeRAII CompoundScope(SemaRef);
+      SmallVector<Stmt *, 2> Stmts;
+      for (Stmt *Sub : CS->body()) {
+        StmtResult R = Sub == CS->body_front()
+                           ? SemaRef.BuildEnumeratingExpansionVar([&] {
+                               return getDerived().TransformStmt(Sub);
+                             })
+                           : getDerived().TransformStmt(Sub);
+        if (R.isInvalid())
+          return StmtError();
+        Stmts.push_back(R.get());
+      }
+      NewInst = getDerived().RebuildCompoundStmt(
+          CS->getLBracLoc(), Stmts, CS->getRBracLoc(), /*IsStmtExpr=*/false);
+    } else {
+      NewInst = getDerived().TransformStmt(OldInst);
+    }
+    if (NewInst.isInvalid())
+      return StmtError();
+
+    SubStmtChanged |= NewInst.get() != OldInst;
+    Instantiations.push_back(NewInst.get());
+  }
 
   if (!getDerived().AlwaysRebuild() && !SubStmtChanged)
     return S;
@@ -9616,16 +9644,56 @@ StmtResult 
TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
 template <typename Derived>
 ExprResult TreeTransform<Derived>::TransformCXXExpansionSelectExpr(
     CXXExpansionSelectExpr *E) {
-  ExprResult Range = getDerived().TransformExpr(E->getRangeExpr());
   ExprResult Idx = getDerived().TransformExpr(E->getIndexExpr());
-  if (Range.isInvalid() || Idx.isInvalid())
+  if (Idx.isInvalid())
     return ExprError();
 
-  if (!getDerived().AlwaysRebuild() && Range.get() == E->getRangeExpr() &&
-      Idx.get() == E->getIndexExpr())
-    return E;
+  InitListExpr *Range = E->getRangeExpr();
+
+  // A known index means we're expanding; only the selected element is needed.
+  if (!Idx.get()->isValueDependent()) {
+    assert(llvm::none_of(Range->inits(), llvm::IsaPred<PackExpansionExpr>) &&
+           "expanding an expansion-init-list that still contains packs");
+    uint64_t I =
+        Idx.get()->EvaluateKnownConstInt(SemaRef.Context).getZExtValue();
+    return getDerived().TransformInitializer(Range->getInit(I),
+                                             /*NotCopyInit=*/false);
+  }
+
+  // Otherwise, rebuild the list. Each element is a full-expression of its own
+  // (the expansions of one pack share an evaluation context).
+  SmallVector<Expr *, 4> Inits;
+  for (Expr *Init : Range->inits()) {
+    EnterExpressionEvaluationContext Ctx(
+        SemaRef, SemaRef.currentEvaluationContext().Context);
+    SmallVector<Expr *, 2> Outputs;
+    if (getDerived().TransformExprs(&Init, 1, /*IsCall=*/false, Outputs))
+      return ExprError();
+
+    for (Expr *Out : Outputs) {
+      // Keep a pack expansion outermost by finishing its pattern instead.
+      if (auto *PE = dyn_cast<PackExpansionExpr>(Out)) {
+        Expr *Pattern = SemaRef.MaybeCreateExprWithCleanups(PE->getPattern());
+        if (Pattern != PE->getPattern()) {
+          ExprResult Res = getDerived().RebuildPackExpansion(
+              Pattern, PE->getEllipsisLoc(), PE->getNumExpansions());
+          if (Res.isInvalid())
+            return ExprError();
+          Out = Res.get();
+        }
+      } else {
+        Out = SemaRef.MaybeCreateExprWithCleanups(Out);
+      }
+      Inits.push_back(Out);
+    }
+  }
+
+  ExprResult NewRange = SemaRef.ActOnCXXExpansionInitList(
+      Inits, Range->getLBraceLoc(), Range->getRBraceLoc());
+  if (NewRange.isInvalid())
+    return ExprError();
 
-  return SemaRef.BuildCXXExpansionSelectExpr(Range.getAs<InitListExpr>(),
+  return 
SemaRef.BuildCXXExpansionSelectExpr(cast<InitListExpr>(NewRange.get()),
                                              Idx.get());
 }
 
diff --git 
a/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-stmt-lifetime.cpp 
b/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-stmt-lifetime.cpp
new file mode 100644
index 0000000000000..6ee28093c16b8
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-stmt-lifetime.cpp
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - 
%s | FileCheck %s
+
+// CWG 3043: The temporary bound to f's parameter lives as long as the
+// expansion variable, so it must be destroyed after the body.
+
+struct T {
+  int& x;
+  T(int& x) noexcept : x(x) {}
+  ~T() noexcept { x = 42; }
+};
+
+const T& f(const T& t) noexcept { return t; }
+T g(int& x) noexcept { return T(x); }
+void body(int);
+
+int lifetime_extension() {
+  int x = 5;
+  template for (auto&& e : {f(g(x)), f(g(x))}) {
+    body(e.x);
+  }
+  return x;
+}
+
+template <typename U>
+int lifetime_extension_instantiate_expansions() {
+  int x = 5;
+  template for (U e : {f(g(x))}) {
+    body(e.x);
+  }
+  return x;
+}
+
+template <typename... Ts>
+int lifetime_extension_pack(Ts... ts) {
+  int x = 5;
+  template for (auto&& e : {f(g(x)), f(g(ts))...}) {
+    body(e.x);
+  }
+  return x;
+}
+
+void instantiate() {
+  lifetime_extension_instantiate_expansions<const T&>();
+  lifetime_extension_pack(1);
+}
+
+// CHECK-LABEL: define {{.*}} i32 @_Z18lifetime_extensionv()
+// CHECK:       call void @_Z1gRi(ptr {{.*}}sret{{.*}} %[[TMP0:[^ ,]+]], ptr 
{{.*}} %x)
+// CHECK-NEXT:  call {{.*}} ptr @_Z1fRK1T(ptr {{.*}} %[[TMP0]])
+// CHECK-NOT:   call void @_ZN1TD1Ev
+// CHECK:       call void @_Z4bodyi(
+// CHECK:       call void @_ZN1TD1Ev(ptr {{.*}} %[[TMP0]])
+// CHECK:       call void @_Z1gRi(ptr {{.*}}sret{{.*}} %[[TMP1:[^ ,]+]], ptr 
{{.*}} %x)
+// CHECK-NEXT:  call {{.*}} ptr @_Z1fRK1T(ptr {{.*}} %[[TMP1]])
+// CHECK-NOT:   call void @_ZN1TD1Ev
+// CHECK:       call void @_Z4bodyi(
+// CHECK:       call void @_ZN1TD1Ev(ptr {{.*}} %[[TMP1]])
+// CHECK:       ret i32
+
+// CHECK-LABEL: define {{.*}} i32 
@_Z41lifetime_extension_instantiate_expansionsIRK1TEiv()
+// CHECK:       call void @_Z1gRi(ptr {{.*}}sret{{.*}} %[[TMP2:[^ ,]+]], ptr 
{{.*}} %x)
+// CHECK-NEXT:  call {{.*}} ptr @_Z1fRK1T(ptr {{.*}} %[[TMP2]])
+// CHECK-NOT:   call void @_ZN1TD1Ev
+// CHECK:       call void @_Z4bodyi(
+// CHECK:       call void @_ZN1TD1Ev(ptr {{.*}} %[[TMP2]])
+// CHECK:       ret i32
+
+// CHECK-LABEL: define {{.*}} i32 @_Z23lifetime_extension_packIJiEEiDpT_(
+// CHECK:       call void @_Z1gRi(ptr {{.*}}sret{{.*}} %[[TMP3:[^ ,]+]], ptr 
{{.*}} %x)
+// CHECK-NEXT:  call {{.*}} ptr @_Z1fRK1T(ptr {{.*}} %[[TMP3]])
+// CHECK-NOT:   call void @_ZN1TD1Ev
+// CHECK:       call void @_Z4bodyi(
+// CHECK:       call void @_ZN1TD1Ev(ptr {{.*}} %[[TMP3]])
+// CHECK:       call void @_Z1gRi(ptr {{.*}}sret{{.*}} %[[TMP4:[^ ,]+]], ptr 
{{.*}} %ts
+// CHECK-NEXT:  call {{.*}} ptr @_Z1fRK1T(ptr {{.*}} %[[TMP4]])
+// CHECK-NOT:   call void @_ZN1TD1Ev
+// CHECK:       call void @_Z4bodyi(
+// CHECK:       call void @_ZN1TD1Ev(ptr {{.*}} %[[TMP4]])
+// CHECK:       ret i32
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp 
b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
index dd450a8f1b76c..fd87a027c1a63 100644
--- a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -927,9 +927,9 @@ constexpr T g(int& x) noexcept { return T(x); }
 
 // CWG 3043:
 //
-// Lifetime extension only applies to destructuring expansion statements
-// (enumerating statements don't have a range variable, and the range variable
-// of iterating statements is constexpr).
+// Temporaries in the expansion-initializer of a destructuring expansion
+// statement persist for the lifetime of the reference initialized by it (the
+// range variable of iterating statements is constexpr).
 constexpr int lifetime_extension() {
   int x = 5;
   int sum  = 0;
@@ -976,6 +976,62 @@ static_assert(lifetime_extension() == 47);
 static_assert(lifetime_extension_instantiate_expansions<int>() == 47);
 static_assert(lifetime_extension_dependent_expansion_stmt<int>() == 47);
 static_assert(foo<int>().lifetime_extension_multiple_instantiations<int>() == 
47);
+
+// Temporaries in an element of an expansion-init-list persist for the lifetime
+// of the expansion variable initialized from it.
+constexpr int lifetime_extension_enumerating() {
+  int x = 5;
+  int sum = 0;
+  template for (auto e : {f(g(x))}) {
+    sum += x;
+  }
+  return sum + x;
+}
+
+constexpr int lifetime_extension_enumerating_ref() {
+  int x = 5;
+  int sum = 0;
+  template for (auto&& e : {f(g(x))}) {
+    sum += e.x;
+  }
+  return sum + x;
+}
+
+template <typename U>
+constexpr int lifetime_extension_enumerating_instantiate_expansions() {
+  int x = 5;
+  int sum = 0;
+  template for (U e : {f(g(x))}) {
+    sum += e.x;
+  }
+  return sum + x;
+}
+
+template <typename U>
+constexpr int lifetime_extension_enumerating_dependent_element() {
+  int x = 5;
+  int sum = 0;
+  template for (auto&& e : {f(g((U&)x))}) {
+    sum += e.x;
+  }
+  return sum + x;
+}
+
+template <typename... Ts>
+constexpr int lifetime_extension_enumerating_pack(Ts... ts) {
+  int x = 5;
+  int sum = 0;
+  template for (auto&& e : {f(g(x)), f(g(ts))...}) {
+    sum += e.x;
+  }
+  return sum + x;
+}
+
+static_assert(lifetime_extension_enumerating() == 47);
+static_assert(lifetime_extension_enumerating_ref() == 47);
+static_assert(lifetime_extension_enumerating_instantiate_expansions<const 
T&>() == 47);
+static_assert(lifetime_extension_enumerating_dependent_element<int>() == 47);
+static_assert(lifetime_extension_enumerating_pack(1, 2) == 50);
 }
 
 template <typename... Ts>

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

Reply via email to