Skip to content

Commit 1ef4f80

Browse files
authored
Merge pull request #67800 from hamishknight/conjunction-order
2 parents 2667df0 + bc31eb1 commit 1ef4f80

File tree

4 files changed

+86
-34
lines changed

4 files changed

+86
-34
lines changed

include/swift/AST/Expr.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -880,12 +880,8 @@ class TapExpr : public Expr {
880880
void setBody(BraceStmt * b) { Body = b; }
881881

882882
SourceLoc getLoc() const { return SubExpr ? SubExpr->getLoc() : SourceLoc(); }
883-
884-
SourceLoc getStartLoc() const {
885-
return SubExpr ? SubExpr->getStartLoc() : SourceLoc();
886-
}
887883

888-
SourceLoc getEndLoc() const;
884+
SourceRange getSourceRange() const;
889885

890886
static bool classof(const Expr *E) {
891887
return E->getKind() == ExprKind::Tap;

lib/AST/Expr.cpp

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2611,29 +2611,33 @@ void InterpolatedStringLiteralExpr::forEachSegment(ASTContext &Ctx,
26112611
TapExpr::TapExpr(Expr * SubExpr, BraceStmt *Body)
26122612
: Expr(ExprKind::Tap, /*Implicit=*/true),
26132613
SubExpr(SubExpr), Body(Body) {
2614-
if (Body) {
2615-
assert(!Body->empty() &&
2616-
Body->getFirstElement().isDecl(DeclKind::Var) &&
2617-
"First element of Body should be a variable to init with the subExpr");
2618-
}
2614+
assert(Body);
2615+
assert(!Body->empty() &&
2616+
Body->getFirstElement().isDecl(DeclKind::Var) &&
2617+
"First element of Body should be a variable to init with the subExpr");
26192618
}
26202619

26212620
VarDecl * TapExpr::getVar() const {
26222621
return dyn_cast<VarDecl>(Body->getFirstElement().dyn_cast<Decl *>());
26232622
}
26242623

2625-
SourceLoc TapExpr::getEndLoc() const {
2626-
// Include the body in the range, assuming the body follows the SubExpr.
2627-
// Also, be (perhaps overly) defensive about null pointers & invalid
2628-
// locations.
2629-
if (auto *const b = getBody()) {
2630-
const auto be = b->getEndLoc();
2631-
if (be.isValid())
2632-
return be;
2633-
}
2634-
if (auto *const se = getSubExpr())
2635-
return se->getEndLoc();
2636-
return SourceLoc();
2624+
SourceRange TapExpr::getSourceRange() const {
2625+
if (!SubExpr)
2626+
return Body->getSourceRange();
2627+
2628+
SourceLoc start = SubExpr->getStartLoc();
2629+
if (!start.isValid())
2630+
start = Body->getStartLoc();
2631+
if (!start.isValid())
2632+
return SourceRange();
2633+
2634+
SourceLoc end = Body->getEndLoc();
2635+
if (!end.isValid())
2636+
end = SubExpr->getEndLoc();
2637+
if (!end.isValid())
2638+
return SourceRange();
2639+
2640+
return SourceRange(start, end);
26372641
}
26382642

26392643
RegexLiteralExpr *

lib/Sema/CSSolver.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2438,25 +2438,31 @@ Constraint *ConstraintSystem::selectConjunction() {
24382438

24392439
auto &SM = getASTContext().SourceMgr;
24402440

2441-
// All of the multi-statement closures should be solved in order of their
2442-
// apperance in the source.
2443-
llvm::sort(
2444-
conjunctions, [&](Constraint *conjunctionA, Constraint *conjunctionB) {
2441+
// Conjunctions should be solved in order of their apperance in the source.
2442+
// This is important because once a conjunction is solved, we don't re-visit
2443+
// it, so we need to make sure we don't solve it before another conjuntion
2444+
// that could provide it with necessary type information. Source order
2445+
// provides an easy to reason about and quick way of establishing this.
2446+
return *std::min_element(
2447+
conjunctions.begin(), conjunctions.end(),
2448+
[&](Constraint *conjunctionA, Constraint *conjunctionB) {
24452449
auto *locA = conjunctionA->getLocator();
24462450
auto *locB = conjunctionB->getLocator();
2447-
24482451
if (!(locA && locB))
24492452
return false;
24502453

2451-
auto *closureA = getAsExpr<ClosureExpr>(locA->getAnchor());
2452-
auto *closureB = getAsExpr<ClosureExpr>(locB->getAnchor());
2454+
auto anchorA = locA->getAnchor();
2455+
auto anchorB = locB->getAnchor();
2456+
if (!(anchorA && anchorB))
2457+
return false;
24532458

2454-
return closureA && closureB
2455-
? SM.isBeforeInBuffer(closureA->getLoc(), closureB->getLoc())
2456-
: false;
2457-
});
2459+
auto slocA = anchorA.getStartLoc();
2460+
auto slocB = anchorB.getStartLoc();
2461+
if (!(slocA.isValid() && slocB.isValid()))
2462+
return false;
24582463

2459-
return conjunctions.front();
2464+
return SM.isBeforeInBuffer(slocA, slocB);
2465+
});
24602466
}
24612467

24622468
bool DisjunctionChoice::attempt(ConstraintSystem &cs) const {

test/Constraints/rdar113326835.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// rdar://113326835 - Make sure we type-check the conjunctions in source order,
4+
// the first closure should be type-checked before we attempt the
5+
// TapExpr/SingleValueExpr conjunctions, since they rely on 'k' being resolved.
6+
7+
func global<T>(_ x: T) -> String { "" }
8+
func global(_ x: Any.Type) -> String { "" }
9+
10+
protocol P {
11+
associatedtype X
12+
}
13+
14+
struct Q<X>: P {
15+
init() {}
16+
func bar(_: String) -> Self { fatalError() }
17+
func qux<U: P>(_: (X) -> U) -> Q<U.X> { fatalError() }
18+
}
19+
20+
struct J<X>: P {
21+
init(_: X) {}
22+
func baz<T>(_ transform: (X) -> T) -> Q<T> { fatalError() }
23+
}
24+
25+
func foo(a: Int) -> Q<String> {
26+
J(a)
27+
.baz { x in
28+
()
29+
return a
30+
}
31+
.qux { k in
32+
Q<String>().bar("\(k)")
33+
}
34+
}
35+
36+
func bar(a: Int) -> Q<String> {
37+
J(a)
38+
.baz { x in
39+
()
40+
return a
41+
}
42+
.qux { k in
43+
Q<String>().bar(if .random() { global(k) } else { global(k) })
44+
// expected-error@-1 {{'if' may only be used as expression in return, throw, or as the source of an assignment}}
45+
}
46+
}

0 commit comments

Comments
 (0)