Skip to content

Commit 23abf74

Browse files
committed
RequirementMachine: Better error recovery from invalid protocol inheritance clauses
getInheritedProtocols() skips type resolution and directly resolves TypeReprs to TypeDecls. On the other hand, when building a protocol requirement signature, we use type resolution to resolve inheritance clause entries so that we can properly support parameterized protocol types, and protocol compositions that contain classes. Since a TypeRepr with an invalid sub-component resolves to an ErrorType, this meant that in invalid code, the first list of protocols might contain protocols that don't appear in the second. This broke rewrite system invariants. Fix this by checking if type resolution failed when building the requirement signature of a protocol, and if so, also look at getInheritedProtocols(). Fixes #61020.
1 parent 60c8b4b commit 23abf74

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,10 +724,28 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
724724

725725
auto selfTy = proto->getSelfInterfaceType();
726726

727+
unsigned errorCount = errors.size();
727728
realizeInheritedRequirements(proto, selfTy,
728729
/*inferRequirements=*/false,
729730
result, errors);
730731

732+
if (errors.size() > errorCount) {
733+
// Add requirements from inherited protocols, which are obtained via
734+
// getDirectlyInheritedNominalTypeDecls(). Normally this duplicates
735+
// the information found in the resolved types from the inheritance
736+
// clause, except when type resolution fails and returns an ErrorType.
737+
//
738+
// For example, in 'protocol P: Q & Blah', where 'Blah' does not exist,
739+
// the type 'Q & Blah' resolves to an ErrorType, while the simpler
740+
// mechanism in getDirectlyInheritedNominalTypeDecls() still finds 'Q'.
741+
for (auto *inheritedProto : proto->getInheritedProtocols()) {
742+
result.push_back({
743+
Requirement(RequirementKind::Conformance,
744+
selfTy, inheritedProto->getDeclaredInterfaceType()),
745+
SourceLoc(), /*wasInferred=*/false});
746+
}
747+
}
748+
731749
// Add requirements from the protocol's own 'where' clause.
732750
WhereClauseOwner(proto).visitRequirements(TypeResolutionStage::Structural,
733751
[&](const Requirement &req, RequirementRepr *reqRepr) {

test/Generics/issue-61020.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-typecheck-verify-swift -debug-generic-signatures 2>&1 | %FileCheck %s
2+
3+
protocol Base<T> {
4+
associatedtype T
5+
}
6+
7+
// CHECK-LABEL: .Derived@
8+
// CHECK-NEXT: Requirement signature: <Self where Self : Base>
9+
protocol Derived: Base & DoesNotExist { // expected-error {{cannot find type 'DoesNotExist' in scope}}
10+
func position(_: T)
11+
}
12+
13+
// CHECK-LABEL: .OtherDerived@
14+
// CHECK-NEXT: Requirement signature: <Self where Self : Base>
15+
protocol OtherDerived: Base<DoesNotExist> {} // expected-error {{cannot find type 'DoesNotExist' in scope}}

0 commit comments

Comments
 (0)