Skip to content

Commit a74dcd8

Browse files
committed
IRGen: Fix circularity in conformance access path evaluation
This fixes the IRGen side of rdar://problem/83687967.
1 parent d66f355 commit a74dcd8

File tree

3 files changed

+143
-10
lines changed

3 files changed

+143
-10
lines changed

lib/IRGen/GenProto.cpp

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2599,7 +2599,7 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF,
25992599

26002600
CanType associatedType =
26012601
sourceConformance.getAssociatedType(sourceType, association)
2602-
->getCanonicalType();
2602+
->getCanonicalType();
26032603
sourceKey.Type = associatedType;
26042604

26052605
auto associatedConformance =
@@ -2614,10 +2614,77 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF,
26142614

26152615
if (!source) return MetadataResponse();
26162616

2617-
auto sourceMetadata =
2618-
IGF.emitAbstractTypeMetadataRef(sourceType);
2619-
auto associatedMetadata =
2620-
IGF.emitAbstractTypeMetadataRef(sourceKey.Type);
2617+
auto *sourceMetadata =
2618+
IGF.emitAbstractTypeMetadataRef(sourceType);
2619+
2620+
// Try to avoid circularity when realizing the associated metadata.
2621+
//
2622+
// Suppose we are asked to produce the witness table for some
2623+
// conformance access path (T : P)(Self.X : Q)(Self.Y : R).
2624+
//
2625+
// The associated conformance accessor for (Self.Y : R) takes the
2626+
// metadata for T.X and T.X : Q as arguments. If T.X is concrete,
2627+
// there are two ways of building it:
2628+
//
2629+
// a) Using the knowledge of the concrete type to build it directly,
2630+
// by first constructing the type metadata for its generic arguments.
2631+
//
2632+
// b) Obtaining T.X from the witness table for T : P. This will also
2633+
// construct the generic type, but from the generic environment
2634+
// of the concrete type of T, and not the abstract environment of
2635+
// our conformance access path.
2636+
//
2637+
// Now, say that T.X == Foo<T.X.Y>, with "Foo<A> where A : R".
2638+
//
2639+
// If approach a) is taken, then constructing Foo<T.X.Y> requires
2640+
// recovering the conformance T.X.Y : R, which recursively evaluates
2641+
// the same conformance access path, eventually causing a stack
2642+
// overflow.
2643+
//
2644+
// With approach b) on the other hand, the type metadata for
2645+
// Foo<T.X.Y> is constructed from the concrete type metadata for T,
2646+
// which must provide some other conformance access path for the
2647+
// conformance to R.
2648+
//
2649+
// This is not very principled, and a remaining issue is with conformance
2650+
// requirements where the subject type consists of multiple terms.
2651+
//
2652+
// A better approach would be for conformance access paths to directly
2653+
// record how type metadata at each intermediate step is constructed.
2654+
llvm::Value *associatedMetadata = nullptr;
2655+
2656+
if (auto response = IGF.tryGetLocalTypeMetadata(associatedType,
2657+
MetadataState::Abstract)) {
2658+
// The associated type metadata was already cached, so we're fine.
2659+
associatedMetadata = response.getMetadata();
2660+
} else {
2661+
// If the associated type is concrete and the parent type is an archetype,
2662+
// it is better to realize the associated type metadata from the witness
2663+
// table of the parent's conformance, instead of realizing the concrete
2664+
// type directly.
2665+
auto depMemType = cast<DependentMemberType>(association);
2666+
CanType baseSubstType =
2667+
sourceConformance.getAssociatedType(sourceType, depMemType.getBase())
2668+
->getCanonicalType();
2669+
if (auto archetypeType = cast<ArchetypeType>(baseSubstType)) {
2670+
AssociatedType baseAssocType(depMemType->getAssocType());
2671+
2672+
MetadataResponse response =
2673+
emitAssociatedTypeMetadataRef(IGF, archetypeType, baseAssocType,
2674+
MetadataState::Abstract);
2675+
2676+
// Cache this response in case we have to realize the associated type
2677+
// again later.
2678+
IGF.setScopedLocalTypeMetadata(associatedType, response);
2679+
2680+
associatedMetadata = response.getMetadata();
2681+
} else {
2682+
// Ok, fall back to realizing the (possibly concrete) type.
2683+
associatedMetadata =
2684+
IGF.emitAbstractTypeMetadataRef(sourceKey.Type);
2685+
}
2686+
}
2687+
26212688
auto sourceWTable = source.getMetadata();
26222689

