Skip to content

Conversation

@Sirraide
Copy link
Member

@Sirraide Sirraide commented Nov 26, 2025

This is an NFC patch that refactors the range-based for code and moves ParseScope and friends into Sema such that they can be reused for iterating expansion statements. It also adds a ForExpansionStmt bool parameter to some functions, but this is still always false and currently unused (it will be used by the next patch in the series).

@github-actions
Copy link

github-actions bot commented Nov 26, 2025

🐧 Linux x64 Test Results

  • 111407 tests passed
  • 4449 tests skipped

@Sirraide Sirraide added c++26 clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Nov 26, 2025 — with Graphite App
@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2025

@llvm/pr-subscribers-hlsl
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: None (Sirraide)

Changes

[Clang] [C++26] Expansion Statements (Part 4)

Remove most expansion-statement-specific code


Patch is 28.60 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169683.diff

2 Files Affected:

  • (modified) clang/include/clang/Sema/Sema.h (+34)
  • (modified) clang/lib/Sema/SemaStmt.cpp (+268-224)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4d25143cffaf4..1101ee9e10778 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11066,6 +11066,37 @@ class Sema final : public SemaBase {
       BuildForRangeKind Kind,
       ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
 
+  /// Set the type of a for-range declaration whose for-range or expansion
+  /// initialiser is dependent.
+  void ActOnDependentForRangeInitializer(VarDecl *LoopVar,
+                                         BuildForRangeKind BFRK);
+
+  /// Holds the 'begin' and 'end' variables of a range-based for loop or
+  /// expansion statement; begin-expr and end-expr are also provided; the
+  /// latter are used in some diagnostics.
+  struct ForRangeBeginEndInfo {
+    VarDecl *BeginVar = nullptr;
+    VarDecl *EndVar = nullptr;
+    Expr *BeginExpr = nullptr;
+    Expr *EndExpr = nullptr;
+    bool isValid() const { return BeginVar != nullptr && EndVar != nullptr; }
+  };
+
+  /// Determine begin-expr and end-expr and build variable declarations for
+  /// them as per [stmt.ranged].
+  ForRangeBeginEndInfo BuildCXXForRangeBeginEndVars(
+      Scope *S, VarDecl *RangeVar, SourceLocation ColonLoc,
+      SourceLocation CoawaitLoc,
+      ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps,
+      BuildForRangeKind Kind, bool ForExpansionStmt,
+      StmtResult *RebuildResult = nullptr,
+      llvm::function_ref<StmtResult()> RebuildWithDereference = {});
+
+  /// Build the range variable of a range-based for loop or iterating
+  /// expansion statement and return its DeclStmt.
+  StmtResult BuildCXXForRangeRangeVar(Scope *S, Expr *Range,
+                                      bool ForExpansionStmt);
+
   /// FinishCXXForRangeStmt - Attach the body to a C++0x for-range statement.
   /// This is a separate step from ActOnCXXForRangeStmt because analysis of the
   /// body cannot be performed until after the type of the range variable is
@@ -11207,6 +11238,9 @@ class Sema final : public SemaBase {
                                            SourceLocation Loc,
                                            unsigned NumParams);
 
+  void ApplyForRangeOrExpansionStatementLifetimeExtension(
+      VarDecl *RangeVar, ArrayRef<MaterializeTemporaryExpr *> Temporaries);
+
 private:
   /// Check whether the given statement can have musttail applied to it,
   /// issuing a diagnostic and returning false if not.
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 655fa31bbf5c7..f948f516b5136 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2409,8 +2409,8 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E,
 }
 
 /// Build a variable declaration for a for-range statement.
-VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc,
-                              QualType Type, StringRef Name) {
+VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc, QualType Type,
+                              StringRef Name, bool ForExpansionStmt) {
   DeclContext *DC = SemaRef.CurContext;
   IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name);
   TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc);
@@ -2420,7 +2420,6 @@ VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc,
   Decl->setCXXForRangeImplicitVar(true);
   return Decl;
 }
