-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[Clang] [C++26] Expansion Statements (Part 6: Destructuring Expansion Statements) #169685
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sirraide
wants to merge
1
commit into
users/Sirraide/expansion-stmts-5-iterating
Choose a base branch
from
users/Sirraide/expansion-stmts-6-destructuring
base: users/Sirraide/expansion-stmts-5-iterating
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
[Clang] [C++26] Expansion Statements (Part 6: Destructuring Expansion Statements) #169685
Sirraide
wants to merge
1
commit into
users/Sirraide/expansion-stmts-5-iterating
from
users/Sirraide/expansion-stmts-6-destructuring
+153
−16
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This was referenced Nov 26, 2025
Member
Author
This was referenced Nov 26, 2025
🐧 Linux x64 Test Results
|
Member
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-codegen Author: None (Sirraide) ChangesFull diff: https://github.com/llvm/llvm-project/pull/169685.diff 4 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 96292d0a4e306..0ddaa461deff5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3702,6 +3702,8 @@ def err_conflicting_codeseg_attribute : Error<
def warn_duplicate_codeseg_attribute : Warning<
"duplicate code segment specifiers">, InGroup<Section>;
+def err_expansion_stmt_invalid_init : Error<
+ "cannot expand expression of type %0">;
def err_expansion_stmt_lambda : Error<
"cannot expand lambda closure type">;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f001851b36ff7..b102544342416 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15703,6 +15703,9 @@ class Sema final : public SemaBase {
BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range,
Expr *Idx);
+ ExprResult BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD,
+ Expr *Idx);
+
std::optional<uint64_t>
ComputeExpansionSize(CXXExpansionStmtPattern *Expansion);
///@}
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 228551c27d2d8..fcc951503deb9 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -173,6 +173,52 @@ TryBuildIterableExpansionStmtInitializer(Sema &S, Expr *ExpansionInitializer,
return Data;
}
+static StmtResult BuildDestructuringCXXExpansionStmt(
+ Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc,
+ bool VarIsConstexpr,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+ 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);
+
+ UnsignedOrNone Arity =
+ S.GetDecompositionElementCount(ExpansionInitializer->getType(), ColonLoc);
+
+ if (!Arity) {
+ S.Diag(ExpansionInitializer->getBeginLoc(),
+ diag::err_expansion_stmt_invalid_init)
+ << ExpansionInitializer->getType()
+ << ExpansionInitializer->getSourceRange();
+ return StmtError();
+ }
+
+ QualType AutoRRef = S.Context.getAutoRRefDeductType();
+ SmallVector<BindingDecl *> Bindings;
+ for (unsigned I = 0; I < *Arity; ++I)
+ Bindings.push_back(BindingDecl::Create(
+ S.Context, S.CurContext, ColonLoc,
+ S.getPreprocessor().getIdentifierInfo("__u" + std::to_string(I)),
+ AutoRRef));
+
+ TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef);
+ auto *DD =
+ DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc,
+ AutoRRef, TSI, SC_Auto, Bindings);
+
+ if (VarIsConstexpr)
+ DD->setConstexpr(true);
+
+ S.ApplyForRangeOrExpansionStatementLifetimeExtension(DD, LifetimeExtendTemps);
+ S.AddInitializerToDecl(DD, ExpansionInitializer, false);
+ return S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(DD), ColonLoc, ColonLoc);
+}
+
CXXExpansionStmtDecl *
Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
SourceLocation TemplateKWLoc) {
@@ -309,8 +355,31 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
Data.EndDecl, LParenLoc, ColonLoc, RParenLoc);
}
- Diag(ESD->getLocation(), diag::err_expansion_statements_todo);
- return StmtError();
+ // If not, try destructuring.
+ StmtResult DecompDeclStmt = BuildDestructuringCXXExpansionStmt(
+ *this, ExpansionInitializer, ColonLoc, ExpansionVar->isConstexpr(),
+ LifetimeExtendTemps);
+ if (DecompDeclStmt.isInvalid()) {
+ ActOnInitializerError(ExpansionVar);
+ return StmtError();
+ }
+
+ auto *DS = DecompDeclStmt.getAs<DeclStmt>();
+ auto *DD = cast<DecompositionDecl>(DS->getSingleDecl());
+ if (DD->isInvalidDecl())
+ return StmtError();
+
+ ExprResult Select = BuildCXXDestructuringExpansionSelectExpr(DD, Index);
+ if (Select.isInvalid()) {
+ ActOnInitializerError(ExpansionVar);
+ return StmtError();
+ }
+
+ if (FinaliseExpansionVar(*this, ExpansionVar, Select))
+ return StmtError();
+
+ return new (Context) CXXDestructuringExpansionStmtPattern(
+ ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc);
}
StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
@@ -339,8 +408,9 @@ StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
Shared.push_back(Iter->getRangeVarStmt());
Shared.push_back(Iter->getBeginVarStmt());
Shared.push_back(Iter->getEndVarStmt());
- } else {
- assert(isa<CXXEnumeratingExpansionStmtPattern>(Expansion) && "TODO");
+ } else if (auto *Destructuring =
+ dyn_cast<CXXDestructuringExpansionStmtPattern>(Expansion)) {
+ Shared.push_back(Destructuring->getDecompositionDeclStmt());
}
// Return an empty statement if the range is empty.
@@ -409,6 +479,23 @@ Sema::BuildCXXExpansionInitListSelectExpr(CXXExpansionInitListExpr *Range,
return Range->getExprs()[I];
}
+ExprResult Sema::BuildCXXDestructuringExpansionSelectExpr(DecompositionDecl *DD,
+ Expr *Idx) {
+ if (Idx->isValueDependent())
+ return new (Context) CXXDestructuringExpansionSelectExpr(Context, DD, Idx);
+
+ Expr::EvalResult ER;
+ if (!Idx->EvaluateAsInt(ER, Context))
+ llvm_unreachable("Failed to evaluate expansion index");
+
+ uint64_t I = ER.Val.getInt().getZExtValue();
+ MarkAnyDeclReferenced(Idx->getBeginLoc(), DD, true);
+ if (auto *BD = DD->bindings()[I]; auto *HVD = BD->getHoldingVar())
+ return HVD->getInit();
+ else
+ return BD->getBinding();
+}
+
std::optional<uint64_t>
Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
assert(!Expansion->hasDependentSize());
@@ -468,5 +555,9 @@ Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
return ER.Val.getInt().getZExtValue();
}
- llvm_unreachable("TODO");
+ if (auto *Destructuring =
+ dyn_cast<CXXDestructuringExpansionStmtPattern>(Expansion))
+ return Destructuring->getDecompositionDecl()->bindings().size();
+
+ llvm_unreachable("Invalid expansion statement class");
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index d471b106065fb..389df3b933745 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9383,20 +9383,35 @@ StmtResult TreeTransform<Derived>::TransformCXXDependentExpansionStmtPattern(
CXXDependentExpansionStmtPattern *S) {
TransformCXXExpansionStmtPatternResult Common;
ExprResult ExpansionInitializer;
+ SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
- Common = TransformCXXExpansionStmtPatternCommonParts(S);
- if (!Common.isValid())
- return StmtError();
+ // Collect lifetime-extended temporaries in case this ends up being a
+ // destructuring expansion statement (for other kinds of expansion statements,
+ // this should make no difference since we ignore 'LifetimeExtendTemps' for
+ // those).
+ {
+ EnterExpressionEvaluationContext ExprEvalCtx(
+ SemaRef, SemaRef.currentEvaluationContext().Context);
+ SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+ SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
- ExpansionInitializer =
- getDerived().TransformExpr(S->getExpansionInitializer());
- if (ExpansionInitializer.isInvalid())
- return StmtError();
+ Common = TransformCXXExpansionStmtPatternCommonParts(S);
+ if (!Common.isValid())
+ return StmtError();
+
+ ExpansionInitializer =
+ getDerived().TransformExpr(S->getExpansionInitializer());
+ if (ExpansionInitializer.isInvalid())
+ return StmtError();
+
+ LifetimeExtendTemps =
+ SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps;
+ }
StmtResult Expansion = SemaRef.BuildNonEnumeratingCXXExpansionStmtPattern(
Common.NewESD, Common.NewInit, Common.NewExpansionVarDecl,
ExpansionInitializer.get(), S->getLParenLoc(), S->getColonLoc(),
- S->getRParenLoc(), /*LifetimeExtendTemps=*/{});
+ S->getRParenLoc(), LifetimeExtendTemps);
if (Expansion.isInvalid())
return StmtError();
@@ -9455,8 +9470,23 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
SmallVector<Stmt *> SharedStmts;
SmallVector<Stmt *> Instantiations;
- if (TransformStmts(SharedStmts, S->getSharedStmts()))
- return StmtError();
+ // Apply lifetime extension to the shared statements if this was a
+ // destructuring expansion statement.
+ {
+ EnterExpressionEvaluationContext ExprEvalCtx(
+ SemaRef, SemaRef.currentEvaluationContext().Context);
+ SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+ SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
+ if (TransformStmts(SharedStmts, S->getSharedStmts()))
+ return StmtError();
+
+ if (S->shouldApplyLifetimeExtensionToSharedStmts()) {
+ auto *VD =
+ cast<VarDecl>(cast<DeclStmt>(SharedStmts.front())->getSingleDecl());
+ SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension(
+ VD, SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps);
+ }
+ }
if (TransformStmts(Instantiations, S->getInstantiations()))
return StmtError();
@@ -9488,7 +9518,18 @@ ExprResult TreeTransform<Derived>::TransformCXXExpansionInitListSelectExpr(
template <typename Derived>
ExprResult TreeTransform<Derived>::TransformCXXDestructuringExpansionSelectExpr(
CXXDestructuringExpansionSelectExpr *E) {
- llvm_unreachable("TOOD");
+ Decl *DD = getDerived().TransformDecl(
+ E->getDecompositionDecl()->getLocation(), E->getDecompositionDecl());
+ ExprResult Idx = getDerived().TransformExpr(E->getIndexExpr());
+ if (!DD || Idx.isInvalid())
+ return ExprError();
+
+ if (!getDerived().AlwaysRebuild() && DD == E->getDecompositionDecl() &&
+ Idx.get() == E->getIndexExpr())
+ return E;
+
+ return SemaRef.BuildCXXDestructuringExpansionSelectExpr(
+ cast<DecompositionDecl>(DD), Idx.get());
}
template<typename Derived>
|
43c4a23 to
45151d1
Compare
2ec657b to
1c80c09
Compare
45151d1 to
b874722
Compare
1c80c09 to
623f024
Compare
623f024 to
576f888
Compare
b874722 to
2db2d6d
Compare
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
Clang issues not falling into any other category
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.

This implements Sema and template instantiation for destructuring expansion statements.