Skip to content

Commit d3083bc

Browse files
committed
AssociatedTypeInference: Make a cycle breaking hack more principled
We would return an ErrorType without diagnosing anything from NormalProtocolConformance::getTypeWitnessAndDecl() to signal a transient condition to associated type inference. But other callers that receive an ErrorType don't expect that this can happen, and they in turn won't diagnose anything. So we'd end up in SILGen with a broken AST. Instead, push the cycle break up into getWitnessTypeForMatching(), and remove the hack from getTypeWitnessAndDecl(). If someone else calls getTypeWitnessAndDecl() and that causes a request cycle, we will now diagnose the request cycle via the usual mechanism. Fixes rdar://120388028.
1 parent 35420f2 commit d3083bc

File tree

5 files changed

+52
-16
lines changed

5 files changed

+52
-16
lines changed

lib/AST/ProtocolConformance.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -587,17 +587,6 @@ NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
587587
return { witnessType, nullptr };
588588
}
589589

590-
// If this conformance is in a state where it is inferring type witnesses but
591-
// we didn't find anything, fail.
592-
//
593-
// FIXME: This is unsound, because we may not have diagnosed anything but
594-
// still end up with an ErrorType in the AST.
595-
if (getDeclContext()->getASTContext().evaluator.hasActiveRequest(
596-
ResolveTypeWitnessesRequest{
597-
const_cast<NormalProtocolConformance *>(this)})) {
598-
return { Type(), nullptr };
599-
}
600-
601590
return evaluateOrDefault(
602591
assocType->getASTContext().evaluator,
603592
TypeWitnessRequest{const_cast<NormalProtocolConformance *>(this),

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2034,9 +2034,27 @@ static Type getWitnessTypeForMatching(NormalProtocolConformance *conformance,
20342034
return std::nullopt;
20352035
});
20362036

2037+
SubstOptions options(std::nullopt);
2038+
options.getTentativeTypeWitness =
2039+
[conformance](const NormalProtocolConformance *otherConf,
2040+
AssociatedTypeDecl *assocType) -> TypeBase * {
2041+
auto thisProto = conformance->getProtocol();
2042+
if (conformance == otherConf ||
2043+
(conformance->getType()->isEqual(otherConf->getType()) &&
2044+
thisProto->inheritsFrom(otherConf->getProtocol()))) {
2045+
// Don't attempt to recursively resolve this type witness.
2046+
return ErrorType::get(assocType->getASTContext()).getPointer();
2047+
}
2048+
2049+
// Okay, this is an unrelated associated type. Proceed to the
2050+
// usual type witness resolution path.
2051+
return nullptr;
2052+
};
2053+
20372054
// Replace Self with the concrete conforming type.
20382055
substType = substType.subst(QueryTypeSubstitutionMap{substitutions},
2039-
LookUpConformanceInModule());
2056+
LookUpConformanceInModule(),
2057+
options);
20402058

20412059
// If we don't have enough type witnesses, leave it abstract.
20422060
if (substType->hasError())
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// This used to crash -- rdar://120388028
4+
5+
struct Node {}
6+
7+
let x = Outer(implementation: Inner<Node>())
8+
9+
protocol Proto {
10+
associatedtype Node
11+
func doThings(things: Outer<Node, Self>)
12+
}
13+
14+
struct Outer<Node, Impl: Proto> where Impl.Node == Node {
15+
var implementation: Impl
16+
}
17+
18+
struct Inner<Node>: Proto { // expected-error {{circular reference}}
19+
// expected-note@-1 {{through reference here}}
20+
func doThings(things: Outer<Node, Inner<Node>>) {}
21+
// expected-note@-1 2{{through reference here}}
22+
// expected-note@-2 {{while resolving type 'Outer<Node, Inner<Node>>'}}
23+
}

test/decl/protocol/typealias_inference.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ protocol BaseProtocol {
1212
init(closure: Closure)
1313
}
1414

15-
struct Base<Value>: BaseProtocol {
15+
struct Base<Value>: BaseProtocol { // expected-error {{circular reference}}
16+
// expected-note@-1 {{through reference here}}
1617
private let closure: Closure
1718

18-
init(closure: Closure) { //expected-error {{reference to invalid type alias 'Closure' of type 'Base<Value>'}}
19+
init(closure: Closure) {
20+
// expected-note@-1 {{while resolving type 'Closure'}}
21+
// expected-note@-2 2{{through reference here}}
1922
self.closure = closure
2023
}
2124
}

validation-test/compiler_crashers_2_fixed/0127-issue-48118.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ extension P {
2828
}
2929
}
3030

31-
public struct S: P {
31+
public struct S: P { // expected-error {{circular reference}}
32+
// expected-note@-1 {{through reference here}}
3233
public typealias PA = String
3334
public typealias PB = Int
3435

3536
public func f1(_ x: Context, _ y: PA) {
3637
}
37-
public func f2(_ x: Context, _ y: PB) { // expected-error {{reference to invalid type alias 'Context' of type 'S'}}
38+
public func f2(_ x: Context, _ y: PB) {
39+
// expected-note@-1 2{{through reference here}}
40+
// expected-note@-2 {{while resolving type 'Context'}}
3841
}
3942
}

0 commit comments

Comments
 (0)