Skip to content

Commit 4915513

Browse files
committed
[CS] Filter out uncallable vars when simplifying applied overloads
This improves diagnostics as we can now consider functions that don't line up exactly with the argument list if no other viable candidates exist.
1 parent d1cbc2b commit 4915513

File tree

7 files changed

+78
-18
lines changed

7 files changed

+78
-18
lines changed

include/swift/AST/Types.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,11 @@ class alignas(1 << TypeAlignInBits) TypeBase
918918
getAnyNominal());
919919
}
920920

921+
/// Checks whether this type may potentially be callable. This returns true
922+
/// for function types, metatypes, nominal types that support being called,
923+
/// and types that have not been inferred yet.
924+
bool mayBeCallable(DeclContext *dc);
925+
921926
/// Checks whether this is a type that supports being called through the
922927
/// implementation of a \c callAsFunction method. Note that this does not
923928
/// check access control.

lib/AST/Type.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,26 @@ const llvm::fltSemantics &BuiltinFloatType::getAPFloatSemantics() const {
19921992
llvm::report_fatal_error("Unknown FP semantics");
19931993
}
19941994

1995+
bool TypeBase::mayBeCallable(DeclContext *dc) {
1996+
if (is<AnyFunctionType>())
1997+
return true;
1998+
1999+
// Callable for construction.
2000+
if (is<AnyMetatypeType>())
2001+
return true;
2002+
2003+
// Unresolved types that could potentially be callable.
2004+
if (isPlaceholder() || is<UnresolvedType>() ||
2005+
isTypeParameter() || isTypeVariableOrMember()) {
2006+
return true;
2007+
}
2008+
// Callable nominal types.
2009+
if (isCallAsFunctionType(dc) || hasDynamicCallableAttribute())
2010+
return true;
2011+
2012+
return false;
2013+
}
2014+
19952015
bool TypeBase::mayHaveSuperclass() {
19962016
if (getClassOrBoundGenericClass())
19972017
return true;

lib/Sema/CSApply.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7747,9 +7747,14 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
77477747
callee = resolveConcreteDeclRef(decl, calleeLoc);
77487748
}
77497749

