Skip to content

Commit 1267f87

Browse files
committed
AST: Fancier getPlaceholderRequirementSignature()
1 parent f9ed50e commit 1267f87

File tree

5 files changed

+81
-16
lines changed

5 files changed

+81
-16
lines changed

lib/AST/Decl.cpp

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6920,28 +6920,46 @@ ProtocolDecl::getProtocolDependencies() const {
69206920
std::nullopt);
69216921
}
69226922

6923-
/// If we hit a request cycle, give the protocol a requirement signature where
6924-
/// everything is Copyable and Escapable. Otherwise, we'll get spurious
6925-
/// downstream diagnostics concerning move-only types.
6923+
/// If we hit a request cycle, give the protocol a requirement signature that
6924+
/// still has inherited protocol requirements on Self, and also conformances
6925+
/// to Copyable and Escapable for all associated types. Otherwise, we'll see
6926+
/// invariant violations from the inheritance clause mismatch, as well as
6927+
/// spurious downstream diagnostics concerning move-only types.
69266928
static RequirementSignature getPlaceholderRequirementSignature(
69276929
const ProtocolDecl *proto) {
69286930
auto &ctx = proto->getASTContext();
69296931

6932+
SmallVector<ProtocolDecl *, 2> inheritedProtos;
6933+
for (auto *inheritedProto : proto->getInheritedProtocols()) {
6934+
inheritedProtos.push_back(inheritedProto);
6935+
}
6936+
6937+
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
6938+
for (auto ip : InvertibleProtocolSet::full()) {
6939+
auto *otherProto = ctx.getProtocol(getKnownProtocolKind(ip));
6940+
inheritedProtos.push_back(otherProto);
6941+
}
6942+
}
6943+
6944+
ProtocolType::canonicalizeProtocols(inheritedProtos);
6945+
69306946
SmallVector<Requirement, 2> requirements;
69316947

6948+
for (auto *inheritedProto : inheritedProtos) {
6949+
requirements.emplace_back(RequirementKind::Conformance,
6950+
proto->getSelfInterfaceType(),
6951+
inheritedProto->getDeclaredInterfaceType());
6952+
}
6953+
69326954
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
6933-
auto add = [&](Type type) {
6955+
for (auto *assocTypeDecl : proto->getAssociatedTypeMembers()) {
69346956
for (auto ip : InvertibleProtocolSet::full()) {
6935-
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
6936-
requirements.emplace_back(RequirementKind::Conformance, type,
6937-
proto->getDeclaredInterfaceType());
6957+
auto *otherProto = ctx.getProtocol(getKnownProtocolKind(ip));
6958+
requirements.emplace_back(RequirementKind::Conformance,
6959+
assocTypeDecl->getDeclaredInterfaceType(),
6960+
otherProto->getDeclaredInterfaceType());
69386961
}
6939-
};
6940-
6941-
add(proto->getSelfInterfaceType());
6942-
6943-
for (auto *assocTypeDecl : proto->getAssociatedTypeMembers())
6944-
add(assocTypeDecl->getDeclaredInterfaceType());
6962+
}
69456963
}
69466964

69476965
// Maintain invariants.

lib/AST/Type.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,11 @@ static void canonicalizeProtocols(SmallVectorImpl<ProtocolDecl *> &protocols,
15271527
if (proto == nullptr)
15281528
continue;
15291529

1530+
// The below algorithm assumes the inheritance graph is acyclic. Just skip
1531+
// it if we have invalid code.
1532+
if (proto->hasCircularInheritedProtocols())
1533+
continue;
1534+
15301535
// Add the protocols we inherited.
15311536
proto->walkInheritedProtocols([&](ProtocolDecl *inherited) {
15321537
if (inherited == proto)

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,6 +2054,11 @@ static void checkProtocolRefinementRequirements(ProtocolDecl *proto) {
20542054
const bool EnabledNoncopyableGenerics =
20552055
ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics);
20562056

2057+
// Check for circular inheritance; the HasCircularInheritedProtocolsRequest
2058+
// will diagnose an error in that case, and we skip all remaining checks.
2059+
if (proto->hasCircularInheritedProtocols())
2060+
return;
2061+
20572062
// If we make a ~P marking but our protocol Self type still conforms to P,
20582063
// diagnose an error.
20592064
//
@@ -3481,9 +3486,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
34813486
void visitProtocolDecl(ProtocolDecl *PD) {
34823487
checkUnsupportedNestedType(PD);
34833488

3484-
// Check for circular inheritance within the protocol.
3485-
(void) PD->hasCircularInheritedProtocols();
3486-
34873489
TypeChecker::checkDeclAttributes(PD);
34883490

34893491
// Check that all named primary associated types are valid.

test/Generics/issue-69318.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// We used to crash -- https://github.com/apple/swift/issues/69318
4+
5+
public protocol View {
6+
associatedtype NodeChildren: ViewGraphNodeChildren
7+
}
8+
9+
public protocol ViewGraphNodeChildren {
10+
associatedtype ParentView: View where ParentView.NodeChildren == Self
11+
// expected-note@-1 {{protocol requires nested type 'ParentView'; add nested type 'ParentView' for conformance}}
12+
}
13+
14+
public protocol ChildlessView: View where NodeChildren == EmptyViewGraphNodeChildren<Self> {}
15+
// expected-error@-1 {{circular reference}}
16+
17+
public struct EmptyViewGraphNodeChildren<ParentView: ChildlessView>: ViewGraphNodeChildren {}
18+
// expected-note@-1 3{{through reference here}}
19+
// expected-error@-2 {{type 'EmptyViewGraphNodeChildren<ParentView>' does not conform to protocol 'ViewGraphNodeChildren'}}

test/Generics/rdar123013710.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
public protocol P {
4+
associatedtype A
5+
associatedtype B: P
6+
7+
var b: B { get }
8+
static func f() -> Any?
9+
}
10+
11+
protocol Q: P where B == Never {} // expected-error {{circular reference}}
12+
13+
extension Never: Q, P { // expected-note 2{{through reference here}}
14+
public typealias A = Never
15+
public static func f() -> Any? { nil }
16+
}
17+
18+
extension Q {
19+
public var b: Never { fatalError() } // expected-note {{through reference here}}
20+
}
21+

0 commit comments

Comments
 (0)