https://github.com/Sirraide updated 
https://github.com/llvm/llvm-project/pull/212319

>From 37fcef37c06d55ee49af3d332f6eb7524ffd4f97 Mon Sep 17 00:00:00 2001
From: Sirraide <[email protected]>
Date: Mon, 27 Jul 2026 18:39:12 +0200
Subject: [PATCH 1/5] [Clang] Ignore returns in unexpanded expansion statements
 during return type deduction

While we could use the same machinery that we use for `if constexpr`
to ignore them conditionally (depending on whether the expansion size
is 0 or not, which would involve precomputing it and storing it e.g.
in the `CXXExpansionStmtPattern` before we process the body), we
revisit the return statement anyway when the expansion statement
is expanded (TreeTransform always rebuilds return statements), so
there's really no reason do perform return type deduction etc. when
we first build the unexpanded pattern.

Fixes #212086.
---
 clang/lib/Sema/SemaStmt.cpp                  | 62 +++++++------
 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp | 95 ++++++++++++++++++++
 2 files changed, 130 insertions(+), 27 deletions(-)

diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 1fde5a45977dd..44d99056dc483 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3651,6 +3651,34 @@ static bool hasDeducedReturnType(FunctionDecl *FD) {
   return FPT->getReturnType()->isUndeducedType();
 }
 
+/// Determine whether a return statement encountered in the current context
+/// is allowed to partake in return type deduction.
+static bool AllowReturnTypeDeductionInCurrentContext(Sema &S) {
+  // C++1z: discarded return statements are not considered when deducing a
+  // return type.
+  //
+  // Also ignore return statements within an unexpanded expansion statement
+  // since we'll revisit them anyway once we expand it; this way, we don't
+  // have to try and compute the expansion size ahead of time to figure out
+  // if it is 0 (and if the expansion statement body is thus discarded).
+  return !S.ExprEvalContexts.back().isDiscardedStatementContext() &&
+         !S.CurContext->isExpansionStmt();
+}
+
+/// Build a return statement that is in a discarded context.
+static StmtResult BuildDiscardedReturnStmt(Sema &S, Expr *RetValExp,
+                                           SourceLocation ReturnLoc) {
+  if (RetValExp) {
+    ExprResult ER =
+        S.ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue=*/false);
+    if (ER.isInvalid())
+      return StmtError();
+    RetValExp = ER.get();
+  }
+  return ReturnStmt::Create(S.Context, ReturnLoc, RetValExp,
+                            /* NRVOCandidate=*/nullptr);
+}
+
 StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
                                          Expr *RetValExp,
                                          NamedReturnInfo &NRInfo,
@@ -3665,18 +3693,9 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation 
ReturnLoc,
   bool HasDeducedReturnType =
       CurLambda && hasDeducedReturnType(CurLambda->CallOperator);
 
-  if (ExprEvalContexts.back().isDiscardedStatementContext() &&
-      (HasDeducedReturnType || CurCap->HasImplicitReturnType)) {
-    if (RetValExp) {
-      ExprResult ER =
-          ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false);
-      if (ER.isInvalid())
-        return StmtError();
-      RetValExp = ER.get();
-    }
-    return ReturnStmt::Create(Context, ReturnLoc, RetValExp,
-                              /* NRVOCandidate=*/nullptr);
-  }
+  if ((HasDeducedReturnType || CurCap->HasImplicitReturnType) &&
+      !AllowReturnTypeDeductionInCurrentContext(*this))
+    return BuildDiscardedReturnStmt(*this, RetValExp, ReturnLoc);
 
   if (HasDeducedReturnType) {
     FunctionDecl *FD = CurLambda->CallOperator;
@@ -4012,7 +4031,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr 
*RetValExp,
 
   StmtResult R =
       BuildReturnStmt(ReturnLoc, RetVal.get(), /*AllowRecovery=*/true);
-  if (R.isInvalid() || ExprEvalContexts.back().isDiscardedStatementContext())
+  if (R.isInvalid() || !AllowReturnTypeDeductionInCurrentContext(*this))
     return R;
 
   VarDecl *VD =
@@ -4122,20 +4141,9 @@ StmtResult Sema::BuildReturnStmt(SourceLocation 
ReturnLoc, Expr *RetValExp,
     }
   }
 
-  // C++1z: discarded return statements are not considered when deducing a
-  // return type.
-  if (ExprEvalContexts.back().isDiscardedStatementContext() &&
-      FnRetType->getContainedAutoType()) {
-    if (RetValExp) {
-      ExprResult ER =
-          ActOnFinishFullExpr(RetValExp, ReturnLoc, /*DiscardedValue*/ false);
-      if (ER.isInvalid())
-        return StmtError();
-      RetValExp = ER.get();
-    }
-    return ReturnStmt::Create(Context, ReturnLoc, RetValExp,
-                              /* NRVOCandidate=*/nullptr);
-  }
+  if (FnRetType->getContainedAutoType() &&
+      !AllowReturnTypeDeductionInCurrentContext(*this))
+    return BuildDiscardedReturnStmt(*this, RetValExp, ReturnLoc);
 
   // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing
   // deduction.
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp 
b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
index fb93cdf588f6c..7a2615bfa432c 100644
--- a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -1606,3 +1606,98 @@ T tf() {
 
 template long tf<long>();
 }