-
 }
 
 static bool ObjCEnumerationCollection(Expr *Collection) {
@@ -2428,6 +2427,25 @@ static bool ObjCEnumerationCollection(Expr *Collection) {
           && Collection->getType()->getAs<ObjCObjectPointerType>() != nullptr;
 }
 
+StmtResult Sema::BuildCXXForRangeRangeVar(Scope *S, Expr *Range,
+                                          bool ForExpansionStmt) {
+  // Divide by 2, since the variables are in the inner scope (loop body).
+  const auto DepthStr = std::to_string(S->getDepth() / 2);
+  SourceLocation RangeLoc = Range->getBeginLoc();
+  VarDecl *RangeVar =
+      BuildForRangeVarDecl(*this, RangeLoc, Context.getAutoRRefDeductType(),
+                           std::string("__range") + DepthStr, ForExpansionStmt);
+  if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
+                            diag::err_for_range_deduction_failure))
+
+    return StmtError();
+
+  // Claim the type doesn't contain auto: we've already done the checking.
+  DeclGroupPtrTy RangeGroup =
+      BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
+  return ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc);
+}
+
 StmtResult Sema::ActOnCXXForRangeStmt(
     Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
     Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc,
@@ -2472,22 +2490,8 @@ StmtResult Sema::ActOnCXXForRangeStmt(
   }
 
   // Build  auto && __range = range-init
-  // Divide by 2, since the variables are in the inner scope (loop body).
-  const auto DepthStr = std::to_string(S->getDepth() / 2);
-  SourceLocation RangeLoc = Range->getBeginLoc();
-  VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc,
-                                           Context.getAutoRRefDeductType(),
-                                           std::string("__range") + DepthStr);
-  if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
-                            diag::err_for_range_deduction_failure)) {
-    ActOnInitializerError(LoopVar);
-    return StmtError();
-  }
-
-  // Claim the type doesn't contain auto: we've already done the checking.
-  DeclGroupPtrTy RangeGroup =
-      BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
-  StmtResult RangeDecl = ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc);
+  auto RangeDecl =
+      BuildCXXForRangeRangeVar(S, Range, /*ForExpansionStmt=*/false);
   if (RangeDecl.isInvalid()) {
     ActOnInitializerError(LoopVar);
     return StmtError();
@@ -2686,6 +2690,226 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
       AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild);
 }
 
