Skip to content

Commit bcee54b

Browse files
committed
GSB: The combination of a superclass and conformance requirement might force a type to be concrete
A protocol can constrain an associated type to Self: protocol P { associatedtype A : Q where A.B == Self } protocol Q { associatedtype B } And a class might conform to this protocol: class C : P { typealias A = D } class D : Q { typealias B = C } The generic signature <Self where Self : P, Self : C> is built during conformance checking. Since Self : C, we must have that Self.A == D; since D.B == C, the requierement 'A.B == Self' in protocol P implies that 'Self == C'. So the correct minimized signature here is <Self where Self == C>. This wasn't handled properly before, because of assumptions in removeSelfDerived() and a couple of other places. Fixes rdar://71677712, rdar://76155506, https://bugs.swift.org/browse/SR-10033, https://bugs.swift.org/browse/SR-13884.
1 parent 7e17aaf commit bcee54b

File tree

4 files changed

+90
-6
lines changed

4 files changed

+90
-6
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5790,6 +5790,11 @@ class RedundantRequirementGraph {
57905790
RequirementKind kind,
57915791
const SmallVectorImpl<Constraint<T>> &impliedByConcrete,
57925792
const Optional<ExplicitRequirement> &concreteTypeRequirement) {
5793+
// This can occur if the combination of a superclass requirement and
5794+
// protocol conformance requirement force a type to become concrete.
5795+
if (!concreteTypeRequirement)
5796+
return;
5797+
57935798
for (auto constraint : impliedByConcrete) {
57945799
if (constraint.source->isDerivedNonRootRequirement())
57955800
continue;
@@ -8102,6 +8107,11 @@ void GenericSignatureBuilder::checkConcreteTypeConstraints(
81028107
/*dropDerivedViaConcrete=*/true,
81038108
/*allCanBeSelfDerived=*/true);
81048109

8110+
// This can occur if the combination of a superclass requirement and
8111+
// protocol conformance requirement force a type to become concrete.
8112+
if (equivClass->concreteTypeConstraints.empty())
8113+
return;
8114+
81058115
checkConstraintList<Type>(
81068116
genericParams, equivClass->concreteTypeConstraints, RequirementKind::SameType,
81078117
[&](const ConcreteConstraint &constraint) {
@@ -8527,12 +8537,26 @@ GenericSignature GenericSignatureBuilder::rebuildSignatureWithoutRedundantRequir
85278537

85288538
auto subjectType = req.getSource()->getStoredType();
85298539
subjectType = stripBoundDependentMemberTypes(subjectType);
8530-
subjectType =
8540+
auto resolvedSubjectType =
85318541
resolveDependentMemberTypes(*this, subjectType,
85328542
ArchetypeResolutionKind::WellFormed);
85338543

8534-
if (auto optReq = createRequirement(req.getKind(), subjectType, req.getRHS(),
8535-
getGenericParams())) {
8544+
// This can occur if the combination of a superclass requirement and
8545+
// protocol conformance requirement force a type to become concrete.
8546+
//
8547+
// FIXME: Is there a more principled formulation of this?
8548+
if (req.getKind() == RequirementKind::Superclass &&
8549+
!resolvedSubjectType->isTypeParameter()) {
8550+
newBuilder.addRequirement(Requirement(RequirementKind::SameType,
8551+
subjectType, resolvedSubjectType),
8552+
newSource, nullptr);
8553+
continue;
8554+
}
8555+
8556+
assert(resolvedSubjectType->isTypeParameter());
8557+
8558+
if (auto optReq = createRequirement(req.getKind(), resolvedSubjectType,
8559+
req.getRHS(), getGenericParams())) {
85368560
auto newReq = stripBoundDependentMemberTypes(*optReq);
85378561
newBuilder.addRequirement(newReq, newSource, nullptr);
85388562
}

test/Generics/sr13884.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-swift-frontend -emit-ir -debug-generic-signatures %s 2>&1 | %FileCheck %s
3+
4+
public protocol P {
5+
associatedtype A : Q where A.B == Self
6+
}
7+
8+
public protocol Q {
9+
associatedtype B
10+
}
11+
12+
public class C : P {
13+
public typealias A = D
14+
}
15+
16+
public class D : Q {
17+
public typealias B = C
18+
}
19+
20+
// Both <T : P & C> and <T : C & P> minimize to <T where T == C>:
21+
// - T : P and T : C imply that T.A == C.A == D;
22+
// - T : P also implies that T.A.B == T, via A.B == Self in P;
23+
// - Since T.A == D, T.A.B == D.B, therefore Self == D.B.
24+
// - D.B is a typealias for C, so really Self == C.
25+
26+
// CHECK-LABEL: Generic signature: <T where T == D.B>
27+
public func takesBoth1<T : P & C>(_: T) {}
28+
// expected-warning@-1 {{redundant conformance constraint 'T' : 'P'}}
29+
// expected-note@-2 {{conformance constraint 'T' : 'P' implied here}}
30+
31+
// CHECK-LABEL: Generic signature: <U where U == D.B>
32+
public func takesBoth2<U : C & P>(_: U) {}
33+
// expected-warning@-1 {{redundant conformance constraint 'U' : 'P'}}
34+
// expected-note@-2 {{conformance constraint 'U' : 'P' implied here}}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
public protocol P {
4+
associatedtype A : Q where A.B == Self
5+
6+
func hasDefaultImplementation1()
7+
func hasDefaultImplementation2<U>(_: U) where U : Q, U.B == Self
8+
}
9+
10+
public extension P {
11+
func hasDefaultImplementation1() {}
12+
func hasDefaultImplementation2<U>(_: U) where U : Q, U.B == Self {}
13+
}
14+
15+
public protocol Q {
16+
associatedtype B
17+
}
18+
19+
public class C : P {
20+
public typealias A = D
21+
}
22+
23+
public class D : Q {
24+
public typealias B = C
25+
}
26+
27+
// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [ossa] @$s41class_conforms_with_default_concrete_self1CCAA1PA2aDP25hasDefaultImplementation1yyFTW : $@convention(witness_method: P) (@in_guaranteed C) -> () {
28+
// CHECK-LABEL: sil shared [transparent] [serialized] [thunk] [ossa] @$s41class_conforms_with_default_concrete_self1CCAA1PA2aDP25hasDefaultImplementation2yyqd__1BQyd__RszAA1QRd__lFTW : $@convention(witness_method: P) <τ_0_0 where τ_0_0 == C><τ_1_0 where τ_1_0 : Q, τ_1_0.B == C> (@in_guaranteed τ_1_0, @in_guaranteed C) -> () {

validation-test/compiler_crashers_2/0189-sr10033.swift renamed to validation-test/compiler_crashers_2_fixed/0189-sr10033.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-frontend -typecheck %s
2-
3-
// REQUIRES: asserts
1+
// RUN: %target-swift-frontend -emit-ir -verify %s
42

53
protocol P1 {
64
associatedtype A2 : P2 where A2.A1 == Self

0 commit comments

Comments
 (0)