+
+// Check that we ignore return statements in the body of an expansion statement
+// with expansion size 0 for the purposes of return-type deduction.
+namespace gh212086 {
+auto ignored() {
+  template for (auto x : {}) {
+    return 3;
+  }
+  return 1.3;
+}
+
+static_assert(__is_same(decltype(ignored()), double));
+
+auto ignored_swapped() {
+  template for (auto x : {}) {
+    return 1.3;
+  }
+  return 3;
+}
+
+static_assert(__is_same(decltype(ignored_swapped()), int));
+
+auto ignored2() {
+  template for (auto x : {}) {
+    return x;
+  }
+}
+
+static_assert(__is_same(decltype(ignored2()), void));
+
+auto ignored3() {
+  template for (auto x : Empty()) {
+    return 3;
+  }
+  return 1.3;
+}
+
+static_assert(__is_same(decltype(ignored3()), double));
+
+auto ignored4() {
+  template for (auto x : Empty()) {
+    return x;
+  }
+}
+
+static_assert(__is_same(decltype(ignored4()), void));
+
+template <typename>
+auto ignored_template() {
+  template for (auto x : {}) {
+    return 3;
+  }
+  return 1.3;
+}
+
+template auto ignored_template<int>();
+static_assert(__is_same(decltype(ignored_template<int>()), double));
+
+template <typename>
+auto ignored_template2() {
+  template for (auto x : {}) {
+    return x;
+  }
+}
+
+template auto ignored_template2<int>();
+static_assert(__is_same(decltype(ignored_template2<int>()), void));
+
+template <typename T>
+auto ignored_template3() {
+  template for (T x : {}) {
+    return x;
+  }
+}
+
+template auto ignored_template3<int>();
+static_assert(__is_same(decltype(ignored_template3<int>()), void));
+
+auto not_ignored() {
+  int a[1]{};
+  template for (auto x : a) {
+    return 3;
+  }
+  return 1.3; // expected-error {{'auto' in return type deduced as 'double' 
here but deduced as 'int' in earlier return statement}}
+}
+
+auto not_ignored2() {
+  int a[1]{};
+  template for (auto x : a) {
+    return x;
+  }
+}
+
+static_assert(__is_same(decltype(not_ignored2()), int));
+}

>From 6ba910732e9cacb0f8ecd254f6fc4cd9d105811b Mon Sep 17 00:00:00 2001
From: Sirraide <[email protected]>
Date: Mon, 27 Jul 2026 20:21:02 +0200
Subject: [PATCH 2/5] precompute the expansion size instead

---
 clang/include/clang/AST/Stmt.h            | 14 ++++
 clang/include/clang/AST/StmtCXX.h         | 67 ++++++++++++----
 clang/include/clang/Sema/Sema.h           | 13 ++--
 clang/lib/AST/ASTImporter.cpp             |  8 +-
 clang/lib/AST/StmtCXX.cpp                 | 47 +++++++-----
 clang/lib/Parse/ParseStmt.cpp             | 14 ++++
 clang/lib/Sema/SemaExpand.cpp             | 94 ++++++++++-------------
 clang/lib/Sema/SemaStmt.cpp               | 26 ++-----
 clang/lib/Sema/TreeTransform.h            | 23 ++++--
 clang/lib/Serialization/ASTReaderStmt.cpp |  7 +-
 clang/lib/Serialization/ASTWriterStmt.cpp |  3 +
 11 files changed, 191 insertions(+), 125 deletions(-)

diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index f07ba9205661b..87f2884884bb2 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -328,6 +328,19 @@ class alignas(void *) Stmt {
     SourceLocation DeferLoc;
   };
 
+  class CXXExpansionStmtPatternBitfields {
+    friend class CXXExpansionStmtPattern;
+    friend class ASTStmtReader;
+
+    LLVM_PREFERRED_TYPE(StmtBitfields)
+    unsigned : NumStmtBits;
+
+    /// Whether the expansion size has already been computed for this, in which
+    /// case it is cached in the CXXExpansionStmtPattern.
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned HasExpansionSize : 1;
+  };
+
   //===--- Expression bitfields classes ---===//
 
   class ExprBitfields {
@@ -1348,6 +1361,7 @@ class alignas(void *) Stmt {
     ReturnStmtBitfields ReturnStmtBits;
     SwitchCaseBitfields SwitchCaseBits;
     DeferStmtBitfields DeferStmtBits;
+    CXXExpansionStmtPatternBitfields CXXExpansionStmtPatternBits;
 
     // Expressions
     ExprBitfields ExprBits;
diff --git a/clang/include/clang/AST/StmtCXX.h 
b/clang/include/clang/AST/StmtCXX.h
index 60cfd3ee85d4d..37a42f77f0fc4 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -672,10 +672,24 @@ class CoreturnStmt : public Stmt {
 /// \see CXXExpansionStmtDecl for more documentation on expansion statements.
 class CXXExpansionStmtPattern final
     : public Stmt,
-      llvm::TrailingObjects<CXXExpansionStmtPattern, Stmt *> {
+      llvm::TrailingObjects<CXXExpansionStmtPattern, Stmt *,
+                            OptionalUnsigned<std::uint64_t>> {
   friend class ASTStmtReader;
   friend TrailingObjects;
 
+public:
+  using ExpansionSize = OptionalUnsigned<std::uint64_t>;
+
+private:
+  size_t numTrailingObjects(OverloadToken<Stmt *>) const {
+    return getNumSubStmts();
+  }
+
+  size_t
+  numTrailingObjects(OverloadToken<ExpansionSize>) const {
+    return CXXExpansionStmtPatternBits.HasExpansionSize;
+  }
+
 public:
   enum class ExpansionStmtKind : uint8_t {
     Enumerating,
@@ -726,15 +740,19 @@ class CXXExpansionStmtPattern final
     COUNT_Iterating,
   };
 
-  CXXExpansionStmtPattern(ExpansionStmtKind PatternKind, EmptyShell Empty);
+  CXXExpansionStmtPattern(ExpansionStmtKind PatternKind, bool HasExpansionSize,
+                          EmptyShell Empty);
   CXXExpansionStmtPattern(ExpansionStmtKind PatternKind,
                           CXXExpansionStmtDecl *ESD, Stmt *Init,
                           DeclStmt *ExpansionVar, SourceLocation LParenLoc,
-                          SourceLocation ColonLoc, SourceLocation RParenLoc);
+                          SourceLocation ColonLoc, SourceLocation RParenLoc,
+                          ExpansionSize Size);
 
 public:
-  static CXXExpansionStmtPattern *
-  CreateEmpty(ASTContext &Context, EmptyShell Empty, ExpansionStmtKind Kind);
+  static CXXExpansionStmtPattern *CreateEmpty(ASTContext &Context,
+                                              EmptyShell Empty,
+                                              ExpansionStmtKind Kind,
+                                              bool HasExpansionSize);
 
   /// Create a dependent expansion statement pattern.
   static CXXExpansionStmtPattern *
@@ -748,20 +766,23 @@ class CXXExpansionStmtPattern final
   CreateDestructuring(ASTContext &Context, CXXExpansionStmtDecl *ESD,
                       Stmt *Init, DeclStmt *ExpansionVar,
                       Stmt *DecompositionDeclStmt, SourceLocation LParenLoc,
-                      SourceLocation ColonLoc, SourceLocation RParenLoc);
+                      SourceLocation ColonLoc, SourceLocation RParenLoc,
+                      ExpansionSize Size);
 
   /// Create an enumerating expansion statement pattern.
   static CXXExpansionStmtPattern *
   CreateEnumerating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
                     DeclStmt *ExpansionVar, SourceLocation LParenLoc,
-                    SourceLocation ColonLoc, SourceLocation RParenLoc);
+                    SourceLocation ColonLoc, SourceLocation RParenLoc,
+                    ExpansionSize Size);
 
   /// Create an iterating expansion statement pattern.
   static CXXExpansionStmtPattern *
   CreateIterating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
                   DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
                   DeclStmt *Iter, SourceLocation LParenLoc,
-                  SourceLocation ColonLoc, SourceLocation RParenLoc);
+                  SourceLocation ColonLoc, SourceLocation RParenLoc,
+                  ExpansionSize Size);
 
   SourceLocation getLParenLoc() const { return LParenLoc; }
   SourceLocation getColonLoc() const { return ColonLoc; }