7750+
// Make sure we have a function type that is callable. This helps ensure
7751+
// Type::mayBeCallable stays up-to-date.
7752+
auto fnRValueTy = cs.getType(fn)->getRValueType();
7753+
assert(fnRValueTy->mayBeCallable(dc));
7754+
77507755
// If this is an implicit call to a `callAsFunction` method, build the
77517756
// appropriate member reference.
7752-
if (cs.getType(fn)->getRValueType()->isCallAsFunctionType(dc)) {
7757+
if (fnRValueTy->isCallAsFunctionType(dc)) {
77537758
fn = buildCallAsFunctionMethodRef(*this, apply, *overload, calleeLoc);
77547759
if (!fn)
77557760
return nullptr;

lib/Sema/CSSimplify.cpp

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ bool constraints::doesMemberRefApplyCurriedSelf(Type baseTy,
135135
}
136136

137137
static bool areConservativelyCompatibleArgumentLabels(
138-
OverloadChoice choice, SmallVectorImpl<FunctionType::Param> &args,
138+
ConstraintSystem &cs, OverloadChoice choice,
139+
SmallVectorImpl<FunctionType::Param> &args,
139140
MatchCallArgumentListener &listener,
140141
Optional<unsigned> unlabeledTrailingClosureArgIndex) {
141142
ValueDecl *decl = nullptr;
@@ -155,23 +156,35 @@ static bool areConservativelyCompatibleArgumentLabels(
155156
return true;
156157
}
157158

158-
if (!decl->hasParameterList())
159-
return true;
160-
161-
// This is a member lookup, which generally means that the call arguments
162-
// (if we have any) will apply to the second level of parameters, with
163-
// the member lookup applying the curried self at the first level. But there
164-
// are cases where we can get an unapplied declaration reference back.
159+
// If this is a member lookup, the call arguments (if we have any) will
160+
// generally be applied to the second level of parameters, with the member
161+
// lookup applying the curried self at the first level. But there are cases
162+
// where we can get an unapplied declaration reference back.
165163
auto hasAppliedSelf =
166164
decl->hasCurriedSelf() &&
167165
doesMemberRefApplyCurriedSelf(choice.getBaseType(), decl);
168166

169-
auto *fnType = decl->getInterfaceType()->castTo<AnyFunctionType>();
170-
if (hasAppliedSelf) {
171-
fnType = fnType->getResult()->getAs<AnyFunctionType>();
172-
assert(fnType && "Parameter list curry level does not match type");
167+
AnyFunctionType *fnType = nullptr;
168+
if (decl->hasParameterList()) {
169+
fnType = decl->getInterfaceType()->castTo<AnyFunctionType>();
170+
if (hasAppliedSelf) {
171+
fnType = fnType->getResult()->getAs<AnyFunctionType>();
172+
assert(fnType && "Parameter list curry level does not match type");
173+
}
174+
} else if (auto *VD = dyn_cast<VarDecl>(decl)) {
175+
// For variables, we can reject any type that we know cannot be callable.
176+
auto varTy = VD->getValueInterfaceType()->lookThroughAllOptionalTypes();
177+
if (!varTy->mayBeCallable(cs.DC))
178+
return false;
179+
fnType = varTy->getAs<AnyFunctionType>();
173180
}
174181

182+
// Given we want to be conservative with this checking, if there's any case
183+
// we can't match arguments for (e.g callable nominals, type parameters),
184+
// default to returning true.
185+
if (!fnType)
186+
return true;
187+
175188
auto params = fnType->getParams();
176189
ParameterListInfo paramInfo(params, decl, hasAppliedSelf);
177190

@@ -11014,7 +11027,7 @@ bool ConstraintSystem::simplifyAppliedOverloadsImpl(
1101411027

1101511028
auto labelsMatch = [&](MatchCallArgumentListener &listener) {
1101611029
if (areConservativelyCompatibleArgumentLabels(
11017-
choice, argsWithLabels, listener,
11030+
*this, choice, argsWithLabels, listener,
1101811031
argList->getFirstTrailingClosureIndex()))
1101911032
return true;
1102011033

test/ClangImporter/objc_parse.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ class IncompleteProtocolAdopter : Incomplete, IncompleteOptional { // expected-e
506506

507507
func testNullarySelectorPieces(_ obj: AnyObject) {
508508
obj.foo(1, bar: 2, 3) // no-warning
509-
obj.foo(1, 2, bar: 3) // expected-error{{cannot invoke 'foo' with an argument list of type '(Int, Int, bar: Int)'}}
509+
obj.foo(1, 2, bar: 3) // expected-error{{argument 'bar' must precede unnamed argument #2}}
510510
}
511511

512512
func testFactoryMethodAvailability() {

test/Constraints/diagnostics.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,9 +1303,14 @@ func f11(_ n: Int) {}
13031303
func f11<T : P2>(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{where 'T' = 'Int'}}
13041304
f11(3, f4) // expected-error {{global function 'f11' requires that 'Int' conform to 'P2'}}
13051305

1306-
let f12: (Int) -> Void = { _ in } // expected-note {{candidate '(Int) -> Void' requires 1 argument, but 2 were provided}}
1307-
func f12<T : P2>(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{candidate requires that 'Int' conform to 'P2' (requirement specified as 'T' : 'P2')}}
1308-
f12(3, f4)// expected-error {{no exact matches in call to global function 'f12'}}
1306+
let f12: (Int) -> Void = { _ in }
1307+
func f12<T : P2>(_ n: T, _ f: @escaping (T) -> T) {} // expected-note {{where 'T' = 'Int'}}
1308+
f12(3, f4)// expected-error {{global function 'f12' requires that 'Int' conform to 'P2'}}
1309+
1310+
// SR-15293: Bad diagnostic for var + func overload with mismatched call
1311+
func f13(x: Int, y: Int) {}
1312+
var f13: Any = 0
1313+
f13(0, x: 0) // expected-error {{incorrect argument labels in call (have '_:x:', expected 'x:y:')}}
13091314

13101315
// SR-12242
13111316
struct SR_12242_R<Value> {}

test/Constraints/generics.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,3 +965,15 @@ do {
965965
let g2: Gift<Sweets> = Gift<Chocolate>(box)
966966
// expected-error@-1 {{cannot assign value of type 'Gift<Chocolate>' to type 'Gift<Sweets>'}}
967967
}
968+
969+
func testOverloadGenericVarFn() {
970+
struct S<T> {
971+
var foo: T
972+
func foo(_ y: Int) {}
973+
init() { fatalError() }
974+
}
975+
// Make sure we can pick the variable overload over the function.
976+
S<(String) -> Void>().foo("")
977+
S<((String) -> Void)?>().foo?("")
978+
S<((String) -> Void)?>().foo!("")
979+
}

0 commit comments

Comments
 (0)