Skip to content

Commit 87cd5f8

Browse files
committed
[CSOptimizer] Add support for chained members without arguments
If disjunction represents a member reference that has no arguments applied, let's score that as `1` to indicate that it should be priorized. This helps in situations like `a.b + 1` where resolving `a.b` member chain helps to establish context for `+`.
1 parent 867e641 commit 87cd5f8

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,37 @@ static void findFavoredChoicesBasedOnArity(
253253
favoredChoice(choice);
254254
}
255255

256+
/// Determine whether the given disjunction serves as a base of
257+
/// another member reference i.e. `x.y` where `x` could be overloaded.
258+
static bool isPartOfMemberChain(ConstraintSystem &CS, Constraint *disjunction) {
259+
if (isOperatorDisjunction(disjunction))
260+
return false;
261+
262+
auto &CG = CS.getConstraintGraph();
263+
264+
TypeVariableType *typeVar = nullptr;
265+
266+
// If disjunction is applied, the member is chained on the result.
267+
if (auto appliedFn = CS.getAppliedDisjunctionArgumentFunction(disjunction)) {
268+
typeVar = appliedFn->getResult()->getAs<TypeVariableType>();
269+
} else {
270+
typeVar = disjunction->getNestedConstraints()[0]
271+
->getFirstType()
272+
->getAs<TypeVariableType>();
273+
}
274+
275+
if (!typeVar)
276+
return false;
277+
278+
return llvm::any_of(
279+
CG[typeVar].getConstraints(), [&typeVar](Constraint *constraint) {
280+
if (constraint->getKind() != ConstraintKind::ValueMember)
281+
return false;
282+
283+
return constraint->getFirstType()->isEqual(typeVar);
284+
});
285+
}
286+
256287
} // end anonymous namespace
257288

258289
/// Given a set of disjunctions, attempt to determine
@@ -286,8 +317,15 @@ static void determineBestChoicesInContext(
286317
auto applicableFn =
287318
getApplicableFnConstraint(cs.getConstraintGraph(), disjunction);
288319

289-
if (applicableFn.isNull())
320+
if (applicableFn.isNull()) {
321+
// If this is a chained member reference it could be prioritized since
322+
// it helps to establish context for other calls i.e. `a.b + 2` if
323+
// `a` is a disjunction it should be preferred over `+`.
324+
if (isPartOfMemberChain(cs, disjunction))
325+
recordResult(disjunction, {/*score=*/1.0});
326+
290327
continue;
328+
}
291329

292330
auto argFuncType =
293331
applicableFn.get()->getFirstType()->getAs<FunctionType>();
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -solver-expression-time-threshold=1
2+
3+
// REQUIRES: OS=macosx,no_asan
4+
// REQUIRES: objc_interop
5+
6+
import Foundation
7+
8+
struct CGRect {
9+
var x: CGFloat
10+
11+
init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) { }
12+
init(x: Double, y: Double, width: Double, height: Double) { }
13+
}
14+
15+
protocol View {}
16+
17+
extension Optional: View where Wrapped: View {}
18+
19+
extension View {
20+
func frame() -> some View { self }
21+
func frame(x: Int, y: Int, w: Int, z: Int) -> some View { self }
22+
func frame(y: Bool) -> some View { self }
23+
}
24+
25+
struct NSView {
26+
var frame: CGRect
27+
}
28+
29+
func test(margin: CGFloat, view: NSView!) -> CGRect {
30+
// `view` is first attempted as `NSView?` and only if that fails is force unwrapped
31+
return CGRect(x: view.frame.x + margin,
32+
y: view.frame.x + margin,
33+
width: view.frame.x - view.frame.x - view.frame.x - (margin * 2),
34+
height: margin)
35+
}

0 commit comments

Comments
 (0)