From bc949c3680521eaaf6200693064239d70d3beca4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 7 Nov 2024 13:08:57 -0800 Subject: [PATCH 1/2] [CSBindings] Don't favor application result types before application happens Until `ApplicableFunction` constraint is simplified result type associated with it cannot be bound because the binding set if incomplete. Resolves: rdar://139237088 --- include/swift/Sema/ConstraintSystem.h | 4 ++++ lib/Sema/CSBindings.cpp | 13 ++++++++++--- lib/Sema/TypeCheckConstraints.cpp | 10 ++++++++++ test/Constraints/async.swift | 17 +++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 4257be52b4ce1..3bd70ea0e5655 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -486,6 +486,10 @@ class TypeVariableType::Implementation { /// Determine whether this type variable represents a subscript result type. bool isSubscriptResultType() const; + /// Determine whether this type variable represents a result type of an + /// application i.e. a call, an operator, or a subscript. + bool isApplicationResultType() const; + /// Determine whether this type variable represents an opened /// type parameter pack. bool isParameterPack() const; diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 5605600f4e425..30f3d7b346570 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1293,9 +1293,16 @@ bool BindingSet::favoredOverDisjunction(Constraint *disjunction) const { return !hasConversions(binding.BindingType); })) { - // Result type of subscript could be l-value so we can't bind it early. - if (!TypeVar->getImpl().isSubscriptResultType() && - llvm::none_of(Info.DelayedBy, [](const Constraint *constraint) { + bool isApplicationResultType = TypeVar->getImpl().isApplicationResultType(); + if (llvm::none_of(Info.DelayedBy, [&isApplicationResultType]( + const Constraint *constraint) { + // Let's not attempt to bind result type before application + // happens. For example because it could be discardable or + // l-value (subscript applications). + if (isApplicationResultType && + constraint->getKind() == ConstraintKind::ApplicableFunction) + return true; + return constraint->getKind() == ConstraintKind::Disjunction || constraint->getKind() == ConstraintKind::ValueMember; })) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index f2524ef717f08..f9868b994aed8 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -175,6 +175,16 @@ bool TypeVariableType::Implementation::isSubscriptResultType() const { KeyPathExpr::Component::Kind::UnresolvedSubscript; } +bool TypeVariableType::Implementation::isApplicationResultType() const { + if (!(locator && locator->getAnchor())) + return false; + + if (!locator->isLastElement()) + return false; + + return isExpr(locator->getAnchor()) || isSubscriptResultType(); +} + bool TypeVariableType::Implementation::isParameterPack() const { return locator && locator->isForGenericParameter() diff --git a/test/Constraints/async.swift b/test/Constraints/async.swift index 732fd3b5f829d..f914d49b56372 100644 --- a/test/Constraints/async.swift +++ b/test/Constraints/async.swift @@ -180,3 +180,20 @@ struct OverloadInImplicitAsyncClosure { init(int: Int) throws { } } + +@available(SwiftStdlib 5.5, *) +func test(_: Int) async throws {} + +@discardableResult +@available(SwiftStdlib 5.5, *) +func test(_: Int) -> String { "" } + +@available(SwiftStdlib 5.5, *) +func compute(_: @escaping () -> Void) {} + +@available(SwiftStdlib 5.5, *) +func test_sync_in_closure_context() { + compute { + test(42) // Ok (select sync overloads and discards the result) + } +} From bdabde7f1ad0d8163b4d93859fc42dd6d47a3420 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 8 Nov 2024 13:59:52 -0800 Subject: [PATCH 2/2] [Tests] NFC: Add a test-case for rdar://139238255 --- test/Constraints/closures.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index c204f7c131ccd..7c2ce68f9ed65 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -1267,3 +1267,19 @@ do { // Currently legal. let _: () -> Int = { return fatalError() } + +// Make sure that `Void` assigned to closure result doesn't get eagerly propagated into the body +do { + class C { + func f(_: Any) -> Int! { fatalError() } + static func f(_: Any) -> Int! { fatalError() } + } + + class G { + func g(_ u: U, _: (U, T) -> ()) {} + + func g(_ u: U) { + g(u) { $0.f($1) } // expected-warning {{result of call to 'f' is unused}} + } + } +}