Skip to content

Commit a407b13

Browse files
authored
[CS] Allow getCalleeLocator to look through optional chaining (#27053)
[CS] Allow getCalleeLocator to look through optional chaining
2 parents e011c67 + dc8216e commit a407b13

File tree

4 files changed

+28
-17
lines changed

4 files changed

+28
-17
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ Type FailureDiagnostic::resolveInterfaceType(Type type,
159159

160160
/// Given an apply expr, returns true if it is expected to have a direct callee
161161
/// overload, resolvable using `getChoiceFor`. Otherwise, returns false.
162-
static bool shouldHaveDirectCalleeOverload(const ApplyExpr *apply) {
163-
auto *fnExpr = apply->getFn()->getValueProvidingExpr();
162+
static bool shouldHaveDirectCalleeOverload(const CallExpr *callExpr) {
163+
auto *fnExpr = callExpr->getDirectCallee();
164164

165165
// An apply of an apply/subscript doesn't have a direct callee.
166166
if (isa<ApplyExpr>(fnExpr) || isa<SubscriptExpr>(fnExpr))
@@ -170,11 +170,9 @@ static bool shouldHaveDirectCalleeOverload(const ApplyExpr *apply) {
170170
if (isa<ClosureExpr>(fnExpr))
171171
return false;
172172

173-
// If the optionality changes, there's no direct callee.
174-
if (isa<BindOptionalExpr>(fnExpr) || isa<ForceValueExpr>(fnExpr) ||
175-
isa<OptionalTryExpr>(fnExpr)) {
173+
// No direct callee for a try!/try?.
174+
if (isa<ForceTryExpr>(fnExpr) || isa<OptionalTryExpr>(fnExpr))
176175
return false;
177-
}
178176

179177
// If we have an intermediate cast, there's no direct callee.
180178
if (isa<ExplicitCastExpr>(fnExpr))
@@ -225,20 +223,23 @@ FailureDiagnostic::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
225223
if (auto overload = getChoiceFor(anchor)) {
226224
// If we have resolved an overload for the callee, then use that to get the
227225
// function type and callee.
228-
if (auto *decl = overload->choice.getDeclOrNull())
229-
callee = decl;
230-
226+
callee = overload->choice.getDeclOrNull();
231227
rawFnType = overload->openedType;
232228
} else {
233229
// If we didn't resolve an overload for the callee, we must be dealing with
234230
// a call of an arbitrary function expr.
235231
auto *call = cast<CallExpr>(anchor);
236232
assert(!shouldHaveDirectCalleeOverload(call) &&
237233
"Should we have resolved a callee for this?");
238-
rawFnType = cs.getType(call->getFn())->getRValueType();
234+
rawFnType = cs.getType(call->getFn());
239235
}
240236

241-
auto *fnType = resolveType(rawFnType)->getAs<FunctionType>();
237+
// Try to resolve the function type by loading lvalues and looking through
238+
// optional types, which can occur for expressions like `fn?(5)`.
239+
auto *fnType = resolveType(rawFnType)
240+
->getRValueType()
241+
->lookThroughAllOptionalTypes()
242+
->getAs<FunctionType>();
242243
if (!fnType)
243244
return None;
244245

lib/Sema/ConstraintSystem.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,15 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator(Expr *expr) {
432432
return getConstraintLocator(fnLocator,
433433
ConstraintLocator::ConstructorMember);
434434
}
435-
// Otherwise fall through and look for locators anchored on the fn expr.
436-
expr = fnExpr->getSemanticsProvidingExpr();
435+
436+
// Otherwise fall through and look for locators anchored on the function
437+
// expr. For CallExprs, this can look through things like parens and
438+
// optional chaining.
439+
if (auto *callExpr = dyn_cast<CallExpr>(expr)) {
440+
expr = callExpr->getDirectCallee();
441+
} else {
442+
expr = fnExpr;
443+
}
437444
}
438445

439446
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {

test/Constraints/function.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ func returnsTakesEscapingFn() -> (@escaping () -> Int) -> Void { takesEscapingFn
169169
prefix operator ^^^
170170
prefix func ^^^(_ x: Int) -> (@escaping () -> Int) -> Void { takesEscapingFn }
171171

172-
func testWeirdFnExprs<T>(_ fn: () -> Int, _ cond: Bool, _ any: Any, genericArg: T) { // expected-note 11{{parameter 'fn' is implicitly non-escaping}}
172+
func testWeirdFnExprs<T>(_ fn: () -> Int, _ cond: Bool, _ any: Any, genericArg: T) { // expected-note 12{{parameter 'fn' is implicitly non-escaping}}
173173
(any as! (@escaping () -> Int) -> Void)(fn)
174174
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
175175

@@ -183,6 +183,9 @@ func testWeirdFnExprs<T>(_ fn: () -> Int, _ cond: Bool, _ any: Any, genericArg:
183183
(^^^5)(fn)
184184
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
185185

186+
(try! takesEscapingFn)(fn)
187+
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
188+
186189
var optFn: Optional = takesEscapingFn
187190
optFn?(fn)
188191
// expected-error@-1 {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}

test/Constraints/optional.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ class A {
1010
@objc(do_b_2:) func do_b(_ x: Int) {}
1111
@objc func do_b(_ x: Float) {}
1212

13-
@objc func do_c(x: Int) {}
14-
@objc func do_c(y: Int) {}
13+
@objc func do_c(x: Int) {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(x:)')}}
14+
@objc func do_c(y: Int) {} // expected-note {{incorrect labels for candidate (have: '(_:)', expected: '(y:)')}}
1515
}
1616

1717
func test0(_ a: AnyObject) {
@@ -20,7 +20,7 @@ func test0(_ a: AnyObject) {
2020
a.do_b?(1)
2121
a.do_b?(5.0)
2222

23-
a.do_c?(1) // expected-error {{cannot invoke value of function type with argument list '(Int)'}}
23+
a.do_c?(1) // expected-error {{no exact matches in call to instance method 'do_c'}}
2424
a.do_c?(x: 1)
2525
}
2626

0 commit comments

Comments
 (0)