Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
40 changes: 38 additions & 2 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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<AssignExpr>(target))
target = assignment->getSrc();

// Let closures through because their eligibility depends on captures.
if (isa<CaptureListExpr>(target) || isa<ClosureExpr>(target))
return true;

// Could be an eligible function, more checking would be performed
// at the later stage.
if (isa<DeclRefExpr>(target) || isa<OverloadedDeclRefExpr>(target) ||
isa<UnresolvedDotExpr>(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:
Expand Down Expand Up @@ -3245,7 +3281,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
}

if (!matchFunctionRepresentations(func1->getExtInfo(), func2->getExtInfo(),
kind, Options)) {
kind, Options, locator)) {
return getTypeMatchFailure(locator);
}

Expand Down
10 changes: 10 additions & 0 deletions lib/Sema/CSStep.h
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,16 @@ class TypeVariableStep final : public BindingStep<TypeVarBindingProducer> {
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() &&
Expand Down
14 changes: 14 additions & 0 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <algorithm>
#include <cmath>

using namespace swift;
Expand Down Expand Up @@ -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())
Expand Down
12 changes: 4 additions & 8 deletions test/Concurrency/sendable_keypaths.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,12 @@ do {
fatalError()
}

// TODO(rdar://125948508): This shouldn't be ambiguous (@Sendable version should be preferred)
func test() -> KeyPath<String, Int> {
true ? kp() : kp() // expected-error {{failed to produce diagnostic for expression}}
true ? kp() : kp() // Ok
}

func forward<T>(_ v: T) -> T { v }
// TODO(rdar://125948508): This shouldn't be ambiguous (@Sendable version should be preferred)
let _: KeyPath<String, Int> = forward(kp()) // expected-error {{conflicting arguments to generic parameter 'T' ('any KeyPath<String, Int> & Sendable' vs. 'KeyPath<String, Int>')}}
let _: KeyPath<String, Int> = forward(kp()) // Ok
}

do {
Expand All @@ -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) -> 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
Expand Down
19 changes: 18 additions & 1 deletion test/Concurrency/sendable_methods.swift
Original file line number Diff line number Diff line change
Expand Up @@ -331,4 +331,21 @@ do {

static func ff() {}
}
}
}

// 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
}
}
}
2 changes: 1 addition & 1 deletion test/Generics/deduction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func acceptFunction<T, U>(_ 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, U>(_: T) -> (T, U) { } // expected-note {{in call to function 'returnTuple'}}
Expand Down
9 changes: 5 additions & 4 deletions test/SILOptimizer/sil_combine1.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
3 changes: 1 addition & 2 deletions test/expr/closure/closures2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ do {
let _i = Mootex<Int>()

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()
}
}
Expand Down
11 changes: 6 additions & 5 deletions test/type/subclass_composition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ protocol P1 {
typealias DependentInConcreteConformance = Self
}

class Base<T> : P1 { // expected-note {{arguments to generic parameter 'T' ('String' and 'Int') are expected to be equal}}
class Base<T> : P1 {
typealias DependentClass = T

required init(classInit: ()) {}
Expand Down Expand Up @@ -333,7 +333,8 @@ func conformsToP2<T : P2>(_: T) {}
func conformsToBaseIntAndP2<T : Base<Int> & P2>(_: T) {}
// expected-note@-1 {{where 'T' = 'FakeDerived'}}
// expected-note@-2 {{where 'T' = 'T1'}}
// expected-note@-3 2 {{where 'T' = 'Base<Int>'}}
// expected-note@-3 2 {{where 'T' = 'Base<String>'}}
// expected-note@-4 {{where 'T' = 'Base<Int>'}}

func conformsToBaseIntAndP2WithWhereClause<T>(_: T) where T : Base<Int> & P2 {}
// expected-note@-1 {{where 'T' = 'FakeDerived'}}
Expand Down Expand Up @@ -450,8 +451,8 @@ func conformsTo<T1 : P2, T2 : Base<Int> & P2>(
// expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'Base<Int>' conform to 'P2'}}

conformsToBaseIntAndP2(badBase)
// expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'Base<Int>' conform to 'P2'}}
// expected-error@-2 {{cannot convert value of type 'Base<String>' to expected argument type 'Base<Int>'}}
// expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'Base<String>' conform to 'P2'}}
// expected-error@-2 {{global function 'conformsToBaseIntAndP2' requires that 'Base<String>' inherit from 'Base<Int>'}}

conformsToBaseIntAndP2(fakeDerived)
// expected-error@-1 {{global function 'conformsToBaseIntAndP2' requires that 'FakeDerived' inherit from 'Base<Int>'}}
Expand Down Expand Up @@ -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'}}
func invalidOverload(_: P5 & P6) {} // expected-error {{invalid redeclaration of 'invalidOverload'}}