[llvm-branch-commits] [clang] [Clang] [NFC] Expansion Statements (Part 4: for-range refactor) (PR #169683)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Nov 26 09:46:29 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: None (Sirraide)
<details>
<summary>Changes</summary>
[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
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]
``````````
</details>
https://github.com/llvm/llvm-project/pull/169683
More information about the llvm-branch-commits
mailing list