Skip to content

Commit e65290c

Browse files
committed
Sema: Associated type inference skips witnesses that might trigger a request cycle
This implements a structural walk over the TypeRepr to catch situations where we attempt to infer `A` from `func f(_: A)`, which references the concrete `A` that will be synthesized in the conforming type. Fixes: - rdar://34956654 / #48680 - rdar://38913692 / #49066 - rdar://56672411 - #50010 - rdar://81587765 / #57355 - rdar://117442510
1 parent b598baf commit e65290c

File tree

4 files changed

+242
-17
lines changed

4 files changed

+242
-17
lines changed

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,127 @@ static bool associatedTypesAreSameEquivalenceClass(AssociatedTypeDecl *a,
161161
return false;
162162
}
163163

164+
namespace {
165+
166+
/// Try to avoid situations where resolving the type of a witness calls back
167+
/// into associated type inference.
168+
struct TypeReprCycleCheckWalker : ASTWalker {
169+
llvm::SmallDenseSet<Identifier, 2> circularNames;
170+
ValueDecl *witness;
171+
bool found;
172+
173+
TypeReprCycleCheckWalker(
174+
const llvm::SetVector<AssociatedTypeDecl *> &allUnresolved)
175+
: witness(nullptr), found(false) {
176+
for (auto *assocType : allUnresolved) {
177+
circularNames.insert(assocType->getName());
178+
}
179+
}
180+
181+
PreWalkAction walkToTypeReprPre(TypeRepr *T) override {
182+
// FIXME: We should still visit any generic arguments of this member type.
183+
// However, we want to skip 'Foo.Element' because the 'Element' reference is
184+
// not unqualified.
185+
if (auto *memberTyR = dyn_cast<MemberTypeRepr>(T)) {
186+
return Action::SkipChildren();
187+
}
188+
189+
if (auto *identTyR = dyn_cast<SimpleIdentTypeRepr>(T)) {
190+
if (circularNames.count(identTyR->getNameRef().getBaseIdentifier()) > 0) {
191+
// If unqualified lookup can find a type with this name without looking
192+
// into protocol members, don't skip the witness, since this type might
193+
// be a candidate witness.
194+
auto desc = UnqualifiedLookupDescriptor(
195+
identTyR->getNameRef(), witness->getDeclContext(),
196+
identTyR->getLoc(), UnqualifiedLookupOptions());
197+
198+
auto &ctx = witness->getASTContext();
199+
auto results =
200+
evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {});
201+
202+
// Ok, resolving this name would trigger associated type inference
203+
// recursively. We're going to skip this witness.
204+
if (results.allResults().empty()) {
205+
found = true;
206+
return Action::Stop();
207+
}
208+
}
209+
}
210+
211+
return Action::Continue();
212+
}
213+
214+
bool checkForPotentialCycle(ValueDecl *witness) {
215+
// Don't do this for protocol extension members, because we have a
216+
// mini "solver" that avoids similar issues instead.
217+
if (witness->getDeclContext()->getSelfProtocolDecl() != nullptr)
218+
return false;
219+
220+
// If we already have an interface type, don't bother trying to
221+
// avoid a cycle.
222+
if (witness->hasInterfaceType())
223+
return false;
224+
225+
// We call checkForPotentailCycle() multiple times with
226+
// different witnesses.
227+
found = false;
228+
this->witness = witness;
229+
230+
auto walkInto = [&](TypeRepr *tyR) {
231+
if (tyR)
232+
tyR->walk(*this);
233+
return found;
234+
};
235+
236+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(witness)) {
237+
for (auto *param : *AFD->getParameters()) {
238+
if (walkInto(param->getTypeRepr()))
239+
return true;
240+
}
241+
242+
if (auto *FD = dyn_cast<FuncDecl>(witness)) {
243+
if (walkInto(FD->getResultTypeRepr()))
244+
return true;
245+
}
246+
247+
return false;
248+
}
249+
250+
if (auto *SD = dyn_cast<SubscriptDecl>(witness)) {
251+
for (auto *param : *SD->getIndices()) {
252+
if (walkInto(param->getTypeRepr()))
253+
return true;
254+
}
255+
256+
if (walkInto(SD->getElementTypeRepr()))
257+
return true;
258+
259+
return false;
260+
}
261+
262+
if (auto *VD = dyn_cast<VarDecl>(witness)) {
263+
if (walkInto(VD->getTypeReprOrParentPatternTypeRepr()))
264+
return true;
265+
266+
return false;
267+
}
268+
269+
if (auto *EED = dyn_cast<EnumElementDecl>(witness)) {
270+
for (auto *param : *EED->getParameterList()) {
271+
if (walkInto(param->getTypeRepr()))
272+
return true;
273+
}
274+
275+
return false;
276+
}
277+
278+
assert(false && "Should be exhaustive");
279+
return false;
280+
}
281+
};
282+
283+
}
284+
164285
InferredAssociatedTypesByWitnesses
165286
AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
166287
ConformanceChecker &checker,
@@ -176,11 +297,13 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
176297
abort();
177298
}
178299

300+
TypeReprCycleCheckWalker cycleCheck(allUnresolved);
301+
179302
InferredAssociatedTypesByWitnesses result;
180303

