[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