26232690
AssociatedConformance associatedConformanceRef(sourceProtocol,

test/Generics/rdar83687967.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
3+
// RUN: %target-swift-frontend -emit-ir %s
4+
5+
public protocol P1 {}
6+
7+
public protocol P2 {
8+
associatedtype A: P1
9+
}
10+
11+
public protocol P3 {
12+
associatedtype B: P2
13+
associatedtype A where B.A == A
14+
}
15+
16+
public struct G<A: P1>: P2 {}
17+
18+
public func callee<T: P1>(_: T.Type) {}
19+
20+
// CHECK: rdar83687967.(file).caller11@
21+
// CHECK: Generic signature: <Child where Child : P3, Child.B == G<Child.A>>
22+
public func caller11<Child: P3>(_: Child)
23+
where Child.B == G<Child.A> {
24+
callee(Child.A.self)
25+
}
26+
27+
// CHECK: rdar83687967.(file).caller12@
28+
// CHECK: Generic signature: <Child where Child : P3, Child.B == G<Child.A>>
29+
public func caller12<Child: P3>(_: Child)
30+
// expected-note@-1 {{conformance constraint 'Child.A' : 'P1' implied here}}
31+
where Child.B == G<Child.A>, Child.A : P1 {
32+
// expected-warning@-1 {{redundant conformance constraint 'Child.A' : 'P1'}}
33+
34+
// Make sure IRGen can evaluate the conformance access path
35+
// (Child : P3)(Self.B : P2)(Self.A : P1).
36+
callee(Child.A.self)
37+
}
38+
39+
// CHECK: rdar83687967.(file).X1@
40+
// CHECK: Requirement signature: <Self where Self.Child : P3, Self.Child.B == G<Self.Child.A>>
41+
public protocol X1 {
42+
associatedtype Child: P3
43+
where Child.B == G<Child.A>
44+
}
45+
46+
// CHECK: rdar83687967.(file).X2@
47+
// CHECK: Requirement signature: <Self where Self.Child : P3, Self.Child.B == G<Self.Child.A>>
48+
49+
public protocol X2 {
50+
associatedtype Child: P3
51+
// expected-note@-1 {{conformance constraint 'Self.Child.A' : 'P1' implied here}}
52+
where Child.B == G<Child.A>, Child.A : P1
53+
// expected-warning@-1 {{redundant conformance constraint 'Self.Child.A' : 'P1'}}
54+
}
55+
56+
public func caller21<T : X1>(_: T) {
57+
// Make sure IRGen can evaluate the conformance access path
58+
// (T : X1)(Child : P3)(Self.B : P2)(Self.A : P1).
59+
callee(T.Child.A.self)
60+
}
61+
62+
public func caller22<T : X2>(_: T) {
63+
// Make sure IRGen can evaluate the conformance access path
64+
// (T : X2)(Child : P3)(Self.B : P2)(Self.A : P1).
65+
callee(T.Child.A.self)
66+
}

test/IRGen/associated_types.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,17 @@ func testFastRuncible<T: Runcible, U: FastRuncible>(_ t: T, u: U)
7575
// 1. Get the type metadata for U.RuncerType.Runcee.
7676
// 1a. Get the type metadata for U.RuncerType.
7777
// Note that we actually look things up in T, which is going to prove unfortunate.
78-
// CHECK: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness([[INT]] 255, i8** %T.Runcible, %swift.type* %T, %swift.protocol_requirement* getelementptr{{.*}}i32 12), i32 -1), %swift.protocol_requirement* getelementptr inbounds (<{{.*}}>, <{{.*}}>* @"$s16associated_types8RuncibleMp", i32 0, i32 14)) [[NOUNWIND_READNONE:#.*]]
79-
// CHECK-NEXT: %T.RuncerType = extractvalue %swift.metadata_response [[T2]], 0
78+
// CHECK: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness([[INT]] 255, i8** %U.FastRuncible, %swift.type* %U, %swift.protocol_requirement* getelementptr{{.*}}i32 9), i32 -1), %swift.protocol_requirement* getelementptr inbounds (<{{.*}}>, <{{.*}}>* @"$s16associated_types12FastRuncibleMp", i32 0, i32 10)) [[NOUNWIND_READNONE:#.*]]
79+
// CHECK-NEXT: [[T3:%.*]] = extractvalue %swift.metadata_response [[T2]], 0
8080
// CHECK-NEXT: extractvalue %swift.metadata_response [[T2]], 1
8181
// 2. Get the witness table for U.RuncerType.Runcee : Speedy
8282
// 2a. Get the protocol witness table for U.RuncerType : FastRuncer.
83-
// CHECK-NEXT: %T.RuncerType.FastRuncer = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %U.FastRuncible, %swift.type* %U, %swift.type* %T.RuncerType
83+
// CHECK-NEXT: %T.RuncerType.FastRuncer = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %U.FastRuncible, %swift.type* %U, %swift.type* [[T3]]
8484
// 1c. Get the type metadata for U.RuncerType.Runcee.
85-
// CHECK-NEXT: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness([[INT]] 0, i8** %T.RuncerType.FastRuncer, %swift.type* %T.RuncerType, {{.*}}, %swift.protocol_requirement* getelementptr inbounds (<{{.*}}>, <{{.*}}>* @"$s16associated_types10FastRuncerMp", i32 0, i32 10))
85+
// CHECK-NEXT: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness([[INT]] 0, i8** %T.RuncerType.FastRuncer, %swift.type* [[T3]], {{.*}}, %swift.protocol_requirement* getelementptr inbounds (<{{.*}}>, <{{.*}}>* @"$s16associated_types10FastRuncerMp", i32 0, i32 10))
8686
// CHECK-NEXT: %T.RuncerType.Runcee = extractvalue %swift.metadata_response [[T2]], 0
8787
// 2b. Get the witness table for U.RuncerType.Runcee : Speedy.
88-
// CHECK-NEXT: %T.RuncerType.Runcee.Speedy = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %T.RuncerType.FastRuncer, %swift.type* %T.RuncerType, %swift.type* %T.RuncerType.Runcee
88+
// CHECK-NEXT: %T.RuncerType.Runcee.Speedy = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %T.RuncerType.FastRuncer, %swift.type* [[T3]], %swift.type* %T.RuncerType.Runcee
8989
// 3. Perform the actual call.
9090
// CHECK-NEXT: [[T0_GEP:%.*]] = getelementptr inbounds i8*, i8** %T.RuncerType.Runcee.Speedy, i32 1
9191
// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[T0_GEP]]

0 commit comments

Comments
 (0)