Skip to content

Commit ce75256

Browse files
authored
Merge pull request #41851 from slavapestov/rqm-fix-protocol-refinement-bug
RequirementMachine: Fix terrible bug in minimization of protocol inheritance
2 parents 1301759 + 30da30e commit ce75256

File tree

4 files changed

+40
-25
lines changed

4 files changed

+40
-25
lines changed

lib/AST/RequirementMachine/MinimalConformances.cpp

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,18 @@ class MinimalConformances {
159159

160160
DebugOptions Debug;
161161

162-
// All conformance rules, sorted by (isExplicit(), getLHS()), with non-explicit
163-
// rules with longer left hand sides coming first.
162+
// All conformance rules in the current minimization domain, sorted by
163+
// (isExplicit(), getLHS()), with non-explicit rules with longer left hand
164+
// sides coming first.
164165
//
165166
// The idea here is that we want less canonical rules to be eliminated first,
166167
// but we prefer to eliminate non-explicit rules, in an attempt to keep protocol
167168
// conformance rules in the same protocol as they were originally defined in.
168169
SmallVector<unsigned, 4> ConformanceRules;
169170

170-
// Maps a conformance rule to a conformance path deriving the subject type's
171-
// base type. For example, consider the following conformance rule:
171+
// Maps a conformance rule in the current minimization domain to a conformance
172+
// path deriving the subject type's base type. For example, consider the
173+
// following conformance rule:
172174
//
173175
// T.[P:A].[Q:B].[R] => T.[P:A].[Q:B]
174176
//
@@ -177,15 +179,17 @@ class MinimalConformances {
177179
// path for the term T.[P:A].[Q], known as the 'parent path'.
178180
llvm::MapVector<unsigned, SmallVector<unsigned, 2>> ParentPaths;
179181

180-
// Maps a conformance rule to a list of paths. Each path in the list is a unique
181-
// derivation of the conformance in terms of other conformance rules.
182+
// Maps a conformance rule in the current minimization domain to a list of paths.
183+
// Each path in the list is a unique derivation of the conformance in terms of
184+
// other conformance rules.
182185
llvm::MapVector<unsigned, std::vector<SmallVector<unsigned, 2>>> ConformancePaths;
183186

184-
// The set of conformance rules which are protocol refinements, that is rules of
185-
// the form [P].[Q] => [P].
187+
// The set of conformance rules (from all minimization domains) which are protocol
188+
// refinements, that is rules of the form [P].[Q] => [P].
186189
llvm::DenseSet<unsigned> ProtocolRefinements;
187190

188-
// This is the result.
191+
// This is the computed result set of redundant conformance rules in the current
192+
// minimization domain.
189193
llvm::DenseSet<unsigned> &RedundantConformances;
190194

191195
void decomposeTermIntoConformanceRuleLeftHandSides(
@@ -391,17 +395,15 @@ void MinimalConformances::collectConformanceRules() {
391395
if (!rule.isAnyConformanceRule())
392396
continue;
393397

398+
// Save protocol refinement relations in a side table.
399+
if (rule.isProtocolRefinementRule(Context))
400+
ProtocolRefinements.insert(ruleID);
401+
394402
if (!System.isInMinimizationDomain(rule.getLHS().getRootProtocol()))
395403
continue;
396404

397405
ConformanceRules.push_back(ruleID);
398406

399-
// Save protocol refinement relations in a side table.
400-
if (rule.isProtocolRefinementRule()) {
401-
ProtocolRefinements.insert(ruleID);
402-
continue;
403-
}
404-
405407
auto lhs = rule.getLHS();
406408

407409
// Record a parent path if the subject type itself requires a non-trivial
@@ -698,7 +700,7 @@ bool MinimalConformances::isValidConformancePath(
698700
bool MinimalConformances::isValidRefinementPath(
699701
const llvm::SmallVectorImpl<unsigned> &path) const {
700702
for (unsigned ruleID : path) {
701-
if (!System.getRule(ruleID).isProtocolRefinementRule())
703+
if (ProtocolRefinements.count(ruleID) == 0)
702704
return false;
703705
}
704706

@@ -867,8 +869,14 @@ void MinimalConformances::computeMinimalConformances() {
867869
for (const auto &path : paths) {
868870
// Only consider a protocol refinement rule to be redundant if it is
869871
// witnessed by a composition of other protocol refinement rules.
870-
if (isProtocolRefinement && !isValidRefinementPath(path))
872+
if (isProtocolRefinement && !isValidRefinementPath(path)) {
873+
if (Debug.contains(DebugFlags::MinimalConformances)) {
874+
llvm::dbgs() << "Not a refinement path: ";
875+
dumpConformancePath(llvm::errs(), path);
876+
llvm::dbgs() << "\n";
877+
}
871878
continue;
879+
}
872880

873881
llvm::SmallDenseSet<unsigned, 4> visited;
874882
visited.insert(ruleID);

lib/AST/RequirementMachine/Rule.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ bool Rule::isIdentityConformanceRule() const {
9393

9494
/// If this is a rule of the form [P].[Q] => [P] where [P] and [Q] are
9595
/// protocol symbols, return true, otherwise return false.
96-
bool Rule::isProtocolRefinementRule() const {
96+
bool Rule::isProtocolRefinementRule(RewriteContext &ctx) const {
9797
if (LHS.size() == 2 &&
9898
RHS.size() == 1 &&
9999
LHS[0] == RHS[0] &&
@@ -111,7 +111,7 @@ bool Rule::isProtocolRefinementRule() const {
111111
auto *proto = LHS[0].getProtocol();
112112
auto *otherProto = LHS[1].getProtocol();
113113

114-
auto inherited = proto->getInheritedProtocols();
114+
auto inherited = ctx.getInheritedProtocols(proto);
115115
return (std::find(inherited.begin(), inherited.end(), otherProto)
116116
!= inherited.end());
117117
}

lib/AST/RequirementMachine/Rule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class Rule final {
108108

109109
bool isIdentityConformanceRule() const;
110110

111-
bool isProtocolRefinementRule() const;
111+
bool isProtocolRefinementRule(RewriteContext &ctx) const;
112112

113113
bool isCircularConformanceRule() const;
114114

test/Generics/redundant_protocol_refinement.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
// RUN: %target-typecheck-verify-swift
22
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-protocol-signatures=verify %s 2>&1 | %FileCheck %s
33

4-
protocol Base {}
5-
64
// CHECK-LABEL: redundant_protocol_refinement.(file).Base@
75
// CHECK-LABEL: Requirement signature: <Self>
8-
9-
protocol Middle : Base {}
6+
protocol Base {}
107

118
// CHECK-LABEL: redundant_protocol_refinement.(file).Middle@
129
// CHECK-LABEL: Requirement signature: <Self where Self : Base>
10+
protocol Middle : Base {}
1311

12+
// CHECK-LABEL: redundant_protocol_refinement.(file).Derived@
13+
// CHECK-LABEL: Requirement signature: <Self where Self : Middle>
1414
protocol Derived : Middle, Base {}
1515
// expected-note@-1 {{conformance constraint 'Self' : 'Base' implied here}}
1616
// expected-warning@-2 {{redundant conformance constraint 'Self' : 'Base'}}
1717

18-
// CHECK-LABEL: redundant_protocol_refinement.(file).Derived@
18+
// CHECK-LABEL: redundant_protocol_refinement.(file).Derived2@
1919
// CHECK-LABEL: Requirement signature: <Self where Self : Middle>
20+
protocol Derived2 : Middle {}
21+
22+
// CHECK-LABEL: redundant_protocol_refinement.(file).MoreDerived@
23+
// CHECK-LABEL: Requirement signature: <Self where Self : Derived2>
24+
protocol MoreDerived : Derived2, Base {}
25+
// expected-note@-1 {{conformance constraint 'Self' : 'Base' implied here}}
26+
// expected-warning@-2 {{redundant conformance constraint 'Self' : 'Base'}}
2027

2128
protocol P1 {}
2229

0 commit comments

Comments
 (0)