@@ -924,14 +945,26 @@ class CXXExpansionStmtPattern final
     getSubStmt(EXPANSION_INITIALIZER) = S;
   }
 
+  // Get the expansion size, if known.
+  ExpansionSize getExpansionSize() const {
+    if (!CXXExpansionStmtPatternBits.HasExpansionSize)
+      return std::nullopt;
+    return *getTrailingObjects<ExpansionSize>();
+  }
+
+  void setExpansionSize(ExpansionSize Size) {
+    assert(CXXExpansionStmtPatternBits.HasExpansionSize);
+    *getTrailingObjects<ExpansionSize>() = Size;
+  }
+
   child_range children() {
-    return child_range(getTrailingObjects(),
-                       getTrailingObjects() + getNumSubStmts());
+    return child_range(getTrailingObjects<Stmt*>(),
+                       getTrailingObjects<Stmt*>() + getNumSubStmts());
   }
 
   const_child_range children() const {
-    return const_child_range(getTrailingObjects(),
-                             getTrailingObjects() + getNumSubStmts());
+    return const_child_range(getTrailingObjects<Stmt*>(),
+                             getTrailingObjects<Stmt*>() + getNumSubStmts());
   }
 
   static bool classof(const Stmt *T) {
@@ -940,19 +973,19 @@ class CXXExpansionStmtPattern final
 
 private:
   template <typename... Args>
-  static CXXExpansionStmtPattern *AllocateAndConstruct(ASTContext &Context,
-                                                       ExpansionStmtKind Kind,
-                                                       Args &&...Arguments);
+  static CXXExpansionStmtPattern *
+  AllocateAndConstruct(ASTContext &Context, ExpansionStmtKind Kind,
+                       bool HasExpansionSize, Args &&...Arguments);
 
   static unsigned getNumSubStmts(ExpansionStmtKind Kind);
   Stmt *getSubStmt(unsigned Idx) const {
     assert(Idx < getNumSubStmts());
-    return getTrailingObjects()[Idx];
+    return getTrailingObjects<Stmt*>()[Idx];
   }
 
   Stmt *&getSubStmt(unsigned Idx) {
     assert(Idx < getNumSubStmts());
-    return getTrailingObjects()[Idx];
+    return getTrailingObjects<Stmt*>()[Idx];
   }
 };
 
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 8a30f6319bcef..e3c44929cd284 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15873,11 +15873,9 @@ class Sema final : public SemaBase {
 
   StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
 
-  StmtResult BuildCXXEnumeratingExpansionStmtPattern(Decl *ESD, Stmt *Init,
-                                                     Stmt *ExpansionVar,
-                                                     SourceLocation LParenLoc,
-                                                     SourceLocation ColonLoc,
-                                                     SourceLocation RParenLoc);
+  CXXExpansionStmtPattern *BuildCXXEnumeratingExpansionStmtPattern(
+      Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc,
+      SourceLocation ColonLoc, SourceLocation RParenLoc);
 
   StmtResult BuildNonEnumeratingCXXExpansionStmtPattern(
       CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt,
@@ -15887,8 +15885,9 @@ class Sema final : public SemaBase {
 
   ExprResult BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx);
 
-  std::optional<uint64_t>
-  ComputeExpansionSize(CXXExpansionStmtPattern *Expansion);
+  /// Compute the expansion size of an iterating expansion statement.
+  CXXExpansionStmtPattern::ExpansionSize
+  ComputeIteratingExpansionSize(VarDecl *RangeVar, SourceLocation ColonLoc);
   ///@}
 };
 
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 3ad71a223903c..f144168ca89b3 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -7577,7 +7577,7 @@ 
ASTNodeImporter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
   case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating:
     return CXXExpansionStmtPattern::CreateEnumerating(
         Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToLParenLoc,
-        ToColonLoc, ToRParenLoc);
+        ToColonLoc, ToRParenLoc, S->getExpansionSize());
 
   case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: {
     auto ToRange = importChecked(Err, S->getRangeVarStmt());
@@ -7588,7 +7588,8 @@ 
ASTNodeImporter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
 
     return CXXExpansionStmtPattern::CreateIterating(
         Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToRange,
-        ToBegin, ToIter, ToLParenLoc, ToColonLoc, ToRParenLoc);
+        ToBegin, ToIter, ToLParenLoc, ToColonLoc, ToRParenLoc,
+        S->getExpansionSize());
   }
 
   case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: {
@@ -7599,7 +7600,8 @@ 
ASTNodeImporter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
 
     return CXXExpansionStmtPattern::CreateDestructuring(
         Importer.getToContext(), ToESD, ToInit, ToExpansionVar,
-        ToDecompositionDeclStmt, ToLParenLoc, ToColonLoc, ToRParenLoc);
+        ToDecompositionDeclStmt, ToLParenLoc, ToColonLoc, ToRParenLoc,
+        S->getExpansionSize());
   }
 
   case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent: {
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 704d0d4c6b0df..2b60db33e2544 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -128,25 +128,34 @@ 
CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args)
 }
 
 CXXExpansionStmtPattern::CXXExpansionStmtPattern(ExpansionStmtKind PatternKind,
+                                                 bool HasExpansionSize,
                                                  EmptyShell Empty)
