Skip to content

Commit 6ca3d3f

Browse files
authored
Merge pull request #13138 from xedin/rdar-35702810
[CSSolver/SILGen] Fix solver to support function conversion with collection subtyping
2 parents 504c2df + 515520e commit 6ca3d3f

File tree

4 files changed

+141
-3
lines changed

4 files changed

+141
-3
lines changed

lib/SILGen/SILGenPoly.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ ManagedValue Transform::transform(ManagedValue v,
467467

468468
// Subtype conversions:
469469

470-
// - upcasts
470+
// - upcasts for classes
471471
if (outputSubstType->getClassOrBoundGenericClass() &&
472472
inputSubstType->getClassOrBoundGenericClass()) {
473473
auto class1 = inputSubstType->getClassOrBoundGenericClass();
@@ -490,6 +490,32 @@ ManagedValue Transform::transform(ManagedValue v,
490490
}
491491
}
492492

493+
// - upcasts for collections
494+
if (outputSubstType->getStructOrBoundGenericStruct() &&
495+
inputSubstType->getStructOrBoundGenericStruct()) {
496+
auto *inputStruct = inputSubstType->getStructOrBoundGenericStruct();
497+
auto *outputStruct = outputSubstType->getStructOrBoundGenericStruct();
498+
499+
// Attempt collection upcast only if input and output declarations match.
500+
if (inputStruct == outputStruct) {
501+
FuncDecl *fn = nullptr;
502+
auto &ctx = SGF.getASTContext();
503+
if (inputStruct == ctx.getArrayDecl()) {
504+
fn = SGF.SGM.getArrayForceCast(Loc);
505+
} else if (inputStruct == ctx.getDictionaryDecl()) {
506+
fn = SGF.SGM.getDictionaryUpCast(Loc);
507+
} else if (inputStruct == ctx.getSetDecl()) {
508+
fn = SGF.SGM.getSetUpCast(Loc);
509+
} else {
510+
llvm_unreachable("unsupported collection upcast kind");
511+
}
512+
513+
return SGF.emitCollectionConversion(Loc, fn, inputSubstType,
514+
outputSubstType, v, ctxt)
515+
.getScalarValue();
516+
}
517+
}
518+
493519
// - upcasts from an archetype
494520
if (outputSubstType->getClassOrBoundGenericClass()) {
495521
if (auto archetypeType = dyn_cast<ArchetypeType>(inputSubstType)) {

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,8 +2090,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
20902090
}
20912091

20922092
// Special implicit nominal conversions.
2093-
if (!type1->is<LValueType>() &&
2094-
kind >= ConstraintKind::Conversion) {
2093+
if (!type1->is<LValueType>() && kind >= ConstraintKind::Subtype) {
20952094
// Array -> Array.
20962095
if (isArrayType(desugar1) && isArrayType(desugar2)) {
20972096
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");

test/Interpreter/FunctionConversion.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,61 @@ FunctionConversionTestSuite.test("ThrowVariance") {
241241
do { try print(g()) } catch {}
242242
}
243243

244+
class A: Quilt {
245+
var n: Int8 {
246+
return 42
247+
}
248+
}
249+
250+
func rdar35702810_arr<T: Quilt>(type: T.Type, _ fn: ([T]?) -> Int8) -> Int8 {
251+
let x: [T] = [A() as! T]
252+
return fn(x)
253+
}
254+
255+
func rdar35702810_map<T: Quilt>(type: T.Type, _ fn: ([String: T]) -> Int8) -> Int8 {
256+
let x: [String: T] = ["ultimate question": A() as! T]
257+
return fn(x)
258+
}
259+
260+
FunctionConversionTestSuite.test("CollectionUpCastsInFuncParameters") {
261+
let fn_arr: ([Quilt]?) -> Int8 = { v in v![0].n }
262+
let fn_map: ([String: Quilt]) -> Int8 = { v in v["ultimate question"]!.n }
263+
264+
expectEqual(rdar35702810_arr(type: A.self, fn_arr), 42)
265+
expectEqual(rdar35702810_map(type: A.self, fn_map), 42)
266+
}
267+
268+
protocol X: Hashable {}
269+
class B: X {
270+
var hashValue: Int { return 42 }
271+
static func == (lhs: B, rhs: B) -> Bool {
272+
return lhs.hashValue == rhs.hashValue
273+
}
274+
}
275+
276+
func rdar35702810_arr_hashable<T: X>(type: T.Type, _ fn: ([T]?) -> Int) -> Int {
277+
let x: [T] = [B() as! T]
278+
return fn(x)
279+
}
280+
281+
func rdar35702810_map_hashable<T: X>(type: T.Type, _ fn: ([String: T]) -> Int) -> Int {
282+
let x: [String: T] = ["ultimate question": B() as! T]
283+
return fn(x)
284+
}
285+
286+
func rdar35702810_set_hashable<T: X>(type: T.Type, _ fn: (Set<T>) -> Int) -> Int {
287+
let x: Set<T> = [B() as! T]
288+
return fn(x)
289+
}
290+
291+
FunctionConversionTestSuite.test("CollectionUpCastsWithHashableInFuncParameters") {
292+
let fn_arr: ([AnyHashable]?) -> Int = { v in v![0].hashValue }
293+
let fn_map: ([String: AnyHashable]) -> Int = { v in v["ultimate question"]!.hashValue }
294+
let fn_set: (Set<AnyHashable>) -> Int = { v in v.first!.hashValue }
295+
296+
expectEqual(rdar35702810_arr_hashable(type: B.self, fn_arr), 42)
297+
expectEqual(rdar35702810_map_hashable(type: B.self, fn_map), 42)
298+
expectEqual(rdar35702810_set_hashable(type: B.self, fn_set), 42)
299+
}
300+
244301
runAllTests()

test/SILGen/function_conversion.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,3 +598,59 @@ func convTupleAny(_ f1: @escaping () -> (),
598598
// CHECK-NEXT: dealloc_stack [[OPTIONAL_VALUE]]
599599
// CHECK-NEXT: dealloc_stack [[ANY_VALUE]]
600600
// CHECK-NEXT: return
601+
602+
// ==== Support collection subtyping in function argument position
603+
604+
protocol Z {}
605+
class A: Z {}
606+
607+
func foo_arr<T: Z>(type: T.Type, _ fn: ([T]?) -> Void) {}
608+
func foo_map<T: Z>(type: T.Type, _ fn: ([Int: T]) -> Void) {}
609+
610+
func rdar35702810() {
611+
let fn_arr: ([Z]?) -> Void = { _ in }
612+
let fn_map: ([Int: Z]) -> Void = { _ in }
613+
614+
// CHECK: function_ref @_T0s15_arrayForceCastSayq_GSayxGr0_lF : $@convention(thin) <τ_0_0, τ_0_1> (@owned Array<τ_0_0>) -> @owned Array<τ_0_1>
615+
// CHECK: apply %4<A, Z>(%3) : $@convention(thin) <τ_0_0, τ_0_1> (@owned Array<τ_0_0>) -> @owned Array<τ_0_1>
616+
foo_arr(type: A.self, fn_arr)
617+
618+
// CHECK: function_ref @_T0s17_dictionaryUpCasts10DictionaryVyq0_q1_GACyxq_Gs8HashableRzsAFR0_r2_lF : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_0 : Hashable, τ_0_2 : Hashable> (@owned Dictionary<τ_0_0, τ_0_1>) -> @owned Dictionary<τ_0_2, τ_0_3>
619+
// CHECK: apply %2<Int, A, Int, Z>(%0) : $@convention(thin) <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_0 : Hashable, τ_0_2 : Hashable> (@owned Dictionary<τ_0_0, τ_0_1>) -> @owned Dictionary<τ_0_2, τ_0_3>
620+
// CHECK: apply %1(%3) : $@callee_guaranteed (@owned Dictionary<Int, Z>) -> ()
621+
foo_map(type: A.self, fn_map)
622+
}
623+
624+
protocol X: Hashable {}
625+
class B: X {
626+
var hashValue: Int { return 42 }
627+
static func == (lhs: B, rhs: B) -> Bool {
628+
return lhs.hashValue == rhs.hashValue
629+
}
630+
}
631+
632+
func bar_arr<T: X>(type: T.Type, _ fn: ([T]?) -> Void) {}
633+
func bar_map<T: X>(type: T.Type, _ fn: ([T: Int]) -> Void) {}
634+
func bar_set<T: X>(type: T.Type, _ fn: (Set<T>) -> Void) {}
635+
636+
func rdar35702810_anyhashable() {
637+
let fn_arr: ([AnyHashable]?) -> Void = { _ in }
638+
let fn_map: ([AnyHashable: Int]) -> Void = { _ in }
639+
let fn_set: (Set<AnyHashable>) -> Void = { _ in }
640+
641+
642+
// CHECK: function_ref @_T0Says11AnyHashableVGSgIegx_Say19function_conversion1BCGSgIgx_TR : $@convention(thin) (@owned Optional<Array<B>>, @guaranteed @callee_guaranteed (@owned Optional<Array<AnyHashable>>) -> ()) -> ()
643+
// CHECK: partial_apply [callee_guaranteed] %12(%11) : $@convention(thin) (@owned Optional<Array<B>>, @guaranteed @callee_guaranteed (@owned Optional<Array<AnyHashable>>) -> ()) -> ()
644+
// CHECK: convert_function %13 : $@callee_guaranteed (@owned Optional<Array<B>>) -> () to $@noescape @callee_guaranteed (@owned Optional<Array<B>>) -> ()
645+
bar_arr(type: B.self, fn_arr)
646+
647+
// CHECK: function_ref @_T0s10DictionaryVys11AnyHashableVSiGIegx_ABy19function_conversion1BCSiGIgx_TR : $@convention(thin) (@owned Dictionary<B, Int>, @guaranteed @callee_guaranteed (@owned Dictionary<AnyHashable, Int>) -> ()) -> ()
648+
// CHECK: partial_apply [callee_guaranteed] %21(%20) : $@convention(thin) (@owned Dictionary<B, Int>, @guaranteed @callee_guaranteed (@owned Dictionary<AnyHashable, Int>) -> ()) -> ()
649+
// CHECK: convert_function %22 : $@callee_guaranteed (@owned Dictionary<B, Int>) -> () to $@noescape @callee_guaranteed (@owned Dictionary<B, Int>) -> ()
650+
bar_map(type: B.self, fn_map)
651+
652+
// CHECK: function_ref @_T0s3SetVys11AnyHashableVGIegx_ABy19function_conversion1BCGIgx_TR : $@convention(thin) (@owned Set<B>, @guaranteed @callee_guaranteed (@owned Set<AnyHashable>) -> ()) -> ()
653+
// CHECK: partial_apply [callee_guaranteed] %30(%29) : $@convention(thin) (@owned Set<B>, @guaranteed @callee_guaranteed (@owned Set<AnyHashable>) -> ()) -> ()
654+
// CHECK: convert_function %31 : $@callee_guaranteed (@owned Set<B>) -> () to $@noescape @callee_guaranteed (@owned Set<B>) -> ()
655+
bar_set(type: B.self, fn_set)
656+
}

0 commit comments

Comments
 (0)