+void Sema::ApplyForRangeOrExpansionStatementLifetimeExtension(
+    VarDecl *RangeVar, ArrayRef<MaterializeTemporaryExpr *> Temporaries) {
+  if (Temporaries.empty())
+    return;
+
+  InitializedEntity Entity = InitializedEntity::InitializeVariable(RangeVar);
+  for (auto *MTE : Temporaries)
+    MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
+}
+
+Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars(
+    Scope *S, VarDecl *RangeVar, SourceLocation ColonLoc,
+    SourceLocation CoawaitLoc,
+    ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps,
+    BuildForRangeKind Kind, bool ForExpansionStmt, StmtResult *RebuildResult,
+    llvm::function_ref<StmtResult()> RebuildWithDereference) {
+  QualType RangeVarType = RangeVar->getType();
+  SourceLocation RangeLoc = RangeVar->getLocation();
+  const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType();
+
+  ExprResult BeginRangeRef =
+      BuildDeclRefExpr(RangeVar, RangeVarNonRefType, VK_LValue, ColonLoc);
+  if (BeginRangeRef.isInvalid())
+    return {};
+
+  ExprResult EndRangeRef =
+      BuildDeclRefExpr(RangeVar, RangeVarNonRefType, VK_LValue, ColonLoc);
+  if (EndRangeRef.isInvalid())
+    return {};
+
+  QualType AutoType = Context.getAutoDeductType();
+  Expr *Range = RangeVar->getInit();
+  if (!Range)
+    return {};
+  QualType RangeType = Range->getType();
+
+  if (RequireCompleteType(RangeLoc, RangeType,
+                          diag::err_for_range_incomplete_type))
+    return {};
+
+  // P2718R0 - Lifetime extension in range-based for loops.
+  if (getLangOpts().CPlusPlus23)
+    ApplyForRangeOrExpansionStatementLifetimeExtension(RangeVar,
+                                                       LifetimeExtendTemps);
+
+  // Build auto __begin = begin-expr, __end = end-expr.
+  // Divide by 2, since the variables are in the inner scope (loop body).
+  const auto DepthStr = std::to_string(S->getDepth() / 2);
+  VarDecl *BeginVar =
+      BuildForRangeVarDecl(*this, ColonLoc, AutoType,
+                           std::string("__begin") + DepthStr, ForExpansionStmt);
+  VarDecl *EndVar =
+      BuildForRangeVarDecl(*this, ColonLoc, AutoType,
+                           std::string("__end") + DepthStr, ForExpansionStmt);
+
+  // Build begin-expr and end-expr and attach to __begin and __end variables.
+  ExprResult BeginExpr, EndExpr;
+  if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) {
+    // - if _RangeT is an array type, begin-expr and end-expr are __range and
+    //   __range + __bound, respectively, where __bound is the array bound. If
+    //   _RangeT is an array of unknown size or an array of incomplete type,
+    //   the program is ill-formed;
+
+    // begin-expr is __range.
+    BeginExpr = BeginRangeRef;
+    if (!CoawaitLoc.isInvalid()) {
+      BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get());
+      if (BeginExpr.isInvalid())
+        return {};
+    }
+    if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc,
+                              diag::err_for_range_iter_deduction_failure)) {
+      NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+      return {};
+    }
+
+    // Find the array bound.
+    ExprResult BoundExpr;
+    if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(UnqAT))
+      BoundExpr = IntegerLiteral::Create(
+          Context, CAT->getSize(), Context.getPointerDiffType(), RangeLoc);
+    else if (const VariableArrayType *VAT =
+                 dyn_cast<VariableArrayType>(UnqAT)) {
+      // For a variably modified type we can't just use the expression within
+      // the array bounds, since we don't want that to be re-evaluated here.
+      // Rather, we need to determine what it was when the array was first
+      // created - so we resort to using sizeof(vla)/sizeof(element).
+      // For e.g.
+      //  void f(int b) {
+      //    int vla[b];
+      //    b = -1;   <-- This should not affect the num of iterations below
+      //    for (int &c : vla) { .. }
+      //  }
+
+      // FIXME: This results in codegen generating IR that recalculates the
+      // run-time number of elements (as opposed to just using the IR Value
+      // that corresponds to the run-time value of each bound that was
+      // generated when the array was created.) If this proves too embarrassing
+      // even for unoptimized IR, consider passing a magic-value/cookie to
+      // codegen that then knows to simply use that initial llvm::Value (that
+      // corresponds to the bound at time of array creation) within
+      // getelementptr.  But be prepared to pay the price of increasing a
+      // customized form of coupling between the two components - which  could
+      // be hard to maintain as the codebase evolves.
+
+      ExprResult SizeOfVLAExprR = ActOnUnaryExprOrTypeTraitExpr(
+          EndVar->getLocation(), UETT_SizeOf,
+          /*IsType=*/true,
+          CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo(
+                                               VAT->desugar(), RangeLoc))
+              .getAsOpaquePtr(),
+          EndVar->getSourceRange());
+      if (SizeOfVLAExprR.isInvalid())
+        return {};
+
+      ExprResult SizeOfEachElementExprR = ActOnUnaryExprOrTypeTraitExpr(
+          EndVar->getLocation(), UETT_SizeOf,
+          /*IsType=*/true,
+          CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo(
+                                               VAT->getElementType(), RangeLoc))
+              .getAsOpaquePtr(),
+          EndVar->getSourceRange());
+      if (SizeOfEachElementExprR.isInvalid())
+        return {};
+
+      BoundExpr =
+          ActOnBinOp(S, EndVar->getLocation(), tok::slash, SizeOfVLAExprR.get(),
+                     SizeOfEachElementExprR.get());
+      if (BoundExpr.isInvalid())
+        return {};
+
+    } else {
+      // Can't be a DependentSizedArrayType or an IncompleteArrayType since
+      // UnqAT is not incomplete and Range is not type-dependent.
+      llvm_unreachable("Unexpected array type in for-range");
+    }
+
+    // end-expr is __range + __bound.
+    EndExpr =
+        ActOnBinOp(S, ColonLoc, tok::plus, EndRangeRef.get(), BoundExpr.get());
+    if (EndExpr.isInvalid())
+      return {};
+    if (FinishForRangeVarDecl(*this, EndVar, EndExpr.get(), ColonLoc,
+                              diag::err_for_range_iter_deduction_failure)) {
+      NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
+      return {};
+    }
+  } else {
+    OverloadCandidateSet CandidateSet(RangeLoc,
+                                      OverloadCandidateSet::CSK_Normal);
+    BeginEndFunction BEFFailure;
+    ForRangeStatus RangeStatus =
+        BuildNonArrayForRange(*this, BeginRangeRef.get(), EndRangeRef.get(),
+                              RangeType, BeginVar, EndVar, ColonLoc, CoawaitLoc,
+                              &CandidateSet, &BeginExpr, &EndExpr, &BEFFailure);
+
+    if (Kind == BFRK_Build && RangeStatus == FRS_NoViableFunction &&
+        BEFFailure == BEF_begin) {
+      // If the range is being built from an array parameter, emit a
+      // a diagnostic that it is being treated as a pointer.
+      if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Range)) {
+        if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
+          QualType ArrayTy = PVD->getOriginalType();
+          QualType PointerTy = PVD->getType();
+          if (PointerTy->isPointerType() && ArrayTy->isArrayType()) {
+            Diag(Range->getBeginLoc(), diag::err_range_on_array_parameter)
+                << RangeLoc << PVD << ArrayTy << PointerTy;
+            Diag(PVD->getLocation(), diag::note_declared_at);
+            return {};
+          }
+        }
+      }
+
+      // If building the range failed, try dereferencing the range expression
+      // unless a diagnostic was issued or the end function is problematic.
+      if (RebuildWithDereference) {
+        assert(RebuildResult);
+        StmtResult SR = RebuildWithDereference();
+        if (SR.isInvalid() || SR.isUsable()) {
+          *RebuildResult = SR;
+          return {};
+        }
+      }
+    }
+
+    // Otherwise, emit diagnostics if we haven't already.
+    if (RangeStatus == FRS_NoViableFunction) {
+      Expr *Range = BEFFailure ? EndRangeRef.get() : BeginRangeRef.get();
+      CandidateSet.NoteCandidates(
+          PartialDiagnosticAt(Range->getBeginLoc(),
+                              PDiag(diag::err_for_range_invalid)
+                                  << RangeLoc << Range->getType()
+                                  << BEFFailure),
+          *this, OCD_AllCandidates, Range);
+    }
+    // Return an error if no fix was discovered.
+    if (RangeStatus != FRS_Success)
+      return {};
+  }
+
+  assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() &&
+         "invalid range expression in for loop");
+
+  return {BeginVar, EndVar, BeginExpr.get(), EndExpr.get()};
+}
+
+void Sema::ActOnDependentForRangeInitializer(VarDecl *LoopVar,
+                                             BuildForRangeKind BFRK) {
+  // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill
+  // them in properly when we instantiate the loop.
+  if (!LoopVar->isInvalidDecl() && BFRK != BFRK_Check) {
+    if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar))
+      for (auto *Binding : DD->bindings()) {
+        if (!Binding->isParameterPack())
+          Binding->setType(Context.DependentTy);
+      }
+    LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
+  }
+}
+
 StmtResult Sema::BuildCXXForRangeStmt(
     SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
     SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End,
@@ -2717,216 +2941,36 @@ StmtResult Sema::BuildCXXForRangeStmt(
   if (RangeVarType->isDependentType()) {
     // The range is implicitly used as a placeholder when it is dependent.
     RangeVar->markUsed(Context);
-
-    // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill
-    // them in properly when we instantiate the loop.
-    if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) {
-      if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar))
-        for (auto *Binding : DD->bindings()) {
-          if (!Binding->isParameterPack())
-            Binding->setType(Context.DependentTy);
-        }
-      LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
-    }
+    ActOnDependentForRangeInitializer(LoopVar, Kind);
   } else if (!BeginDeclStmt.get()) {
-    SourceLocation RangeLoc = RangeVar->getLocation();
-
-    const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType();
-
-    ExprResult BeginRangeRef = BuildDeclRefExpr(RangeVar, RangeVarNonRefType,
-                                                VK_LValue, ColonLoc);
-    if (BeginRangeRef.isInvalid())
-      return StmtError();
-
-    ExprResult EndRangeRef = BuildDeclRefExpr(RangeVar, RangeVarNonRefType,
-                                              VK_LValue, ColonLoc);
-    if (EndRangeRef.isInvalid())
-      return StmtError();
+    StmtResult RebuildResult;
+    auto RebuildWithDereference = [&] {
+      return RebuildForRangeWithDereference(
+          *this, S, ForLoc, CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc,
+          RangeVar->getInit(), RangeVar->getLocation(), RParenLoc);
+    };
 
-    QualType AutoType = Context.getAutoDeductType();
-    Expr *Range = RangeVar->getInit();
-    if (!Range)
-      return StmtError();
-    QualType RangeType = Range->getType();
+    auto BeginRangeRefTy = RangeVar->getType().getNonReferenceType();
+    auto [BeginVar, EndVar, BeginExpr, EndExpr] = BuildCXXForRangeBeginEndVars(
+        S, RangeVar, ColonLoc, CoawaitLoc, LifetimeExtendTemps, Kind,
+        /*ForExpansionStmt=*/false, &RebuildResult, RebuildWithDereference);
 
-    if (RequireCompleteType(RangeLoc, RangeType,
-                            diag::err_for_range_incomplete_type))
+    if (!RebuildResult.isUnset())
+      return RebuildResult;
+    if (!BeginVar || !EndVar)
       return StmtError();
 
-    // P2718R0 - Lifetime extension in range-based for loops.
-    if (getLangOpts().CPlusPlus23 && !LifetimeExtendTemps.empty()) {
-      InitializedEntity Entity =
-          InitializedEntity::InitializeVariable(RangeVar);
-      for (auto *MTE : LifetimeExtendTemps)
-        MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
-    }
-
-    // Build auto __begin = begin-expr, __end = end-expr.
-    // Divide by 2, since the variables are in the inner scope (loop body).
-    const auto DepthStr = std::to_string(S->getDepth() / 2);
-    VarDecl *BeginVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType,
-                                             std::string("__begin") + DepthStr);
-    VarDecl *EndVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType,
-                                           std::string("__end") + DepthStr);
-
-    // Build begin-expr and end-expr and attach to __begin and __end variables.
-    ExprResult BeginExpr, EndExpr;
-    if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) {
-      // - if _RangeT is an array type, begin-expr and end-expr are __range and
-      //   __range + __bound, respectively, where __bound is the array bound. If
-      //   _RangeT is an array of unknown size or an array of incomplete type,
-      //   the program is ill-formed;
-
-      // begin-expr is __range.
-      BeginExpr = BeginRangeRef;
-      if (!CoawaitLoc.isInvalid()) {
-        BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get());
-        if (BeginExpr.isInvalid())
-          return StmtError();
-      }
-      if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc,
-                                diag::err_for_range_iter_deduction_failure)) {
-        NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
-        return StmtError();
-      }
-
-      // Find the array bound.
-      ExprResult BoundExpr;
-      if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(UnqAT))
-        BoundExpr = IntegerLiteral::Create(
-            Context, CAT->getSize(), Context.getPointerDiffType(), RangeLoc);
-      else if (const VariableArrayType *VAT =
-               dyn_cast<VariableArrayType>(UnqAT))...
[truncated]