181304
auto isExtensionUsableForInference = [&](const ExtensionDecl *extension) {
182305
// The context the conformance being checked is declared on.
183-
const auto conformanceCtx = checker.Conformance->getDeclContext();
306+
const auto conformanceCtx = conformance->getDeclContext();
184307
if (extension == conformanceCtx)
185308
return true;
186309

@@ -250,11 +373,17 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
250373
// If the potential witness came from an extension, and our `Self`
251374
// type can't use it regardless of what associated types we end up
252375
// inferring, skip the witness.
253-
if (auto extension = dyn_cast<ExtensionDecl>(witness->getDeclContext()))
376+
if (auto extension = dyn_cast<ExtensionDecl>(witness->getDeclContext())) {
254377
if (!isExtensionUsableForInference(extension)) {
255378
LLVM_DEBUG(llvm::dbgs() << "Extension not usable for inference\n");
256379
continue;
257380
}
381+
}
382+
383+
if (cycleCheck.checkForPotentialCycle(witness)) {
384+
LLVM_DEBUG(llvm::dbgs() << "Skipping witness to avoid request cycle\n");
385+
continue;
386+
}
258387

259388
// Try to resolve the type witness via this value witness.
260389
auto witnessResult = inferTypeWitnessesViaValueWitness(req, witness);
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-swift-frontend -emit-silgen %s -parse-as-library -module-name Test -experimental-lazy-typecheck
3+
4+
// This file should type check successfully.
5+
6+
// rdar://117442510
7+
public protocol P1 {
8+
associatedtype Value
9+
10+
func makeValue() -> Value
11+
func useProducedValue(_ produceValue: () -> Value)
12+
}
13+
14+
public typealias A1 = S1.Value
15+
16+
public struct S1: P1 {
17+
public func makeValue() -> Int { return 1 }
18+
public func useProducedValue(_ produceValue: () -> Value) {
19+
_ = produceValue()
20+
}
21+
}
22+
23+
// rdar://56672411
24+
public protocol P2 {
25+
associatedtype X = Int
26+
func foo(_ x: X)
27+
}
28+
29+
public typealias A2 = S2.X
30+
31+
public struct S2: P2 {
32+
public func bar(_ x: X) {}
33+
public func foo(_ x: X) {}
34+
}
35+
36+
// https://github.com/apple/swift/issues/57355
37+
public protocol P3 {
38+
associatedtype T
39+
var a: T { get }
40+
var b: T { get }
41+
var c: T { get }
42+
}
43+
44+
public typealias A3 = S3.T
45+
46+
public struct S3: P3 {
47+
public let a: Int
48+
public let b: T
49+
public let c: T
50+
}
51+
52+
// Regression tests
53+
public protocol P4 {
54+
associatedtype A
55+
func f(_: A)
56+
}
57+
58+
public typealias A = Int
59+
60+
public typealias A4 = S4.A
61+
62+
public struct S4: P4 {
63+
public func f(_: A) {}
64+
}
65+
66+
public typealias A5 = OuterGeneric<Int>.Inner.A
67+
68+
public struct OuterGeneric<A> {
69+
public struct Inner: P4 {
70+
public func f(_: A) { }
71+
}
72+
}
73+
74+
public typealias A6 = OuterNested.Inner.A
75+
76+
public struct OuterNested {
77+
public struct A {}
78+
79+
public struct Inner: P4 {
80+
public func f(_: A) {}
81+
}
82+
}
83+
84+
public protocol CaseProtocol {
85+
associatedtype A = Int
86+
static func a(_: A) -> Self
87+
static func b(_: A) -> Self
88+
static func c(_: A) -> Self
89+
}
90+
91+
public typealias A7 = CaseWitness.A
92+
93+
public enum CaseWitness: CaseProtocol {
94+
case a(_: A)
95+
case b(_: A)
96+
case c(_: A)
97+
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-swift-frontend -emit-ir %s
22

33
// https://github.com/apple/swift/issues/48395
44

5-
struct DefaultAssociatedType {
5+
public struct DefaultAssociatedType {
66
}
77

88
protocol Protocol {
99
associatedtype AssociatedType = DefaultAssociatedType
1010
init(object: AssociatedType)
1111
}
1212

13-
final class Conformance: Protocol {
13+
public final class Conformance: Protocol {
1414
private let object: AssociatedType
15-
init(object: AssociatedType) { // expected-error {{reference to invalid associated type 'AssociatedType' of type 'Conformance'}}
15+
public init(object: AssociatedType) {
1616
self.object = object
1717
}
1818
}
Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-swift-frontend -emit-ir %s
22

33
// https://github.com/apple/swift/issues/48464
44

5-
protocol VectorIndex {
5+
public protocol VectorIndex {
66
associatedtype Vector8 : Vector where Vector8.Index == Self, Vector8.Element == UInt8
77
}
8-
enum VectorIndex1 : VectorIndex {
8+
public enum VectorIndex1 : VectorIndex {
99
case i0
10-
typealias Vector8 = Vector1<UInt8>
10+
public typealias Vector8 = Vector1<UInt8>
1111
}
12-
protocol Vector {
12+
public protocol Vector {
1313
associatedtype Index: VectorIndex
1414
associatedtype Element
1515
init(elementForIndex: (Index) -> Element)
1616
subscript(index: Index) -> Element { get set }
1717
}
18-
struct Vector1<Element> : Vector {
19-
//typealias Index = VectorIndex1 // Uncomment this line to workaround bug.
20-
var e0: Element
21-
init(elementForIndex: (VectorIndex1) -> Element) {
18+
public struct Vector1<Element> : Vector {
19+
public var e0: Element
20+
public init(elementForIndex: (VectorIndex1) -> Element) {
2221
e0 = elementForIndex(.i0)
2322
}
24-
subscript(index: Index) -> Element { // expected-error {{reference to invalid associated type 'Index' of type 'Vector1<Element>'}}
23+
public subscript(index: Index) -> Element {
2524
get { return e0 }
2625
set { e0 = newValue }
2726
}
2827
}
2928
extension Vector where Index == VectorIndex1 {
30-
init(_ e0: Element) { fatalError() }
29+
public init(_ e0: Element) { fatalError() }
3130
}

0 commit comments

Comments
 (0)