Skip to content

Commit bc31eb1

Browse files
committed
[CS] Solve all conjunctions in source order
Previously we would only do source ordering for ClosureExprs, but other conjunctions need to have their source location taken into account too, in order to make sure we don't try and type-check e.g a TapExpr in a second closure before we type-check the first closure. Also while here, switch to `std::min_element` instead of sorting, and treat invalid source locations as incomparable. rdar://113326835
1 parent 1e3dd5c commit bc31eb1

File tree

2 files changed

+64
-12
lines changed

2 files changed

+64
-12
lines changed

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)