@Sirraide Sirraide marked this pull request as ready for review November 26, 2025 17:46
@llvmbot llvmbot added the clang Clang issues not falling into any other category label Nov 26, 2025
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-3-enumerating branch from ad76ea8 to c4c6899 Compare November 26, 2025 18:05
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-4-nfc-for-range-refactor branch from d36d97f to bf106a6 Compare November 26, 2025 18:05
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-3-enumerating branch from c4c6899 to b85d1e4 Compare December 1, 2025 17:26
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-4-nfc-for-range-refactor branch from bf106a6 to 7f234c2 Compare December 1, 2025 17:26
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-3-enumerating branch from b85d1e4 to 56ad658 Compare December 1, 2025 20:30
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-4-nfc-for-range-refactor branch from 7f234c2 to 8a3ae34 Compare December 1, 2025 20:30
@llvmbot llvmbot added HLSL HLSL Language Support clang:openmp OpenMP related changes to Clang labels Dec 3, 2025
@github-actions
Copy link

github-actions bot commented Dec 3, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff origin/main HEAD --extensions h,cpp -- clang/include/clang/Parse/Parser.h clang/include/clang/Sema/Scope.h clang/include/clang/Sema/Sema.h clang/lib/Interpreter/IncrementalParser.cpp clang/lib/Parse/ParseCXXInlineMethods.cpp clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseDeclCXX.cpp clang/lib/Parse/ParseExpr.cpp clang/lib/Parse/ParseExprCXX.cpp clang/lib/Parse/ParseHLSL.cpp clang/lib/Parse/ParseObjc.cpp clang/lib/Parse/ParseOpenACC.cpp clang/lib/Parse/ParseOpenMP.cpp clang/lib/Parse/ParsePragma.cpp clang/lib/Parse/ParseStmt.cpp clang/lib/Parse/ParseTemplate.cpp clang/lib/Parse/Parser.cpp clang/lib/Sema/Scope.cpp clang/lib/Sema/Sema.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaStmt.cpp --diff_from_common_commit