-    : Stmt(CXXExpansionStmtPatternClass, Empty), PatternKind(PatternKind) {}
+    : Stmt(CXXExpansionStmtPatternClass, Empty), PatternKind(PatternKind) {
+  CXXExpansionStmtPatternBits.HasExpansionSize = HasExpansionSize;
+}
 
 CXXExpansionStmtPattern::CXXExpansionStmtPattern(
     ExpansionStmtKind PatternKind, CXXExpansionStmtDecl *ESD, Stmt *Init,
     DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc,
-    SourceLocation RParenLoc)
+    SourceLocation RParenLoc, ExpansionSize Size)
     : Stmt(CXXExpansionStmtPatternClass), PatternKind(PatternKind),
       LParenLoc(LParenLoc), ColonLoc(ColonLoc), RParenLoc(RParenLoc),
       ParentDecl(ESD) {
   setInit(Init);
   setExpansionVarStmt(ExpansionVar);
   setBody(nullptr);
+
+  CXXExpansionStmtPatternBits.HasExpansionSize = Size.has_value();
+  if (Size.has_value())
+    *getTrailingObjects<ExpansionSize>() = Size;
 }
 
 template <typename... Args>
 CXXExpansionStmtPattern *CXXExpansionStmtPattern::AllocateAndConstruct(
-    ASTContext &Context, ExpansionStmtKind Kind, Args &&...Arguments) {
-  std::size_t Size = totalSizeToAlloc<Stmt *>(getNumSubStmts(Kind));
+    ASTContext &Context, ExpansionStmtKind Kind, bool HasExpansionSize,
+    Args &&...Arguments) {
+  std::size_t Size = totalSizeToAlloc<Stmt *, ExpansionSize>(
+      getNumSubStmts(Kind), HasExpansionSize);
   void *Mem = Context.Allocate(Size, alignof(CXXExpansionStmtPattern));
   return new (Mem)
       CXXExpansionStmtPattern(Kind, std::forward<Args>(Arguments)...);
@@ -157,9 +166,10 @@ CXXExpansionStmtPattern 
*CXXExpansionStmtPattern::CreateDependent(
     DeclStmt *ExpansionVar, Expr *ExpansionInitializer,
     SourceLocation LParenLoc, SourceLocation ColonLoc,
     SourceLocation RParenLoc) {
-  CXXExpansionStmtPattern *Pattern =
-      AllocateAndConstruct(Context, ExpansionStmtKind::Dependent, ESD, Init,
-                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+  CXXExpansionStmtPattern *Pattern = AllocateAndConstruct(
+      Context, ExpansionStmtKind::Dependent, /*HasExpansionSize=*/false, ESD,
+      Init, ExpansionVar, LParenLoc, ColonLoc, RParenLoc,
+      /*Size=*/std::nullopt);
   Pattern->setExpansionInitializer(ExpansionInitializer);
   return Pattern;
 }
@@ -168,37 +178,38 @@ CXXExpansionStmtPattern 
*CXXExpansionStmtPattern::CreateDestructuring(
     ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
     DeclStmt *ExpansionVar, Stmt *DecompositionDeclStmt,
     SourceLocation LParenLoc, SourceLocation ColonLoc,
-    SourceLocation RParenLoc) {
+    SourceLocation RParenLoc, ExpansionSize Size) {
   CXXExpansionStmtPattern *Pattern =
-      AllocateAndConstruct(Context, ExpansionStmtKind::Destructuring, ESD, 
Init,
-                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+      AllocateAndConstruct(Context, ExpansionStmtKind::Destructuring, 
Size.has_value(), ESD, Init,
+                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc, Size);
   Pattern->setDecompositionDeclStmt(DecompositionDeclStmt);
   return Pattern;
 }
 
 CXXExpansionStmtPattern *
 CXXExpansionStmtPattern::CreateEmpty(ASTContext &Context, EmptyShell Empty,
-                                     ExpansionStmtKind Kind) {
-  return AllocateAndConstruct(Context, Kind, Empty);
+                                     ExpansionStmtKind Kind, bool 
HasExpansionSize) {
+  return AllocateAndConstruct(Context, Kind, HasExpansionSize, 
HasExpansionSize,
+                              Empty);
 }
 
 CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateEnumerating(
     ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
     DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc,
-    SourceLocation RParenLoc) {
-  return AllocateAndConstruct(Context, ExpansionStmtKind::Enumerating, ESD,
+    SourceLocation RParenLoc, ExpansionSize Size) {
+  return AllocateAndConstruct(Context, ExpansionStmtKind::Enumerating, 
Size.has_value(), ESD,
                               Init, ExpansionVar, LParenLoc, ColonLoc,
-                              RParenLoc);
+                              RParenLoc, Size);
 }
 
 CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateIterating(
     ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
     DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin, DeclStmt *Iter,
     SourceLocation LParenLoc, SourceLocation ColonLoc,
-    SourceLocation RParenLoc) {
+    SourceLocation RParenLoc, ExpansionSize Size) {
   CXXExpansionStmtPattern *Pattern =
-      AllocateAndConstruct(Context, ExpansionStmtKind::Iterating, ESD, Init,
-                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+      AllocateAndConstruct(Context, ExpansionStmtKind::Iterating, 
Size.has_value(), ESD, Init,
+                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc, Size);
   Pattern->setRangeVarStmt(Range);
   Pattern->setBeginVarStmt(Begin);
   Pattern->setIterVarStmt(Iter);
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 1d4bdf6d8f8e1..d5c0af5cbf983 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2371,6 +2371,20 @@ StmtResult Parser::ParseForStatement(SourceLocation 
*TrailingElseLoc,
 
   MisleadingIndentationChecker MIChecker(*this, MSK_for, ForLoc);
 
+  // If this is an expansion statement with a known expansion size of 0, parse
+  // the body as a discarded statement.
+  bool ShouldEnterDiscardedContext = false;
+  if (ESD && ForRangeStmt.isUsable()) {
+    auto *Pattern = ForRangeStmt.getAs<CXXExpansionStmtPattern>();
+    ShouldEnterDiscardedContext = Pattern->getExpansionSize() == 0u;
+  }
+
+  EnterExpressionEvaluationContext PotentiallyDiscarded(
+      Actions, Sema::ExpressionEvaluationContext::DiscardedStatement,
+      /*LambdaContextDecl=*/nullptr,
+      Sema::ExpressionEvaluationContextRecord::EK_Other,
+      ShouldEnterDiscardedContext);
+
   // Read the body statement.
   StmtResult Body(ParseStatement(TrailingElseLoc));
 
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 779b7add08344..ec2b7671e12d3 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -23,6 +23,7 @@
 #include "llvm/ADT/ScopeExit.h"
 
 using namespace clang;
+using ExpansionSize = CXXExpansionStmtPattern::ExpansionSize;
 
 namespace {
 struct IterableExpansionStmtData {
@@ -76,32 +77,6 @@ static auto InitListContainsPack(const InitListExpr *ILE) {
                       [](const Expr *E) { return isa<PackExpansionExpr>(E); });
 }
 
-static bool HasDependentSize(const DeclContext *CurContext,
-                             const CXXExpansionStmtPattern *Pattern) {
-  switch (Pattern->getKind()) {
-  case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: {
-    auto *SelectExpr = cast<CXXExpansionSelectExpr>(
-        Pattern->getExpansionVariable()->getInit());
-    return InitListContainsPack(SelectExpr->getRangeExpr());
-  }
-
-  case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
-    // Even if the size isn't technically dependent, delay expansion until
-    // we're no longer in a template since evaluating a lambda declared in
-    // a template doesn't work too well.
-    assert(CurContext->isExpansionStmt());
-    return CurContext->getParent()->isDependentContext();
-
-  case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
-    return true;
-
-  case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
-    return false;
-  }
-
-  llvm_unreachable("invalid pattern kind");
-}
-
 static IterableExpansionStmtData TryBuildIterableExpansionStmtInitializer(
     Sema &S, Expr *ExpansionInitializer, Expr *Index, SourceLocation ColonLoc,
     bool VarIsConstexpr,
@@ -359,12 +334,21 @@ StmtResult Sema::ActOnCXXExpansionStmtPattern(
       LifetimeExtendTemps);
 }
 
-StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
+CXXExpansionStmtPattern *Sema::BuildCXXEnumeratingExpansionStmtPattern(
     Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc,
     SourceLocation ColonLoc, SourceLocation RParenLoc) {
+  // The expansion size is known if the expansion-initializer contains no
+  // pack expansions.
+  auto *VD = cast<VarDecl>(cast<DeclStmt>(ExpansionVar)->getSingleDecl());
+  auto *SelectExpr = cast<CXXExpansionSelectExpr>(VD->getInit());
+  bool SizeIsKnown = !InitListContainsPack(SelectExpr->getRangeExpr());
+  ExpansionSize Size =
+      SizeIsKnown ? ExpansionSize(SelectExpr->getRangeExpr()->getNumInits())
+                  : std::nullopt;
+
   return CXXExpansionStmtPattern::CreateEnumerating(
       Context, cast<CXXExpansionStmtDecl>(ESD), Init,
-      cast<DeclStmt>(ExpansionVar), LParenLoc, ColonLoc, RParenLoc);
+      cast<DeclStmt>(ExpansionVar), LParenLoc, ColonLoc, RParenLoc, Size);
 }
 
 StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
@@ -426,9 +410,11 @@ StmtResult 
Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
     if (FinalizeExpansionVar(*this, ExpansionVar, Deref.get()))
       return StmtError();
 
+    auto *RangeVar = cast<VarDecl>(Data.RangeDecl->getSingleDecl());
+    ExpansionSize Size = ComputeIteratingExpansionSize(RangeVar, ColonLoc);
     return CXXExpansionStmtPattern::CreateIterating(
         Context, ESD, Init, ExpansionVarStmt, Data.RangeDecl, Data.BeginDecl,
-        Data.IterDecl, LParenLoc, ColonLoc, RParenLoc);
+        Data.IterDecl, LParenLoc, ColonLoc, RParenLoc, Size);
   }
 
   // If not, try destructuring.
@@ -489,8 +475,12 @@ StmtResult 
Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
   if (FinalizeExpansionVar(*this, ExpansionVar, Select))
     return StmtError();
 
+  // We only build a destructuring expansion statement once the initializer
+  // is no longer dependent; thus, the expansion size is always known.
+  ExpansionSize Size = DD->bindings().size();
   return CXXExpansionStmtPattern::CreateDestructuring(
-      Context, ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, 
RParenLoc);
+      Context, ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc,
+      Size);
 }
 
 StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
@@ -502,7 +492,8 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
          "should not rebuild expansion statement after instantiation");
 
   Expansion->setBody(Body);
-  if (HasDependentSize(CurContext, Expansion))
+  ExpansionSize NumInstantiations = Expansion->getExpansionSize();
+  if (!NumInstantiations.has_value())
     return Expansion;
 
   // Now that we're expanding this, exit the context of the expansion stmt
@@ -510,11 +501,6 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt 
*Body) {
   ContextRAII CtxGuard(*this, CurContext->getParent(),
                        /*NewThis=*/false);
 
-  // This can fail if this is an iterating expansion statement.
-  std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion);
-  if (!NumInstantiations)
-    return StmtError();
-
   // Collect preamble statements.
   //
   // There are at most 3 of these: for iterating expansion statements, these
