diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 9830c66dbe91e..19355581b4f92 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -746,6 +746,29 @@ bool CompareDeclSpecializationRequest::evaluate( unsigned numParams1 = params1.size(); unsigned numParams2 = params2.size(); + // Handle the following situation: + // + // struct S { + // func test() {} + // static func test(_: S) {} + // } + // + // Calling `S.test(s)` where `s` has a type `S` without any other context + // should prefer a complete call to a static member over a partial + // application of an instance once based on the choice of the base type. + // + // The behavior is consistent for double-applies as well i.e. + // `S.test(s)()` if static method produced a function type it would be + // preferred. + if (decl1->isInstanceMember() != decl2->isInstanceMember() && + isa(decl1) && isa(decl2)) { + auto selfTy = decl1->isInstanceMember() ? selfTy2 : selfTy1; + auto params = decl1->isInstanceMember() ? params2 : params1; + if (params.size() == 1 && params[0].getPlainType()->isEqual(selfTy)) { + return completeResult(!decl1->isInstanceMember()); + } + } + if (numParams1 > numParams2) return completeResult(false); diff --git a/test/Constraints/old_hack_related_ambiguities.swift b/test/Constraints/old_hack_related_ambiguities.swift index d69ec110ac88b..fb1be16628093 100644 --- a/test/Constraints/old_hack_related_ambiguities.swift +++ b/test/Constraints/old_hack_related_ambiguities.swift @@ -414,3 +414,61 @@ do { } } } + +do { + struct S { + func test() -> Int { 42 } + static func test(_: S...) {} + + func doubleApply() {} + static func doubleApply(_: S) -> () -> Int { { 42 } } + } + + func test(s: S) { + let res1 = S.test(s) + // expected-warning@-1 {{constant 'res1' inferred to have type '()', which may be unexpected}} + // expected-note@-2 {{add an explicit type annotation to silence this warning}} + _ = res1 + + let useInstance = S.test(s)() + let _: Int = useInstance + + let res2 = { + S.test(s) + } + let _: () -> Void = res2 + + let _ = { () async -> Void in + _ = 42 + return S.test(s) + } + + let res3 = S.doubleApply(s) + let _: () -> Int = res3 + + let res4 = S.doubleApply(s)() + let _: Int = res4 + + let res5 = { S.doubleApply(s)() } + let _: () -> Int = res5 + + let res6 = { + _ = 42 + return S.doubleApply(s) + } + let _: () -> Int = res6() + } + + func testAsyncContext(s: S) async { + let res1 = S.test(s) + // expected-warning@-1 {{constant 'res1' inferred to have type '()', which may be unexpected}} + // expected-note@-2 {{add an explicit type annotation to silence this warning}} + _ = res1 + + let res2 = S.doubleApply(s) + let _: () -> Int = res2 + + let res3 = S.doubleApply(s)() + let _: Int = res3 + } +}