⚠️
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.
⚠️

View the diff from clang-format here.
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index ecf1cba8c..e997dd300 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4826,7 +4826,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
   if (T.consumeOpen())
     return;
 
-  ParseScope StructScope(Actions, Scope::ClassScope|Scope::DeclScope);
+  ParseScope StructScope(Actions, Scope::ClassScope | Scope::DeclScope);
   Actions.ActOnTagStartDefinition(getCurScope(), TagDecl);
 
   // `LateAttrParseExperimentalExtOnly=true` requests that only attributes
diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp
index 79938b5db..cc712489e 100644
--- a/clang/lib/Sema/Scope.cpp
+++ b/clang/lib/Sema/Scope.cpp
@@ -12,8 +12,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Sema/Scope.h"
-#include "clang/Sema/Sema.h"
 #include "clang/AST/Decl.h"
+#include "clang/Sema/Sema.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace clang;
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index efd33fd39..153bf071e 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -2987,7 +2987,7 @@ void Sema::ExitScope() {
 
 void Sema::FreeScopes() {
   // Take care not to delete 'CurScope' twice.
-  auto IsCurScope = [&](auto& S) { return S.get() == CurScope; };
+  auto IsCurScope = [&](auto &S) { return S.get() == CurScope; };
   if (llvm::find_if(ScopeCache, IsCurScope) == ScopeCache.end())
     delete CurScope;
 
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index da1005f8a..f5b388868 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2738,12 +2738,10 @@ Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars(
   // Build auto __begin = begin-expr, __end = end-expr.
   // Divide by 2, since the variables are in the inner scope (loop body).
   const auto DepthStr = std::to_string(S->getDepth() / 2);
-  VarDecl *BeginVar =
-      BuildForRangeVarDecl(*this, ColonLoc, AutoType,
-                           std::string("__begin") + DepthStr, Constexpr);
-  VarDecl *EndVar =
-      BuildForRangeVarDecl(*this, ColonLoc, AutoType,
-                           std::string("__end") + DepthStr, Constexpr);
+  VarDecl *BeginVar = BuildForRangeVarDecl(
+      *this, ColonLoc, AutoType, std::string("__begin") + DepthStr, Constexpr);
+  VarDecl *EndVar = BuildForRangeVarDecl(
+      *this, ColonLoc, AutoType, std::string("__end") + DepthStr, Constexpr);
 
   // Build begin-expr and end-expr and attach to __begin and __end variables.
   ExprResult BeginExpr, EndExpr;

@Sirraide Sirraide changed the title [Clang] [NFC] Expansion Statements (Part 4: for-range refactor) [Clang] [NFC] Expansion Statements (Part 4: for-range and ParseScope refactor) Dec 3, 2025
@Sirraide
Copy link
Member Author

Sirraide commented Dec 3, 2025

Update: Iterating expansion statements require building a lambda in Sema (see part 5 in this patch series), which necessitated moving ParseScope and friends from the Parser into Sema; I’ve included this refactor in this NFC patch.

As an aside, it makes more sense for ParseScope etc. to be in Sema anyway since its implementation exclusively calls Sema functions—technically, it does call some parser functions, but those functions then in turn only call Sema functions and don’t actually access any parser state. The only reference to parser state is that the current token location is passed to Sema... but Sema doesn’t do anything w/ it, so I just removed that parameter.

@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-3-enumerating branch from 56ad658 to 2a9bd94 Compare December 3, 2025 21:23
@Sirraide Sirraide force-pushed the users/Sirraide/expansion-stmts-4-nfc-for-range-refactor branch from 46e2331 to 1553a83 Compare December 3, 2025 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++26 clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:openmp OpenMP related changes to Clang clang Clang issues not falling into any other category HLSL HLSL Language Support

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants