Skip to content

Commit 6244a6b

Browse files
committed
GSB: Teach 'derived via concrete' computation about superclass constraints
Under certain circumstances, introducing a concrete same-type or superclass constraint can re-introduce conformance constraints which were previously redundant. For example, consider this code, which we correctly support today: protocol P { associatedtype T : Q } protocol Q {} class SomeClass<U : Q> {} struct Outer<T> where T : P { func inner<U>(_: U) where T == SomeClass<U>, U : Q {} } The constraint 'T == SomeClass<U>' makes the outer constraint `T : P' redundant, because SomeClass already conforms to P. It also introduces an implied same-type constraint 'U == T.T'. However, whereas 'T : P' together with 'U == T.T' make 'U : Q' redundant, the introduction of the constraint 'T == SomeClass<U>' removes 'T : P', so we re-introduce an explicit constraint 'U : Q' in order to get a valid generic signature. This code path did the right thing for constraints derived via concrete same-type constraints, but it did not handle superclass constraints. As a result, this case was broken: struct Outer<T> where T : P { func inner<U>(_: U) where T : SomeClass<U>, U : Q {} } This is the same example as above, except T is related via a superclass constraint to SomeClass<U>, instead of via a concrete same-type constraint. The subtlety here is that we must check if the superclass type actually conforms to the requirement source's protocol, because it is possible to have a superclass-constrained generic parameter where some conformances are abstract. Eg, if SomeClass did not conform to another protocol P2, we could write func foo<T, U>(_: T, _: U) where T : SomeClass<U>, T : P2 {} In this case, 'T : P2' is an abstract conformance on the type 'T'. The common case where this would come up in real code is when you have a class that conforms to a protocol with an associated type, and one of the protocol requirements was fulfilled by a default in a protocol extension, eg: protocol P { associatedtype T : Q func foo() } extension P { func foo() {} } class ConformsWithDefault<T : Q> : P {} The above used to crash; now it will type-check correctly. Fixes <rdar://problem/44736411>, <https://bugs.swift.org/browse/SR-8814>..
1 parent c93c434 commit 6244a6b

File tree

4 files changed

+79
-3
lines changed

4 files changed

+79
-3
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,11 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource(
837837

838838
if (parentEquivClass->concreteType)
839839
derivedViaConcrete = true;
840+
else if (parentEquivClass->superclass &&
841+
builder.lookupConformance(parentType->getCanonicalType(),
842+
parentEquivClass->superclass,
843+
source->getProtocolDecl()))
844+
derivedViaConcrete = true;
840845

841846
// The parent potential archetype must conform to the protocol in which
842847
// this requirement resides. Add this constraint.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol P {
4+
associatedtype T : Q
5+
}
6+
7+
protocol Q {
8+
associatedtype T : R
9+
10+
var t: T { get }
11+
}
12+
13+
protocol R {}
14+
15+
func takesR<T : R>(_: T) {}
16+
17+
class C<T : Q> : P {}
18+
19+
struct Outer<T : P> {
20+
struct Inner<U> where T : C<U> {
21+
func doStuff(_ u: U) {
22+
takesR(u.t)
23+
}
24+
}
25+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
protocol P {
4+
associatedtype T : Q
5+
func foo()
6+
}
7+
8+
extension P {
9+
func foo() {}
10+
}
11+
12+
protocol Q {}
13+
14+
class C<T : Q> : P {}
15+
16+
protocol PP {
17+
associatedtype T : QQ
18+
func foo()
19+
}
20+
21+
extension PP {
22+
func foo() {}
23+
}
24+
25+
class QQ {}
26+
27+
class CC<T : QQ> : PP {}
28+
29+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s42class_conforms_with_default_implementation1CCyqd__GAA1PA2aEP3fooyyFTW : $@convention(witness_method: P) <τ_0_0><τ_1_0 where τ_0_0 : C<τ_1_0>, τ_1_0 : Q> (@in_guaranteed τ_0_0) -> () {
30+
// CHECK: [[WITNESS:%.*]] = function_ref @$s42class_conforms_with_default_implementation1PPAAE3fooyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
31+
// CHECK: apply [[WITNESS]]<τ_0_0>(%0) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
32+
// CHECK: return
33+
34+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s42class_conforms_with_default_implementation2CCCyqd__GAA2PPA2aEP3fooyyFTW : $@convention(witness_method: PP) <τ_0_0><τ_1_0 where τ_0_0 : CC<τ_1_0>, τ_1_0 : QQ> (@in_guaranteed τ_0_0) -> () {
35+
// CHECK: [[WITNESS:%.*]] = function_ref @$s42class_conforms_with_default_implementation2PPPAAE3fooyyF : $@convention(method) <τ_0_0 where τ_0_0 : PP> (@in_guaranteed τ_0_0) -> ()
36+
// CHECK: apply [[WITNESS]]<τ_0_0>(%0) : $@convention(method) <τ_0_0 where τ_0_0 : PP> (@in_guaranteed τ_0_0) -> ()
37+
// CHECK: return
38+
39+
// CHECK-LABEL: sil_witness_table hidden <T where T : Q> C<T>: P module class_conforms_with_default_implementation {
40+
// CHECK-NEXT: associated_type_protocol (T: Q): dependent
41+
// CHECK-NEXT: associated_type T: T
42+
// CHECK-NEXT: method #P.foo: <Self where Self : P> (Self) -> () -> () : @$s42class_conforms_with_default_implementation1CCyqd__GAA1PA2aEP3fooyyFTW
43+
// CHECK-NEXT: }
44+
45+
// CHECK-LABEL: sil_witness_table hidden <T where T : QQ> CC<T>: PP module class_conforms_with_default_implementation {
46+
// CHECK-NEXT: associated_type T: T
47+
// CHECK-NEXT: method #PP.foo: <Self where Self : PP> (Self) -> () -> () : @$s42class_conforms_with_default_implementation2CCCyqd__GAA2PPA2aEP3fooyyFTW
48+
// CHECK-NEXT: }

validation-test/compiler_crashers_2/sr11108.swift renamed to validation-test/compiler_crashers_2_fixed/sr11108.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
// RUN: not --crash %target-swift-emit-silgen %s
2-
3-
// REQUIRES: asserts
1+
// RUN: %target-swift-emit-silgen %s
42

53
protocol Example {
64
associatedtype Signed: SignedInteger

0 commit comments

Comments
 (0)