Skip to content

Commit a154641

Browse files
committed
[RequirementMachine] Propagate explicit requirement IDs from redundant
rules in a separate pass after homotopy reduction. RewriteSystem::propagateExplicitBits was too eager in propagating IDs from explicit rules within rewrite loops, which resulted in bogus redundancy warnings when the canonical form of an explicit rule was given a different requirement ID. Instead, propagate requirement IDs after homotopy reduction when redundant rules are computed and rewrite loops are simplified.
1 parent be30d76 commit a154641

File tree

6 files changed

+119
-12
lines changed

6 files changed

+119
-12
lines changed

lib/AST/RequirementMachine/Debug.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,11 @@ enum class DebugFlags : unsigned {
6464
RedundantRulesDetail = (1<<13),
6565

6666
/// Print debug output from the concrete contraction pre-processing pass.
67-
ConcreteContraction = (1<<14)
67+
ConcreteContraction = (1<<14),
68+
69+
/// Print debug output from propagating explicit requirement
70+
/// IDs from redundant rules.
71+
PropagateRequirementIDs = (1<<15),
6872
};
6973

7074
using DebugOptions = OptionSet<DebugFlags>;

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -195,24 +195,100 @@ void RewriteSystem::propagateExplicitBits() {
195195
loop.findRulesAppearingOnceInEmptyContext(*this);
196196

197197
bool sawExplicitRule = false;
198-
Optional<unsigned> requirementID = None;
199198

200199
for (unsigned ruleID : rulesInEmptyContext) {
201200
const auto &rule = getRule(ruleID);
202-
if (rule.isExplicit()) {
201+
if (rule.isExplicit())
203202
sawExplicitRule = true;
204-
requirementID = rule.getRequirementID();
205-
break;
206-
}
207203
}
208204
if (sawExplicitRule) {
209205
for (unsigned ruleID : rulesInEmptyContext) {
210206
auto &rule = getRule(ruleID);
211207
if (!rule.isPermanent() && !rule.isExplicit())
212-
rule.markExplicit(requirementID);
208+
rule.markExplicit(None);
209+
}
210+
}
211+
}
212+
}
213+
214+
/// Propagate requirement IDs from redundant rules to their
215+
/// replacements that appear once in empty context.
216+
void RewriteSystem::propagateRedundantRequirementIDs() {
217+
if (Debug.contains(DebugFlags::PropagateRequirementIDs)) {
218+
llvm::dbgs() << "\nPropagating requirement IDs: {";
219+
}
220+
221+
for (auto ruleAndReplacement : RedundantRules) {
222+
auto ruleID = ruleAndReplacement.first;
223+
auto rewritePath = ruleAndReplacement.second;
224+
auto &rule = Rules[ruleID];
225+
226+
auto requirementID = rule.getRequirementID();
227+
if (!requirementID.hasValue())
228+
continue;
229+
230+
// FIXME: This code is almost identical to `RewriteLoop::recompute`.
231+
232+
// Rules appearing in empty context (possibly more than once).
233+
llvm::SmallDenseSet<unsigned, 2> rulesInEmptyContext;
234+
// The number of times each rule appears (with or without context).
235+
llvm::SmallDenseMap<unsigned, unsigned, 2> ruleFrequency;
236+
237+
RewritePathEvaluator evaluator(MutableTerm(rule.getLHS()));
238+
for (auto step : rewritePath) {
239+
switch (step.Kind) {
240+
case RewriteStep::Rule: {
241+
if (!step.isInContext() && !evaluator.isInContext())
242+
rulesInEmptyContext.insert(step.getRuleID());
243+
244+
++ruleFrequency[step.getRuleID()];
245+
break;
246+
}
247+
248+
case RewriteStep::LeftConcreteProjection:
249+
case RewriteStep::Decompose:
250+
case RewriteStep::PrefixSubstitutions:
251+
case RewriteStep::Shift:
252+
case RewriteStep::Relation:
253+
case RewriteStep::DecomposeConcrete:
254+
case RewriteStep::RightConcreteProjection:
255+
break;
256+
}
257+
258+
evaluator.apply(step, *this);
259+
}
260+
261+
// Collect all rules that we saw exactly once in empty context.
262+
SmallVector<unsigned, 1> rulesOnceInEmptyContext;
263+
for (auto rule : rulesInEmptyContext) {
264+
auto found = ruleFrequency.find(rule);
265+
assert(found != ruleFrequency.end());
266+
267+
if (found->second == 1)
268+
rulesOnceInEmptyContext.push_back(rule);
269+
}
270+
271+
for (auto ruleID : rulesOnceInEmptyContext) {
272+
auto &replacement = Rules[ruleID];
273+
if (!replacement.isPermanent() &&
274+
!replacement.getRequirementID().hasValue()) {
275+
if (Debug.contains(DebugFlags::PropagateRequirementIDs)) {
276+
llvm::dbgs() << "\n- propagating ID = "
277+
<< requirementID
278+
<< "\n from ";
279+
rule.dump(llvm::dbgs());
280+
llvm::dbgs() << "\n to ";
281+
replacement.dump(llvm::dbgs());
282+
}
283+
284+
replacement.markExplicit(requirementID);
213285
}
214286
}
215287
}
288+
289+
if (Debug.contains(DebugFlags::PropagateRequirementIDs)) {
290+
llvm::dbgs() << "\n}\n";
291+
}
216292
}
217293

