From 8f766f7c8fa0de3cb04c37a382afaf9a4de4b3d2 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 9 May 2022 18:12:52 -0700 Subject: [PATCH 1/2] [CSSimplify] Avoid filtering `init` overloads of a callable type If there is a call to `init` on a callable type that has a trailing closure, let's avoid filtering because it's impossible to tell whether a trailing closure belongs to an `init` call or implicit `.callAsFunction` that would be attempted after it. Resolves: rdar://92912878 (cherry picked from commit d81007cf08214c500c8aa6253c93a0d3bb872e67) (cherry picked from commit 4955e5179386baad74d472f0e799283c62e85695) --- lib/Sema/CSSimplify.cpp | 18 ++++++++++++++++++ test/Constraints/callAsFunction.swift | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 8d5dc905c51d4..662e146f3129b 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -10926,6 +10926,24 @@ bool ConstraintSystem::simplifyAppliedOverloadsImpl( auto *argList = getArgumentList(getConstraintLocator(locator)); + // If argument list has trailing closures and this is `init` call to + // a callable type, let's not filter anything since there is a possibility + // that it needs an implicit `.callAsFunction` to work. + if (argList && argList->hasAnyTrailingClosures()) { + if (disjunction->getLocator() + ->isLastElement()) { + auto choice = disjunction->getNestedConstraints()[0]->getOverloadChoice(); + if (auto *decl = choice.getDeclOrNull()) { + auto *dc = decl->getDeclContext(); + if (auto *parent = dc->getSelfNominalTypeDecl()) { + auto type = parent->getDeclaredInterfaceType(); + if (type->isCallableNominalType(DC)) + return false; + } + } + } + } + // Consider each of the constraints in the disjunction. retry_after_fail: bool hasUnhandledConstraints = false; diff --git a/test/Constraints/callAsFunction.swift b/test/Constraints/callAsFunction.swift index e66e5b1a31098..948630d65ffd5 100644 --- a/test/Constraints/callAsFunction.swift +++ b/test/Constraints/callAsFunction.swift @@ -29,3 +29,25 @@ struct Test { } } } + +// rdar://92912878 - filtering prevents disambiguation of `.callAsFunction` +func test_no_filtering_of_overloads() { + struct S { + init() {} + init(_: String) {} + + func callAsFunction(_ fn: () -> T) -> T { + fn() + } + } + + func test(_: () -> Void) { + } + + test { + _ = S() { // Ok + _ = 42 + print("Hello") + } + } +} From 4cbd53d406a6984ebf69bd10440288a5780c9932 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 10 May 2022 10:04:06 -0700 Subject: [PATCH 2/2] [ConstraintSystem] Adjust `getCalleeLocator` to handle implicit `callAsFunction` A call to `.callAsFunction` could be injected after initializer if the type is callable, `getCalleeLocator` should recognize that situation and return appropriate locator otherwise if `callAsFunction` has e.g. a result builder attached to one of its parameters the solver wouldn't be able to find it. Resolves: rdar://92914226 (cherry picked from commit f7c4ff68472392af542d1a01feda49b703874a03) (cherry picked from commit 557d83cfc0aefb982e0d62698fe7733196288dd6) --- lib/Sema/ConstraintSystem.cpp | 7 +++++++ test/Constraints/result_builder.swift | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 5be1dbfd496fa..9a5ba0a2baa1e 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -557,6 +557,13 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( } if (auto *UDE = getAsExpr(anchor)) { + if (UDE->isImplicit() && + UDE->getName().getBaseName() == Context.Id_callAsFunction) { + return getConstraintLocator(anchor, + {LocatorPathElt::ApplyFunction(), + LocatorPathElt::ImplicitCallAsFunction()}); + } + return getConstraintLocator( anchor, TypeChecker::getSelfForInitDelegationInConstructor(DC, UDE) ? ConstraintLocator::ConstructorMember diff --git a/test/Constraints/result_builder.swift b/test/Constraints/result_builder.swift index b08c64a2db321..17906dfe2906b 100644 --- a/test/Constraints/result_builder.swift +++ b/test/Constraints/result_builder.swift @@ -1174,3 +1174,27 @@ let list3 = list { } print(list3) // CHECK: (cons "4" (cons (cons "3" (cons 2.0 nil)) (cons 1 nil))) + +func test_callAsFunction_with_resultBuilder() { + struct CallableTest { + func callAsFunction(@TupleBuilder _ body: (Bool) -> T) { + print(body(true)) + } + } + + CallableTest() { + 0 + "with parens" + $0 + } + + CallableTest { + 1 + "without parens" + $0 + } +} + +test_callAsFunction_with_resultBuilder() +// CHECK: (0, "with parens", true) +// CHECK: (1, "without parens", true)