diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index d2dc0bb82618a..01a10ac6094f9 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -29,6 +29,7 @@ #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" +#include "swift/Sema/CSBindings.h" #include "swift/Sema/CSFix.h" #include "swift/Sema/CSTrail.h" #include "swift/Sema/Constraint.h" @@ -6127,6 +6128,10 @@ class TypeVariableBinding { return Binding.hasDefaultedLiteralProtocol(); } + bool fromSupertype() const { + return Binding.Kind == AllowedBindingKind::Subtypes; + } + bool attempt(ConstraintSystem &cs) const; /// Determine what fix (if any) needs to be introduced into a diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 3701f7f6c93a2..c4525c3b0cfe0 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2471,7 +2471,8 @@ isSubtypeOf(FunctionTypeRepresentation potentialSubRepr, static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1, FunctionType::ExtInfo einfo2, ConstraintKind kind, - ConstraintSystemOptions options) { + ConstraintSystemOptions options, + ConstraintLocatorBuilder locator) { auto rep1 = einfo1.getRepresentation(); auto rep2 = einfo2.getRepresentation(); bool clangTypeMismatch = @@ -2530,6 +2531,41 @@ static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1, if ((rep1 == rep2) && clangTypeMismatch) { return false; } + + // Narrow the conversion down to closures and declarations + // that are going to be further checked by CSApply and SILGen. + if (rep1 == FunctionTypeRepresentation::Swift && + rep2 == FunctionTypeRepresentation::CFunctionPointer) { + auto target = locator.trySimplifyToExpr(); + if (!target) + return false; + + target = target->getSemanticsProvidingExpr(); + + // Assignment is fine as look as the "source" is eligible. + if (auto *assignment = dyn_cast(target)) + target = assignment->getSrc(); + + // Let closures through because their eligibility depends on captures. + if (isa(target) || isa(target)) + return true; + + // Could be an eligible function, more checking would be performed + // at the later stage. + if (isa(target) || isa(target) || + isa(target)) + return true; + + // In all other cases conversion is invalid. For example: + // `let _: (@conversion(c) () -> Void)? = true ? { ... } : nil` + // + // Here ternary has two choices: (() -> ())? (based on closure) + // and (@conversion(c) () -> Void)? (based on the contextual type). + // The only reasonable deduction is the second one because `nil` + // cannot be _converted_ to `@convention(c)` function type. + return false; + } + return true; case ConstraintKind::BridgingConversion: @@ -3245,7 +3281,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, } if (!matchFunctionRepresentations(func1->getExtInfo(), func2->getExtInfo(), - kind, Options)) { + kind, Options, locator)) { return getTypeMatchFailure(locator); } diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index f09d3827a2f87..0a3de707723c7 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -583,6 +583,16 @@ class TypeVariableStep final : public BindingStep { if (CS.shouldAttemptFixes()) return false; + // For `TVO_PrefersSubtypeBinding` type variables bindings are + // sorted to make sure that non-subtype/equal bindings are attempted + // last, so if any subtype or equality bindings produced a solution, + // let's stop checking since supertype bindings are always concidered + // to be worse. + if (TypeVar->getImpl().prefersSubtypeBinding()) { + if (AnySolved) + return choice.fromSupertype(); + } + // If we were able to solve this without considering // default literals, don't bother looking at default literals. return AnySolved && choice.hasDefaultedProtocol() && diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index cbddf562b79ea..df05a2e4a96b9 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -36,16 +36,19 @@ #include "swift/Basic/Assertions.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Statistic.h" +#include "swift/Sema/CSBindings.h" #include "swift/Sema/CSFix.h" #include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Sema/PreparedOverload.h" #include "swift/Sema/SolutionResult.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Format.h" +#include #include using namespace swift; @@ -5398,6 +5401,17 @@ TypeVarBindingProducer::TypeVarBindingProducer( addBinding(binding); } + // `TVO_PrefersSubtypeBinding` type variables prefer bindings to subtypes + // and equal bindings. It's important to note that the binding inferred + // from a constraint like `Int conv $T` is marked as allowing supertypes + // and considered to be "subtype" bindings. + if (typeVar->getImpl().prefersSubtypeBinding()) { + std::stable_partition(Bindings.begin(), Bindings.end(), + [](const Binding &binding) { + return binding.Kind != AllowedBindingKind::Subtypes; + }); + } + // Infer defaults based on "uncovered" literal protocol requirements. for (const auto &literal : bindings.Literals) { if (!literal.viableAsBinding()) diff --git a/test/Concurrency/sendable_keypaths.swift b/test/Concurrency/sendable_keypaths.swift index 6db444cbbe0ed..cbfa6cfa9d216 100644 --- a/test/Concurrency/sendable_keypaths.swift +++ b/test/Concurrency/sendable_keypaths.swift @@ -222,14 +222,12 @@ do { fatalError() } - // TODO(rdar://125948508): This shouldn't be ambiguous (@Sendable version should be preferred) func test() -> KeyPath { - true ? kp() : kp() // expected-error {{failed to produce diagnostic for expression}} + true ? kp() : kp() // Ok } func forward(_ v: T) -> T { v } - // TODO(rdar://125948508): This shouldn't be ambiguous (@Sendable version should be preferred) - let _: KeyPath = forward(kp()) // expected-error {{conflicting arguments to generic parameter 'T' ('any KeyPath & Sendable' vs. 'KeyPath')}} + let _: KeyPath = forward(kp()) // Ok } do { @@ -247,16 +245,14 @@ do { static func otherFn() {} } - // TODO(rdar://125948508): This shouldn't be ambiguous (@Sendable version should be preferred) func fnRet(cond: Bool) -> () -> Void { - cond ? Test.fn : Test.otherFn // expected-error {{failed to produce diagnostic for expression}} + cond ? Test.fn : Test.otherFn // Ok } func forward(_: T) -> T { } - // TODO(rdar://125948508): This shouldn't be ambiguous (@Sendable version should be preferred) - let _: () -> Void = forward(Test.fn) // expected-error {{conflicting arguments to generic parameter 'T' ('@Sendable () -> ()' vs. '() -> Void')}} + let _: () -> Void = forward(Test.fn) // Ok } // https://github.com/swiftlang/swift/issues/77105 diff --git a/test/Concurrency/sendable_methods.swift b/test/Concurrency/sendable_methods.swift index 75038c79da216..90825a9bcca2c 100644 --- a/test/Concurrency/sendable_methods.swift +++ b/test/Concurrency/sendable_methods.swift @@ -331,4 +331,21 @@ do { static func ff() {} } -} \ No newline at end of file +} + +// Ambiguity between `@Sendable` method and non-Sendable context injected into an Optional. +do { + struct Test { + func action() -> Void {} + + func onAction(_: (() -> Void)?) {} + + func test() { + onAction(true ? action : nil) // Ok + } + + func test(fn1: (@Sendable () -> Void)?, fn2: @escaping () -> Void) { + let _: () -> Void = fn1 ?? fn2 // Ok + } + } +} diff --git a/test/Generics/deduction.swift b/test/Generics/deduction.swift index 7b35f6cbe3ab5..42c567627baf1 100644 --- a/test/Generics/deduction.swift +++ b/test/Generics/deduction.swift @@ -77,7 +77,7 @@ func acceptFunction(_ f: (T) -> U, _ t: T, _ u: U) {} func passFunction(_ f: (Int) -> Float, x: Int, y: Float) { acceptFunction(f, x, y) - acceptFunction(f, y, y) // expected-error{{cannot convert value of type 'Float' to expected argument type 'Int'}} + acceptFunction(f, y, y) // expected-error{{conflicting arguments to generic parameter 'T' ('Float' vs. 'Int')}} } func returnTuple(_: T) -> (T, U) { } // expected-note {{in call to function 'returnTuple'}} diff --git a/test/SILOptimizer/sil_combine1.swift b/test/SILOptimizer/sil_combine1.swift index aad1d654fa4e6..fe358b245c6d7 100644 --- a/test/SILOptimizer/sil_combine1.swift +++ b/test/SILOptimizer/sil_combine1.swift @@ -24,10 +24,11 @@ func compose(_ x: P, _ y: P, _ z: P) -> Int32 { return x.val() + y.val() + z.val() } -//CHECK-LABEL: sil [noinline] @$s12sil_combine120test_compose_closures5Int32VyF : $@convention(thin) () -> Int32 { -//CHECK: [[OEADDR:%.*]] = open_existential_addr immutable_access {{%.*}} : $*any P to $*@opened -//CHECK: [[ADDRCAST:%.*]] = unchecked_addr_cast [[OEADDR]] : $*@opened -//CHECK: struct_element_addr [[ADDRCAST]] : $*CP, #CP.v +// CHECK-LABEL: sil [noinline] @$s12sil_combine120test_compose_closures5Int32VyF : $@convention(thin) () -> Int32 { +// CHECK: [[RESULT:%.*]] = integer_literal $Builtin.Int32, 6 +// CHECK: [[INT32_RESULT:%.*]] = struct $Int32 ([[RESULT]] : $Builtin.Int32) +// CHECK: return [[INT32_RESULT]] : $Int32 +// CHECK: } // end sil function '$s12sil_combine120test_compose_closures5Int32VyF' @inline(never) public func test_compose_closure() -> Int32 { let insult = curry(compose)(CP(1))(CP(2)) diff --git a/test/expr/closure/closures2.swift b/test/expr/closure/closures2.swift index 63b2400102b79..a8f93ecb333e0 100644 --- a/test/expr/closure/closures2.swift +++ b/test/expr/closure/closures2.swift @@ -12,8 +12,7 @@ do { let _i = Mootex() var i: Int { - // expected-error@+1:10 {{failed to produce diagnostic for expression}} - _i.withLock { _ in + _i.withLock { _ in // OK (prefers Never for the result) never() } } diff --git a/test/type/subclass_composition.swift b/test/type/subclass_composition.swift index c34fb9c6ce5ef..66e4e7555e622 100644 --- a/test/type/subclass_composition.swift +++ b/test/type/subclass_composition.swift @@ -4,7 +4,7 @@ protocol P1 { typealias DependentInConcreteConformance = Self } -class Base : P1 { // expected-note {{arguments to generic parameter 'T' ('String' and 'Int') are expected to be equal}} +class Base : P1 { typealias DependentClass = T required init(classInit: ()) {} @@ -333,7 +333,8 @@ func conformsToP2(_: T) {} func conformsToBaseIntAndP2 & P2>(_: T) {} // expected-note@-1 {{where 'T' = 'FakeDerived'}} // expected-note@-2 {{where 'T' = 'T1'}} -// expected-note@-3 2 {{where 'T' = 'Base'}} +// expected-note@-3 2 {{where 'T' = 'Base'}} +// expected-note@-4 {{where 'T' = 'Base'}} func conformsToBaseIntAndP2WithWhereClause(_: T) where T : Base & P2 {} // expected-note@-1 {{where 'T' = 'FakeDerived'}} @@ -450,8 +451,8 @@ func conformsTo & P2>( // expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'Base' conform to 'P2'}} conformsToBaseIntAndP2(badBase) - // expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'Base' conform to 'P2'}} - // expected-error@-2 {{cannot convert value of type 'Base' to expected argument type 'Base'}} + // expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'Base' conform to 'P2'}} + // expected-error@-2 {{global function 'conformsToBaseIntAndP2' requires that 'Base' inherit from 'Base'}} conformsToBaseIntAndP2(fakeDerived) // expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'FakeDerived' inherit from 'Base'}} @@ -583,4 +584,4 @@ protocol P5 where Self: Other {} protocol P6 {} func invalidOverload(_: P5 & P6 & Other) {} // expected-note {{'invalidOverload' previously declared here}} -func invalidOverload(_: P5 & P6) {} // expected-error {{invalid redeclaration of 'invalidOverload'}} \ No newline at end of file +func invalidOverload(_: P5 & P6) {} // expected-error {{invalid redeclaration of 'invalidOverload'}}