@@ -598,13 +584,19 @@ ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr 
*Range, Expr *Idx) {
   return Range->getInit(I);
 }
 
-std::optional<uint64_t>
-Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
-  if (Expansion->isEnumerating())
-    return cast<CXXExpansionSelectExpr>(
-               Expansion->getExpansionVariable()->getInit())
-        ->getRangeExpr()
-        ->getNumInits();
+CXXExpansionStmtPattern::ExpansionSize
+Sema::ComputeIteratingExpansionSize(VarDecl *RangeVar, SourceLocation Loc) {
+  // Even if the size isn't technically dependent, delay expansion until
+  // we're no longer in a template since evaluating a lambda declared in
+  // a template doesn't work too well.
+  assert(CurContext->isExpansionStmt());
+  if (CurContext->getParent()->isDependentContext())
+    return std::nullopt;
+
+  // Computing the expansion size should happen outside the dependent
+  // expansion statement context.
+  ContextRAII CtxGuard(*this, CurContext->getParent(),
+                       /*NewThis=*/false);
 
   // [stmt.expand]p5.2 (CWG3131): N is the result of evaluating the expression
   //
@@ -615,14 +607,12 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern 
*Expansion) {
   //    for (; b != e; ++b) ++result;
   //    return result;
   // }()
-  if (Expansion->isIterating()) {
-    SourceLocation Loc = Expansion->getColonLoc();
-    EnterExpressionEvaluationContext ExprEvalCtx(
-        *this, ExpressionEvaluationContext::ConstantEvaluated);
+  EnterExpressionEvaluationContext ExprEvalCtx(
+      *this, ExpressionEvaluationContext::ConstantEvaluated);
 
-    // TODO: Build the lambda and evaluate it.
-    Diag(Loc, diag::err_iterating_expansion_stmt_unsupported);
-    return std::nullopt;
+  // TODO: Build the lambda and evaluate it.
+  Diag(Loc, diag::err_iterating_expansion_stmt_unsupported);
+  return std::nullopt;
 
 #if 0 // This will be used once we support iterating expansion statements.
     Expr::EvalResult ER;
@@ -640,8 +630,4 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern 
*Expansion) {
     assert(ER.Val.getInt().isNonNegative());
     return ER.Val.getInt().getZExtValue();
 #endif
-  }
-
-  assert(Expansion->isDestructuring());
-  return Expansion->getDecompositionDecl()->bindings().size();
 }
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 44d99056dc483..daac3af97e347 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3651,20 +3651,6 @@ static bool hasDeducedReturnType(FunctionDecl *FD) {
   return FPT->getReturnType()->isUndeducedType();
 }
 