218294
/// After propagating the 'explicit' bit on rules, process pairs of
@@ -658,7 +734,7 @@ void RewriteSystem::performHomotopyReduction(
658734

659735
// If no redundant rules remain which can be eliminated by this pass, stop.
660736
if (!optPair)
661-
return;
737+
break;
662738

663739
unsigned loopID = optPair->first;
664740
unsigned ruleID = optPair->second;
@@ -683,6 +759,8 @@ void RewriteSystem::performHomotopyReduction(
683759

684760
deleteRule(ruleID, replacementPath);
685761
}
762+
763+
propagateRedundantRequirementIDs();
686764
}
687765

688766
void RewriteSystem::normalizeRedundantRules() {

lib/AST/RequirementMachine/RewriteContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ static DebugOptions parseDebugFlags(StringRef debugFlags) {
4242
.Case("redundant-rules", DebugFlags::RedundantRules)
4343
.Case("redundant-rules-detail", DebugFlags::RedundantRulesDetail)
4444
.Case("concrete-contraction", DebugFlags::ConcreteContraction)
45+
.Case("propagate-requirement-ids", DebugFlags::PropagateRequirementIDs)
4546
.Default(None);
4647
if (!flag) {
4748
llvm::errs() << "Unknown debug flag in -debug-requirement-machine "

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ void RewriteSystem::dump(llvm::raw_ostream &out) const {
770770
out << "- " << rule;
771771
if (auto ID = rule.getRequirementID()) {
772772
auto requirement = WrittenRequirements[*ID];
773-
out << ", ";
773+
out << ", ID: " << *ID << ", ";
774774
requirement.req.dump(out);
775775
out << " at ";
776776
requirement.loc.print(out, Context.getASTContext().SourceMgr);

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,9 @@ class Rule final {
177177
}
178178

179179
void markExplicit(Optional<unsigned> requirementID) {
180-
assert(!Explicit && !Permanent &&
180+
assert((!Explicit || requirementID.hasValue()) &&
181+
"Rule is already explicit");
182+
assert(!Permanent &&
181183
"Permanent and explicit are mutually exclusive");
182184
this->requirementID = requirementID;
183185
Explicit = true;
@@ -505,6 +507,8 @@ class RewriteSystem final {
505507

506508
void propagateExplicitBits();
507509

510+
void propagateRedundantRequirementIDs();
511+
508512
void processConflicts();
509513

510514
Optional<std::pair<unsigned, unsigned>>

test/Generics/requirement_machine_diagnostics.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,28 @@ protocol P7 : P6 {
152152
associatedtype AssocP7: P6
153153
}
154154

155-
extension P7 where AssocP6.Element : P6,
156-
AssocP7.AssocP6.Element : P6, // expected-warning{{redundant conformance constraint 'Self.AssocP7.AssocP6.Element' : 'P6'}}
155+
extension P7 where AssocP6.Element : P6, // expected-warning{{redundant conformance constraint 'Self.AssocP6.Element' : 'P6'}}
156+
AssocP7.AssocP6.Element : P6,
157157
AssocP6.Element == AssocP7.AssocP6.Element {
158158
func nestedSameType1() { }
159159
}
160+
161+
protocol P8 {
162+
associatedtype A
163+
associatedtype B
164+
}
165+
166+
protocol P9 : P8 {
167+
associatedtype A
168+
associatedtype B
169+
}
170+
171+
protocol P10 {
172+
associatedtype A
173+
associatedtype C
174+
}
175+
176+
class X3 { }
177+
178+
func sameTypeConcrete2<T : P9 & P10>(_: T) where T.B : X3, T.C == T.B, T.C == X3 { }
179+
// expected-warning@-1{{redundant superclass constraint 'T.B' : 'X3'}}

0 commit comments

Comments
 (0)