[llvm-branch-commits] [clang] [Clang] [C++26] Expansion Statements (Part 5: Iterating Expansion Statements) (PR #169684)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sat Jan 10 14:36:17 PST 2026


================
@@ -55,15 +75,151 @@ static bool HasDependentSize(const CXXExpansionStmtPattern *Pattern) {
     return InitListContainsPack(SelectExpr->getRangeExpr());
   }
 
-  case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
-  case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: {
+    const Expr *Begin = Pattern->getBeginVar()->getInit();
+    const Expr *End = Pattern->getEndVar()->getInit();
+    return Begin->isInstantiationDependent() || End->isInstantiationDependent();
+  }
+
   case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
+    return true;
+
+  case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
     llvm_unreachable("TODO");
   }
 
   llvm_unreachable("invalid pattern kind");
 }
 
+static IterableExpansionStmtData
+TryBuildIterableExpansionStmtInitializer(Sema &S, Expr *ExpansionInitializer,
+                                         Expr *Index, SourceLocation ColonLoc,
+                                         bool VarIsConstexpr) {
+  IterableExpansionStmtData Data;
+
+  // C++26 [stmt.expand]p3: An expression is expansion-iterable if it does not
+  // have array type [...]
+  QualType Ty = ExpansionInitializer->getType().getNonReferenceType();
+  if (Ty->isArrayType())
+    return Data;
+
+  // Lookup member and ADL 'begin()'/'end()'. Only check if they exist; even if
+  // they're deleted, inaccessible, etc., this is still an iterating expansion
+  // statement, albeit an ill-formed one.
+  DeclarationNameInfo BeginName(&S.PP.getIdentifierTable().get("begin"),
+                                ColonLoc);
+  DeclarationNameInfo EndName(&S.PP.getIdentifierTable().get("end"), ColonLoc);
+
+  // Try member lookup first.
+  bool FoundBeginEnd = false;
+  if (auto *Record = Ty->getAsCXXRecordDecl()) {
+    LookupResult BeginLR(S, BeginName, Sema::LookupMemberName);
+    LookupResult EndLR(S, EndName, Sema::LookupMemberName);
+    FoundBeginEnd = S.LookupQualifiedName(BeginLR, Record) &&
+                    S.LookupQualifiedName(EndLR, Record);
+  }
+
+  // Try ADL.
+  //
+  // If overload resolution for 'begin()' *and* 'end()' succeeds (irrespective
+  // of whether it results in a usable candidate), then assume this is an
+  // iterating expansion statement.
+  auto HasADLCandidate = [&](DeclarationName Name) {
+    OverloadCandidateSet Candidates(ColonLoc, OverloadCandidateSet::CSK_Normal);
+    OverloadCandidateSet::iterator Best;
+
+    S.AddArgumentDependentLookupCandidates(Name, ColonLoc, ExpansionInitializer,
+                                           /*ExplicitTemplateArgs=*/nullptr,
+                                           Candidates);
+
+    return Candidates.BestViableFunction(S, ColonLoc, Best) !=
+           OR_No_Viable_Function;
+  };
+
+  if (!FoundBeginEnd && (!HasADLCandidate(BeginName.getName()) ||
+                         !HasADLCandidate(EndName.getName())))
+    return Data;
+
+  auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
+  if (VarIsConstexpr)
+    Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+  EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx);
+
+  // The declarations should be attached to the parent decl context.
+  Sema::ContextRAII CtxGuard(
+      S, S.CurContext->getEnclosingNonExpansionStatementContext(),
+      /*NewThis=*/false);
+
+  // Ok, we know that this is supposed to be an iterable expansion statement;
+  // delegate to the for-range code to build the range/begin/end variables.
+  //
+  // Any failure at this point is a hard error.
+  Data.TheState = IterableExpansionStmtData::State::Error;
+  Scope *Scope = S.getCurScope();
+
+  // CWG 3131: The declaration of 'range' is of the form
+  //
+  //     constexpr[opt] decltype(auto) range = (expansion-initializer);
+  //
+  // where 'constexpr' is present iff the for-range-declaration is 'constexpr'.
+  StmtResult Var = S.BuildCXXForRangeRangeVar(
+      Scope, S.ActOnParenExpr(ColonLoc, ColonLoc, ExpansionInitializer).get(),
+      S.Context.getAutoType(QualType(), AutoTypeKeyword::DecltypeAuto,
+                            /*IsDependent*/ false),
+      VarIsConstexpr);
+  if (Var.isInvalid())
+    return Data;
+
+  // CWG 3131: Discussion around this core issue (though as of the time of
+  // writing not the resolution itself) suggests that the other variables we
+  // create here should likewise be 'constexpr' iff the range variable is
+  // declared 'constexpr'.
+  //
+  // FIXME: As of CWG 3131, 'end' is no longer used outside the lambda that
+  // performs the size calculation (despite that, CWG 3131 currently still
+  // lists it in the generated code, but this is likely an oversight). Ideally,
+  // we should only create 'begin' here instead, but that requires another
+  // substantial refactor of the for-range code.
+  auto *RangeVar = cast<DeclStmt>(Var.get());
+  Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars(
+      Scope, cast<VarDecl>(RangeVar->getSingleDecl()), ColonLoc,
+      /*CoawaitLoc=*/{},
+      /*LifetimeExtendTemps=*/{}, Sema::BFRK_Build, VarIsConstexpr);
+
+  if (!Info.isValid())
+    return Data;
+
+  StmtResult BeginStmt = S.ActOnDeclStmt(
+      S.ConvertDeclToDeclGroup(Info.BeginVar), ColonLoc, ColonLoc);
+  StmtResult EndStmt = S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(Info.EndVar),
+                                       ColonLoc, ColonLoc);
+  if (BeginStmt.isInvalid() || EndStmt.isInvalid())
+    return Data;
+
+  // Build '*(begin + i)'.
----------------
Sirraide wrote:

Was it changed again; I need to take another look at all the core issues because there were like 3 or so that all made different changes to this

https://github.com/llvm/llvm-project/pull/169684


More information about the llvm-branch-commits mailing list