-/// Determine whether a return statement encountered in the current context
-/// is allowed to partake in return type deduction.
-static bool AllowReturnTypeDeductionInCurrentContext(Sema &S) {
-  // C++1z: discarded return statements are not considered when deducing a
-  // return type.
-  //
-  // Also ignore return statements within an unexpanded expansion statement
-  // since we'll revisit them anyway once we expand it; this way, we don't
-  // have to try and compute the expansion size ahead of time to figure out
-  // if it is 0 (and if the expansion statement body is thus discarded).
-  return !S.ExprEvalContexts.back().isDiscardedStatementContext() &&
-         !S.CurContext->isExpansionStmt();
-}
-
 /// Build a return statement that is in a discarded context.
 static StmtResult BuildDiscardedReturnStmt(Sema &S, Expr *RetValExp,
                                            SourceLocation ReturnLoc) {
@@ -3693,8 +3679,8 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation 
ReturnLoc,
   bool HasDeducedReturnType =
       CurLambda && hasDeducedReturnType(CurLambda->CallOperator);
 
-  if ((HasDeducedReturnType || CurCap->HasImplicitReturnType) &&
-      !AllowReturnTypeDeductionInCurrentContext(*this))
+  if (ExprEvalContexts.back().isDiscardedStatementContext() &&
+      (HasDeducedReturnType || CurCap->HasImplicitReturnType))
     return BuildDiscardedReturnStmt(*this, RetValExp, ReturnLoc);
 
   if (HasDeducedReturnType) {
@@ -4031,7 +4017,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr 
*RetValExp,
 
   StmtResult R =
       BuildReturnStmt(ReturnLoc, RetVal.get(), /*AllowRecovery=*/true);
-  if (R.isInvalid() || !AllowReturnTypeDeductionInCurrentContext(*this))
+  if (R.isInvalid() || ExprEvalContexts.back().isDiscardedStatementContext())
     return R;
 
   VarDecl *VD =
@@ -4141,8 +4127,10 @@ StmtResult Sema::BuildReturnStmt(SourceLocation 
ReturnLoc, Expr *RetValExp,
     }
   }
 
-  if (FnRetType->getContainedAutoType() &&
-      !AllowReturnTypeDeductionInCurrentContext(*this))
+  // C++1z: discarded return statements are not considered when deducing a
+  // return type.
+  if (ExprEvalContexts.back().isDiscardedStatementContext() &&
+      FnRetType->getContainedAutoType())
     return BuildDiscardedReturnStmt(*this, RetValExp, ReturnLoc);
 
   // FIXME: Add a flag to the ScopeInfo to indicate whether we're performing
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0f6f168d06812..482fa00f5a789 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9487,9 +9487,9 @@ StmtResult 
TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
     if (ExpansionVar.isInvalid())
       return StmtError();
 
-    NewPattern = CXXExpansionStmtPattern::CreateEnumerating(
-        SemaRef.Context, NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
-        S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc());
+    NewPattern = SemaRef.BuildCXXEnumeratingExpansionStmtPattern(
+        NewESD, Init, ExpansionVar.getAs<DeclStmt>(), S->getLParenLoc(),
+        S->getColonLoc(), S->getRParenLoc());
   } else if (S->isIterating()) {
     StmtResult Begin = TransformStmtInParentContext(S->getBeginVarStmt());
     StmtResult Iter = TransformStmtInParentContext(S->getIterVarStmt());
@@ -9504,11 +9504,17 @@ StmtResult 
TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
     if (ExpansionVar.isInvalid())
       return StmtError();
 
+    // Compute the expansion size if possible.
+    DeclStmt *RangeVarDS = Range.getAs<DeclStmt>();
+    VarDecl *RangeVar = cast<VarDecl>(RangeVarDS->getSingleDecl());
+    CXXExpansionStmtPattern::ExpansionSize Size =
+        SemaRef.ComputeIteratingExpansionSize(RangeVar, S->getColonLoc());
+
     NewPattern = CXXExpansionStmtPattern::CreateIterating(
         SemaRef.Context, NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
-        Range.getAs<DeclStmt>(), Begin.getAs<DeclStmt>(),
+        RangeVarDS, Begin.getAs<DeclStmt>(),
         Iter.getAs<DeclStmt>(), S->getLParenLoc(), S->getColonLoc(),
-        S->getRParenLoc());
+        S->getRParenLoc(), Size);
 
     SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension(
         NewPattern->getRangeVar(), LifetimeExtendTemps);
@@ -9536,6 +9542,13 @@ StmtResult 
TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
     llvm_unreachable("destructuring pattern should never be instantiated");
   }
 
+  // If the expansion size is 0, treat the body as a discarded statement.
+  EnterExpressionEvaluationContext PotentiallyDiscarded(
+      SemaRef, Sema::ExpressionEvaluationContext::DiscardedStatement,
+      /*LambdaContextDecl=*/nullptr,
+      Sema::ExpressionEvaluationContextRecord::EK_Other,
+      NewPattern->getExpansionSize() == 0u);
+
   StmtResult Body = getDerived().TransformStmt(S->getBody());
   if (Body.isInvalid())
     return StmtError();
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index 87cec16a76323..fb904a07532b8 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1784,13 +1784,15 @@ void 
ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
 
 void ASTStmtReader::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
   VisitStmt(S);
-  Record.skipInts(1); // Skip kind.
+  Record.skipInts(2); // Skip kind and whether there is an expansion size.
   S->LParenLoc = readSourceLocation();
   S->ColonLoc = readSourceLocation();
   S->RParenLoc = readSourceLocation();
   S->ParentDecl = cast<CXXExpansionStmtDecl>(Record.readDeclRef());
   for (Stmt *&SubStmt : S->children())
     SubStmt = Record.readSubStmt();
+  if (S->CXXExpansionStmtPatternBits.HasExpansionSize)
+    S->setExpansionSize(Record.readInt());
 }
 
 void ASTStmtReader::VisitCXXExpansionStmtInstantiation(
@@ -3666,7 +3668,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       S = CXXExpansionStmtPattern::CreateEmpty(
           Context, Empty,
           static_cast<CXXExpansionStmtPattern::ExpansionStmtKind>(
-              Record[ASTStmtReader::NumStmtFields]));
+              Record[ASTStmtReader::NumStmtFields]),
+          Record[ASTStmtReader::NumStmtFields + 1]);
       break;
 
     case STMT_CXX_EXPANSION_INSTANTIATION:
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index 70477f4cf4001..336beb667115b 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1760,12 +1760,15 @@ void 
ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
 void ASTStmtWriter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
   VisitStmt(S);
   Record.push_back(static_cast<unsigned>(S->getKind()));
+  Record.push_back(S->getExpansionSize().has_value());
   Record.AddSourceLocation(S->getLParenLoc());
   Record.AddSourceLocation(S->getColonLoc());
   Record.AddSourceLocation(S->getRParenLoc());
   Record.AddDeclRef(S->getDecl());
   for (Stmt *SubStmt : S->children())
     Record.AddStmt(SubStmt);
+  if (CXXExpansionStmtPattern::ExpansionSize Size = S->getExpansionSize())
+    Record.push_back(*Size);
   Code = serialization::STMT_CXX_EXPANSION_PATTERN;
 }
 

>From cfb861c86d47941eadb055bc151be5550d1c8aec Mon Sep 17 00:00:00 2001
From: Sirraide <[email protected]>
Date: Mon, 27 Jul 2026 20:25:08 +0200
Subject: [PATCH 3/5] clang-format

---
 clang/include/clang/AST/StmtCXX.h | 15 +++++++--------
 clang/lib/AST/StmtCXX.cpp         | 29 +++++++++++++++--------------
 clang/lib/Sema/TreeTransform.h    |  5 ++---
 3 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/clang/include/clang/AST/StmtCXX.h 
b/clang/include/clang/AST/StmtCXX.h
index 37a42f77f0fc4..a36dd8a3e078a 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -685,8 +685,7 @@ class CXXExpansionStmtPattern final
     return getNumSubStmts();
   }
 
-  size_t
-  numTrailingObjects(OverloadToken<ExpansionSize>) const {
+  size_t numTrailingObjects(OverloadToken<ExpansionSize>) const {
     return CXXExpansionStmtPatternBits.HasExpansionSize;
   }
 
@@ -958,13 +957,13 @@ class CXXExpansionStmtPattern final
   }
 
   child_range children() {
-    return child_range(getTrailingObjects<Stmt*>(),
-                       getTrailingObjects<Stmt*>() + getNumSubStmts());
+    return child_range(getTrailingObjects<Stmt *>(),
+                       getTrailingObjects<Stmt *>() + getNumSubStmts());
   }
 
   const_child_range children() const {
-    return const_child_range(getTrailingObjects<Stmt*>(),
-                             getTrailingObjects<Stmt*>() + getNumSubStmts());
+    return const_child_range(getTrailingObjects<Stmt *>(),
+                             getTrailingObjects<Stmt *>() + getNumSubStmts());
   }
 
   static bool classof(const Stmt *T) {
@@ -980,12 +979,12 @@ class CXXExpansionStmtPattern final
   static unsigned getNumSubStmts(ExpansionStmtKind Kind);
   Stmt *getSubStmt(unsigned Idx) const {
     assert(Idx < getNumSubStmts());
-    return getTrailingObjects<Stmt*>()[Idx];
+    return getTrailingObjects<Stmt *>()[Idx];
   }
 
   Stmt *&getSubStmt(unsigned Idx) {
     assert(Idx < getNumSubStmts());
-    return getTrailingObjects<Stmt*>()[Idx];
+    return getTrailingObjects<Stmt *>()[Idx];
   }
 };
 
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 2b60db33e2544..2c873c5a74b86 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -177,18 +177,19 @@ CXXExpansionStmtPattern 
*CXXExpansionStmtPattern::CreateDependent(
 CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateDestructuring(
     ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
     DeclStmt *ExpansionVar, Stmt *DecompositionDeclStmt,
-    SourceLocation LParenLoc, SourceLocation ColonLoc,
-    SourceLocation RParenLoc, ExpansionSize Size) {
-  CXXExpansionStmtPattern *Pattern =
-      AllocateAndConstruct(Context, ExpansionStmtKind::Destructuring, 
Size.has_value(), ESD, Init,
-                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc, Size);
+    SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation 
RParenLoc,
+    ExpansionSize Size) {
+  CXXExpansionStmtPattern *Pattern = AllocateAndConstruct(
+      Context, ExpansionStmtKind::Destructuring, Size.has_value(), ESD, Init,
+      ExpansionVar, LParenLoc, ColonLoc, RParenLoc, Size);
   Pattern->setDecompositionDeclStmt(DecompositionDeclStmt);
   return Pattern;
 }
 
 CXXExpansionStmtPattern *
 CXXExpansionStmtPattern::CreateEmpty(ASTContext &Context, EmptyShell Empty,
-                                     ExpansionStmtKind Kind, bool 
HasExpansionSize) {
+                                     ExpansionStmtKind Kind,
+                                     bool HasExpansionSize) {
   return AllocateAndConstruct(Context, Kind, HasExpansionSize, 
HasExpansionSize,
                               Empty);
 }
@@ -197,19 +198,19 @@ CXXExpansionStmtPattern 
*CXXExpansionStmtPattern::CreateEnumerating(
     ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
     DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc,
     SourceLocation RParenLoc, ExpansionSize Size) {
-  return AllocateAndConstruct(Context, ExpansionStmtKind::Enumerating, 
Size.has_value(), ESD,
-                              Init, ExpansionVar, LParenLoc, ColonLoc,
-                              RParenLoc, Size);
+  return AllocateAndConstruct(Context, ExpansionStmtKind::Enumerating,
+                              Size.has_value(), ESD, Init, ExpansionVar,
+                              LParenLoc, ColonLoc, RParenLoc, Size);
 }
 
 CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateIterating(
     ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
     DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin, DeclStmt *Iter,
-    SourceLocation LParenLoc, SourceLocation ColonLoc,
-    SourceLocation RParenLoc, ExpansionSize Size) {
-  CXXExpansionStmtPattern *Pattern =
-      AllocateAndConstruct(Context, ExpansionStmtKind::Iterating, 
Size.has_value(), ESD, Init,
-                           ExpansionVar, LParenLoc, ColonLoc, RParenLoc, Size);
+    SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation 
RParenLoc,
+    ExpansionSize Size) {
+  CXXExpansionStmtPattern *Pattern = AllocateAndConstruct(
+      Context, ExpansionStmtKind::Iterating, Size.has_value(), ESD, Init,
+      ExpansionVar, LParenLoc, ColonLoc, RParenLoc, Size);
   Pattern->setRangeVarStmt(Range);
   Pattern->setBeginVarStmt(Begin);
   Pattern->setIterVarStmt(Iter);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 482fa00f5a789..f9cd0542c044a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9512,9 +9512,8 @@ StmtResult 
TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
 
     NewPattern = CXXExpansionStmtPattern::CreateIterating(
         SemaRef.Context, NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
-        RangeVarDS, Begin.getAs<DeclStmt>(),
-        Iter.getAs<DeclStmt>(), S->getLParenLoc(), S->getColonLoc(),
-        S->getRParenLoc(), Size);
+        RangeVarDS, Begin.getAs<DeclStmt>(), Iter.getAs<DeclStmt>(),
+        S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc(), Size);
 
     SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension(
         NewPattern->getRangeVar(), LifetimeExtendTemps);

>From 6c1e443d4c95f2192a09b5040da62d08618df30d Mon Sep 17 00:00:00 2001
From: Sirraide <[email protected]>
Date: Mon, 27 Jul 2026 20:35:40 +0200
Subject: [PATCH 4/5] add test for 212088

---
 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp 
b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
index 7a2615bfa432c..7d5f385377706 100644
--- a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -1701,3 +1701,11 @@ auto not_ignored2() {
 
 static_assert(__is_same(decltype(not_ignored2()), int));
 }
+
+namespace gh212088 {
+void f() {
+  struct S {};
+  template for (const int &x : []{return S();}()) {
+  }
+}
+}

>From 0e4b71b89abfd4ce072a0986729345c73081c203 Mon Sep 17 00:00:00 2001
From: Sirraide <[email protected]>
Date: Mon, 27 Jul 2026 20:54:10 +0200
Subject: [PATCH 5/5] remove the test again as this doesn't actually fix it

---
 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp 
b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
index 7d5f385377706..7a2615bfa432c 100644
--- a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -1701,11 +1701,3 @@ auto not_ignored2() {
 
 static_assert(__is_same(decltype(not_ignored2()), int));
 }
-
-namespace gh212088 {
-void f() {
-  struct S {};
-  template for (const int &x : []{return S();}()) {
-  }
-}
-}

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

Reply via email to