Skip to content

Commit e7aac5d

Browse files
committed
for expressions scaffolding, making constraints work
1 parent 757de6e commit e7aac5d

File tree

11 files changed

+147
-22
lines changed

11 files changed

+147
-22
lines changed

include/swift/AST/Expr.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6498,16 +6498,22 @@ class KeyPathDotExpr : public Expr {
64986498
}
64996499
};
65006500

6501+
struct ForCollectionInit {
6502+
VarDecl *ForAccumulatorDecl;
6503+
PatternBindingDecl *ForAccumulatorBinding;
6504+
};
6505+
65016506
/// An expression that may wrap a statement which produces a single value.
65026507
class SingleValueStmtExpr : public Expr {
65036508
public:
65046509
enum class Kind {
6505-
If, Switch, Do, DoCatch
6510+
If, Switch, Do, DoCatch, For
65066511
};
65076512

65086513
private:
65096514
Stmt *S;
65106515
DeclContext *DC;
6516+
std::optional<ForCollectionInit> ForExpressionPreamble;
65116517

65126518
SingleValueStmtExpr(Stmt *S, DeclContext *DC)
65136519
: Expr(ExprKind::SingleValueStmt, /*isImplicit*/ true), S(S), DC(DC) {}
@@ -6572,6 +6578,14 @@ class SingleValueStmtExpr : public Expr {
65726578

65736579
SourceRange getSourceRange() const;
65746580

6581+
std::optional<ForCollectionInit> getForExpressionPreamble() const {
6582+
return this->ForExpressionPreamble;
6583+
}
6584+
6585+
void setForExpressionPreamble(ForCollectionInit newPreamble) {
6586+
this->ForExpressionPreamble = newPreamble;
6587+
}
6588+
65756589
static bool classof(const Expr *E) {
65766590
return E->getKind() == ExprKind::SingleValueStmt;
65776591
}

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ IDENTIFIER(alloc)
2929
IDENTIFIER(allocWithZone)
3030
IDENTIFIER(allZeros)
3131
IDENTIFIER(accumulated)
32+
IDENTIFIER(append)
3233
IDENTIFIER(ActorType)
3334
IDENTIFIER(Any)
3435
IDENTIFIER(ArrayLiteralElement)

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,9 @@ EXPERIMENTAL_FEATURE(ThenStatements, false)
424424
/// Enable 'do' expressions.
425425
EXPERIMENTAL_FEATURE(DoExpressions, false)
426426

427+
/// Enable 'for' expressions.
428+
EXPERIMENTAL_FEATURE(ForExpressions, false)
429+
427430
/// Enable implicitly treating the last expression in a function, closure,
428431
/// and 'if'/'switch' expression as the result.
429432
EXPERIMENTAL_FEATURE(ImplicitLastExprResults, false)

include/swift/Sema/SyntacticElementTarget.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
//
13-
// This file defines the SyntacticElementTarget class.
13+
// This file defines the SyntacticElementTarget class (a unit of
14+
// type-checking).
1415
//
1516
//===----------------------------------------------------------------------===//
1617

@@ -59,8 +60,8 @@ struct PackIterationInfo {
5960
/// within the constraint system.
6061
using ForEachStmtInfo = TaggedUnion<SequenceIterationInfo, PackIterationInfo>;
6162

62-
/// Describes the target to which a constraint system's solution can be
63-
/// applied.
63+
/// Describes the target (a unit of type-checking) to which a constraint
64+
/// system's solution can be applied.
6465
class SyntacticElementTarget {
6566
public:
6667
enum class Kind {

lib/AST/ASTDumper.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4496,6 +4496,10 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, Label>,
44964496
void visitSingleValueStmtExpr(SingleValueStmtExpr *E, Label label) {
44974497
printCommon(E, "single_value_stmt_expr", label);
44984498
printDeclContext(E);
4499+
if (auto preamble = E->getForExpressionPreamble()) {
4500+
printRec(preamble->ForAccumulatorDecl, Label::optional("for_preamble_accumulator_decl"));
4501+
printRec(preamble->ForAccumulatorBinding, Label::optional("for_preamble_accumulator_binding"));
4502+
}
44994503
printRec(E->getStmt(), &E->getDeclContext()->getASTContext(),
45004504
Label::optional("stmt"));
45014505
printFoot();

lib/AST/ASTWalker.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,16 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
13851385
Expr *visitKeyPathDotExpr(KeyPathDotExpr *E) { return E; }
13861386

13871387
Expr *visitSingleValueStmtExpr(SingleValueStmtExpr *E) {
1388+
if (auto preamble = E->getForExpressionPreamble()) {
1389+
if (doIt(preamble->ForAccumulatorDecl)) {
1390+
return nullptr;
1391+
}
1392+
1393+
if (doIt(preamble->ForAccumulatorBinding)) {
1394+
return nullptr;
1395+
}
1396+
}
1397+
13881398
if (auto *S = doIt(E->getStmt())) {
13891399
E->setStmt(S);
13901400
} else {

lib/AST/Expr.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2763,6 +2763,8 @@ SingleValueStmtExpr::Kind SingleValueStmtExpr::getStmtKind() const {
27632763
return Kind::Do;
27642764
case StmtKind::DoCatch:
27652765
return Kind::DoCatch;
2766+
case StmtKind::ForEach:
2767+
return Kind::For;
27662768
default:
27672769
llvm_unreachable("Unhandled kind!");
27682770
}
@@ -2781,6 +2783,9 @@ SingleValueStmtExpr::getBranches(SmallVectorImpl<Stmt *> &scratch) const {
27812783
return scratch;
27822784
case Kind::DoCatch:
27832785
return cast<DoCatchStmt>(getStmt())->getBranches(scratch);
2786+
case Kind::For:
2787+
scratch.push_back(cast<ForEachStmt>(getStmt())->getBody());
2788+
return scratch;
27842789
}
27852790
llvm_unreachable("Unhandled case in switch!");
27862791
}

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ UNINTERESTING_FEATURE(RegionBasedIsolation)
116116
UNINTERESTING_FEATURE(PlaygroundExtendedCallbacks)
117117
UNINTERESTING_FEATURE(ThenStatements)
118118
UNINTERESTING_FEATURE(DoExpressions)
119+
UNINTERESTING_FEATURE(ForExpressions)
119120
UNINTERESTING_FEATURE(ImplicitLastExprResults)
120121
UNINTERESTING_FEATURE(RawLayout)
121122
UNINTERESTING_FEATURE(Embedded)

lib/Sema/CSSyntacticElement.cpp

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,8 +1488,34 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) {
14881488
auto &ctx = getASTContext();
14891489

14901490
auto *loc = getConstraintLocator(E);
1491-
Type resultTy = createTypeVariable(loc, /*options*/ 0);
1492-
setType(E, resultTy);
1491+
Type resultType = createTypeVariable(loc, /*options*/ 0);
1492+
setType(E, resultType);
1493+
1494+
if (E->getStmtKind() == SingleValueStmtExpr::Kind::For) {
1495+
auto *rrcProtocol = ctx.getProtocol(KnownProtocolKind::RangeReplaceableCollection);
1496+
auto *sequenceProtocol = ctx.getProtocol(KnownProtocolKind::Sequence);
1497+
1498+
addConstraint(ConstraintKind::ConformsTo,
1499+
resultType,
1500+
rrcProtocol->getDeclaredInterfaceType(),
1501+
loc);
1502+
Type elementTypeVar = createTypeVariable(loc, /*options*/ 0);
1503+
Type elementType = DependentMemberType::get(resultType, sequenceProtocol->getAssociatedType(ctx.Id_Element));
1504+
1505+
addConstraint(ConstraintKind::Bind, elementTypeVar, elementType, loc);
1506+
addConstraint(ConstraintKind::Bind, resultType, ArraySliceType::get(elementTypeVar), loc);
1507+
1508+
auto *binding = E->getForExpressionPreamble()->ForAccumulatorBinding;
1509+
1510+
auto *initializer = binding->getInit(0);
1511+
auto target = SyntacticElementTarget::forInitialization(initializer, Type(), binding, 0, false);
1512+
1513+
if (generateConstraints(target)) {
1514+
return true;
1515+
}
1516+
1517+
addConstraint(ConstraintKind::Conversion, getType(initializer), resultType, loc);
1518+
}
14931519

14941520
// Propagate the implied result kind from the if/switch expression itself
14951521
// into the branches.
@@ -1513,21 +1539,24 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) {
15131539
auto *loc = getConstraintLocator(
15141540
E, {LocatorPathElt::SingleValueStmtResult(idx), ctpElt});
15151541

1516-
ContextualTypeInfo info(resultTy, CTP_SingleValueStmtBranch, loc);
1542+
ContextualTypeInfo info(resultType, CTP_SingleValueStmtBranch, loc);
15171543
setContextualInfo(result, info);
15181544
}
15191545

15201546
TypeJoinExpr *join = nullptr;
1521-
if (branches.empty()) {
1522-
// If we only have statement branches, the expression is typed as Void. This
1523-
// should only be the case for 'if' and 'switch' statements that must be
1524-
// expressions that have branches that all end in a throw, and we'll warn
1525-
// that we've inferred Void.
1526-
addConstraint(ConstraintKind::Bind, resultTy, ctx.getVoidType(), loc);
1527-
} else {
1528-
// Otherwise, we join the result types for each of the branches.
1529-
join = TypeJoinExpr::forBranchesOfSingleValueStmtExpr(
1530-
ctx, resultTy, E, AllocationArena::ConstraintSolver);
1547+
1548+
if (E->getStmtKind() != SingleValueStmtExpr::Kind::For) {
1549+
if (branches.empty()) {
1550+
// If we only have statement branches, the expression is typed as Void. This
1551+
// should only be the case for 'if' and 'switch' statements that must be
1552+
// expressions that have branches that all end in a throw, and we'll warn
1553+
// that we've inferred Void.
1554+
addConstraint(ConstraintKind::Bind, resultType, ctx.getVoidType(), loc);
1555+
} else {
1556+
// Otherwise, we join the result types for each of the branches.
1557+
join = TypeJoinExpr::forBranchesOfSingleValueStmtExpr(
1558+
ctx, resultType, E, AllocationArena::ConstraintSolver);
1559+
}
15311560
}
15321561

15331562
// If this is an implied return in a closure, we need to account for the fact
@@ -1568,11 +1597,11 @@ bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) {
15681597
if (auto *closureTy = getClosureTypeIfAvailable(CE)) {
15691598
auto closureResultTy = closureTy->getResult();
15701599
auto *bindToClosure = Constraint::create(
1571-
*this, ConstraintKind::Bind, resultTy, closureResultTy, loc);
1600+
*this, ConstraintKind::Bind, resultType, closureResultTy, loc);
15721601
bindToClosure->setFavored();
15731602

15741603
auto *bindToVoid = Constraint::create(*this, ConstraintKind::Bind,
1575-
resultTy, ctx.getVoidType(), loc);
1604+
resultType, ctx.getVoidType(), loc);
15761605

15771606
addDisjunctionConstraint({bindToClosure, bindToVoid}, loc);
15781607
}

lib/Sema/PreCheckTarget.cpp

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -11,7 +11,8 @@
1111
//===----------------------------------------------------------------------===//
1212
//
1313
// Pre-checking resolves unqualified name references, type expressions and
14-
// operators.
14+
// operators. Target in this context refers to `SyntacticElementTarget`, which
15+
// is a unit of type-checking.
1516
//
1617
//===----------------------------------------------------------------------===//
1718

@@ -1190,6 +1191,11 @@ class PreCheckTarget final : public ASTWalker {
11901191
/// For the given statement, mark any valid SingleValueStmtExpr children.
11911192
void markAnyValidSingleValueStmts(Stmt *S);
11921193

1194+
/// For the given single value expr that's a `for`-expression, run the
1195+
/// desugaring transformation. For all other kinds of single value statement
1196+
/// expressions do nothing.
1197+
void transformForExpression(SingleValueStmtExpr *E);
1198+
11931199
PreCheckTarget(DeclContext *dc) : Ctx(dc->getASTContext()), DC(dc) {}
11941200

11951201
public:
@@ -1386,8 +1392,10 @@ class PreCheckTarget final : public ASTWalker {
13861392
if (auto *assignment = dyn_cast<AssignExpr>(expr))
13871393
markAcceptableDiscardExprs(assignment->getDest());
13881394

1389-
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(expr))
1395+
if (auto *SVE = dyn_cast<SingleValueStmtExpr>(expr)) {
13901396
checkSingleValueStmtExpr(SVE);
1397+
transformForExpression(SVE);
1398+
}
13911399

13921400
return finish(true, expr);
13931401
}
@@ -1700,6 +1708,49 @@ void PreCheckTarget::checkSingleValueStmtExpr(SingleValueStmtExpr *SVE) {
17001708
}
17011709
}
17021710

1711+
void PreCheckTarget::transformForExpression(SingleValueStmtExpr *SVE) {
1712+
if (SVE->getStmtKind() != SingleValueStmtExpr::Kind::For) {
1713+
return;
1714+
}
1715+
1716+
auto *declCtx = SVE->getDeclContext();
1717+
auto &astCtx = declCtx->getASTContext();
1718+
1719+
auto sveLoc = SVE->getLoc();
1720+
1721+
auto *varDecl = new(astCtx) VarDecl(false, VarDecl::Introducer::Var, sveLoc,
1722+
astCtx.getIdentifier("$forExpressionResult"), declCtx);
1723+
1724+
auto namedPattern = NamedPattern::createImplicit(astCtx, varDecl);
1725+
1726+
auto *initFunc = new(astCtx) UnresolvedMemberExpr(sveLoc, DeclNameLoc(), DeclNameRef(DeclBaseName::createConstructor()), true);
1727+
auto *callExpr = CallExpr::createImplicitEmpty(astCtx, initFunc);
1728+
auto *initExpr = new(astCtx) UnresolvedMemberChainResultExpr(callExpr, initFunc);
1729+
1730+
auto *bindingDecl = PatternBindingDecl::createImplicit(astCtx, StaticSpellingKind::None, namedPattern, initExpr, declCtx);
1731+
1732+
SVE->setForExpressionPreamble({ varDecl, bindingDecl });
1733+
1734+
// For-expressions always have a single branch.
1735+
SmallVector<Stmt *, 1> scratch;
1736+
for (auto *branch : SVE->getBranches(scratch)) {
1737+
auto *BS = dyn_cast<BraceStmt>(branch);
1738+
if (!BS)
1739+
continue;
1740+
1741+
auto &result = BS->getElements().back();
1742+
if (auto stmt = result.dyn_cast<Stmt *>()) {
1743+
if (auto *then = dyn_cast<ThenStmt>(stmt)) {
1744+
auto *declRefExpr = new(astCtx) DeclRefExpr(varDecl, DeclNameLoc(), true);
1745+
auto *dotExpr = new(astCtx) UnresolvedDotExpr(declRefExpr, sveLoc, DeclNameRef(DeclBaseName(astCtx.Id_append)), DeclNameLoc(), true);
1746+
auto *argumentList = ArgumentList::createImplicit(astCtx, { Argument::unlabeled(then->getResult()) });
1747+
auto *callExpr = CallExpr::createImplicit(astCtx, dotExpr, argumentList);
1748+
result = callExpr;
1749+
}
1750+
}
1751+
}
1752+
}
1753+
17031754
void PreCheckTarget::markAnyValidSingleValueStmts(Expr *E) {
17041755
auto findAssignment = [&]() -> AssignExpr * {
17051756
// Don't consider assignments if we have a parent expression (as otherwise

0 commit comments

Comments
 (0)