Skip to content

Commit aa210d2

Browse files
committed
RequirementMachine: Prefer to eliminate concrete type requirements over superclass requirements again
If you have something like this: protocol P { associatedtype A : Q where Self == Self.A.B } protocol Q { associatedtype B } class C : P { typealias A = D } class D : P { typealias B = C } The GSB would minimize the generic signature <T where T : P, T : C> to <T where T == C>, because of the same-type requirement in protocol P. However in reality, the conformance 'C : P' is unsound, because it is no longer covariant. I added a warning in commit d831eff74879cb. Because the upcoming 'concrete contraction' pass eliminates 'T : P' before homotopy reduction gets a chance to run, I'm changing the Requirement Machine to produce a different minimization from the GSB. This is technically an ABI break, but it should not impact any real code in practice. If it does, I'll need to come up with a different workaround in concrete contraction.
1 parent ab6584c commit aa210d2

File tree

3 files changed

+25
-24
lines changed

3 files changed

+25
-24
lines changed

lib/AST/RequirementMachine/Symbol.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,13 @@ class Symbol final {
120120
/// term's type satisfies the layout constraint.
121121
Layout,
122122

123-
/// When appearing at the end of a term, denotes that the term
124-
/// is exactly equal to the concrete type.
125-
ConcreteType,
126-
127123
/// When appearing at the end of a term, denotes that the term
128124
/// is a subclass of the superclass constraint.
129-
///
130-
/// Note that this orders after ConcreteType, to ensure compatibility
131-
/// with the GenericSignatureBuilder on pathological generic signatures
132-
/// where a type is subject to both a superclass and concrete type
133-
/// requirement that imply each other.
134125
Superclass,
126+
127+
/// When appearing at the end of a term, denotes that the term
128+
/// is exactly equal to the concrete type.
129+
ConcreteType,
135130
};
136131

137132
static const unsigned NumKinds = 8;
Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-typecheck-verify-swift
2-
// RUN: not %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-inferred-signatures=verify %s 2>&1 | %FileCheck %s
1+
// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=on
2+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-inferred-signatures=on %s 2>&1 | %FileCheck %s
33

44
public protocol P {
55
associatedtype A : Q where A.B == Self
@@ -21,20 +21,26 @@ public class D : Q {
2121
public typealias B = C
2222
}
2323

24-
// Both <T : P & C> and <T : C & P> minimize to <T where T == C>:
24+
// This is fine, because FinalC is final.
25+
public final class FinalC : P {
26+
public typealias A = FinalD
27+
}
28+
29+
public class FinalD : Q {
30+
public typealias B = FinalC
31+
}
32+
33+
// With the GSB, both <T : P & C> and <T : C & P> minimized to <T where T == C>:
2534
// - T : P and T : C imply that T.A == C.A == D;
2635
// - T : P also implies that T.A.B == T, via A.B == Self in P;
2736
// - Since T.A == D, T.A.B == D.B, therefore Self == D.B.
2837
// - D.B is a typealias for C, so really Self == C.
38+
//
39+
// The Requirement Machine leaves it as <T : C>. Technically this is an ABI break,
40+
// but the entire construction is unsound unless C is final.
41+
42+
// CHECK-LABEL: Generic signature: <T where T : C>
43+
public func takesBoth1<T>(_: T) where T : P, T : C {}
2944

30-
// CHECK-LABEL: Generic signature: <T where T == C>
31-
public func takesBoth1<T : P & C>(_: T) {}
32-
// expected-warning@-1 {{redundant conformance constraint 'T' : 'P'}}
33-
// expected-note@-2 {{conformance constraint 'T' : 'P' implied here}}
34-
// expected-error@-3 {{same-type requirement makes generic parameter 'T' non-generic}}
35-
36-
// CHECK-LABEL: Generic signature: <U where U == C>
37-
public func takesBoth2<U : C & P>(_: U) {}
38-
// expected-warning@-1 {{redundant conformance constraint 'U' : 'P'}}
39-
// expected-note@-2 {{conformance constraint 'U' : 'P' implied here}}
40-
// expected-error@-3 {{same-type requirement makes generic parameter 'U' non-generic}}
45+
// CHECK-LABEL: Generic signature: <U where U : C>
46+
public func takesBoth2<U>(_: U) where U : C, U : P {}

test/Generics/rdar80503090.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class C : P {}
2222
// expected-warning@-1 {{non-final class 'C' cannot safely conform to protocol 'P', which requires that 'Self' is exactly equal to 'Self.T'; this is an error in Swift 6}}
2323

2424
extension P where T : C {
25-
// CHECK-LABEL: Generic signature: <Self where Self == C>
25+
// CHECK-LABEL: Generic signature: <Self where Self : C>
2626
func test() {
2727
missing()
2828
}

0 commit comments

Comments
 (0)