Skip to content

Commit d8215bb

Browse files
committed
RequirementMachine: Rework 'implied rules' computation in computeRedundantRequirementDiagnostics()
For efficiency I want to keep replacement paths for redundant rules unsubstituted, so that earlier replacement paths can reference redundant rules that appear later in the RedundantRules array. Right now we expand replacement paths so that their RewriteSteps only mention non-redundant rules. This patch refactors the computeRedundantRequirementDiagnostics() method a bit: The impliedRequirements set is now named nonExplicitNonRedundantRules, and in addition to storing these rules themselves, the set also stores any _redundant_ rules that reference these rules via their replacement paths. Since this is computing a transitive closure, we walk the RedundantRules array in reverse. A replacement path can only reference a redundant rule if that redundant rule appears later in the array.
1 parent 12095cb commit d8215bb

File tree

1 file changed

+57
-28
lines changed

1 file changed

+57
-28
lines changed

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -610,46 +610,58 @@ void RewriteSystem::verifyRewriteRules(ValidityPolicy policy) const {
610610
/// emit warnings for in the source code.
611611
void RewriteSystem::computeRedundantRequirementDiagnostics(
612612
SmallVectorImpl<RequirementError> &errors) {
613-
// Map redundant rule IDs to their rewrite path for easy access
614-
// in the `isRedundantRule` lambda.
615-
llvm::SmallDenseMap<unsigned, RewritePath> redundantRules;
616-
for (auto &pair : RedundantRules)
617-
redundantRules[pair.first] = pair.second;
618-
619613
// Collect all rule IDs for each unique requirement ID.
620-
llvm::SmallDenseMap<unsigned, llvm::SmallDenseSet<unsigned, 2>>
614+
llvm::SmallDenseMap<unsigned, llvm::SmallVector<unsigned, 2>>
621615
rulesPerRequirement;
622616

623617
// Collect non-explicit requirements that are not redundant.
624-
llvm::SmallDenseSet<unsigned, 2> impliedRequirements;
618+
llvm::SmallDenseSet<unsigned, 2> nonExplicitNonRedundantRules;
625619

626620
for (unsigned ruleID = FirstLocalRule, e = Rules.size();
627621
ruleID < e; ++ruleID) {
628622
auto &rule = getRules()[ruleID];
629623

630-
if (!rule.getRequirementID().hasValue() &&
631-
!rule.isPermanent() && !rule.isRedundant() &&
632-
isInMinimizationDomain(rule.getLHS().getRootProtocol()))
633-
impliedRequirements.insert(ruleID);
624+
if (rule.isPermanent())
625+
continue;
626+
627+
if (!isInMinimizationDomain(rule.getLHS().getRootProtocol()))
628+
continue;
634629

635630
auto requirementID = rule.getRequirementID();
636-
if (!requirementID.hasValue())
631+
632+
if (!requirementID.hasValue()) {
633+
if (!rule.isRedundant())
634+
nonExplicitNonRedundantRules.insert(ruleID);
635+
637636
continue;
637+
}
638638

639-
rulesPerRequirement[*requirementID].insert(ruleID);
639+
rulesPerRequirement[*requirementID].push_back(ruleID);
640640
}
641641

642-
auto isRedundantRule = [&](unsigned ruleID) {
643-
auto &rule = getRules()[ruleID];
644-
645-
// If this rule is replaced using a non-explicit,
646-
// non-redundant rule, it's not redundant.
647-
auto rewritePath = redundantRules[ruleID];
642+
// Compute the set of redundant rules which transitively reference a
643+
// non-explicit non-redundant rule. This updates nonExplicitNonRedundantRules.
644+
//
645+
// Since earlier redundant paths might reference rules which appear later in
646+
// the list but not vice versa, walk the redundant paths in reverse order.
647+
for (const auto &pair : llvm::reverse(RedundantRules)) {
648+
// Pre-condition: the replacement path only references redundant rules
649+
// which we have already seen. If any of those rules transitively reference
650+
// a non-explicit, non-redundant rule, they have been inserted into the
651+
// nonExplicitNonRedundantRules set on previous iterations.
652+
unsigned ruleID = pair.first;
653+
const auto &rewritePath = pair.second;
654+
655+
// Check if this rewrite path references a rule that is already known to
656+
// either be non-explicit and non-redundant, or reference such a rule via
657+
// it's redundancy path.
648658
for (auto step : rewritePath) {
649659
switch (step.Kind) {
650660
case RewriteStep::Rule: {
651-
if (impliedRequirements.count(step.getRuleID()))
652-
return false;
661+
if (nonExplicitNonRedundantRules.count(step.getRuleID())) {
662+
nonExplicitNonRedundantRules.insert(ruleID);
663+
continue;
664+
}
653665

654666
break;
655667
}
@@ -665,20 +677,37 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
665677
}
666678
}
667679

668-
return rule.isRedundant();
680+
// Post-condition: If the current replacement path transitively references
681+
// any non-explicit, non-redundant rules, then nonExplicitNonRedundantRules
682+
// contains the current rule.
683+
}
684+
685+
// We diagnose a redundancy if the rule is redundant, and if its replacement
686+
// path does not transitively involve any non-explicit, non-redundant rules.
687+
auto isRedundantRule = [&](unsigned ruleID) {
688+
const auto &rule = getRules()[ruleID];
689+
690+
return (rule.isRedundant() &&
691+
nonExplicitNonRedundantRules.count(ruleID) == 0);
669692
};
670693

694+
// Finally walk through the written requirements, diagnosing any that are
695+
// redundant.
671696
for (auto requirementID : indices(WrittenRequirements)) {
672697
auto requirement = WrittenRequirements[requirementID];
673-
auto pairIt = rulesPerRequirement.find(requirementID);
674698

675699
// Inferred requirements can be re-stated without warning.
676700
if (requirement.inferred)
677701
continue;
678702

679-
// If there are no rules for this structural requirement, then
680-
// the requirement was never added to the rewrite system because
681-
// it is trivially redundant.
703+
auto pairIt = rulesPerRequirement.find(requirementID);
704+
705+
// If there are no rules for this structural requirement, then the
706+
// requirement is unnecessary in the source code.
707+
//
708+
// This means the requirement was determined to be vacuous by
709+
// requirement lowering and produced no rules, or the rewrite rules were
710+
// trivially simplified by RewriteSystem::addRule().
682711
if (pairIt == rulesPerRequirement.end()) {
683712
errors.push_back(
684713
RequirementError::forRedundantRequirement(requirement.req,
@@ -688,7 +717,7 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
688717

689718
// If all rules derived from this structural requirement are redundant,
690719
// then the requirement is unnecessary in the source code.
691-
auto ruleIDs = pairIt->second;
720+
const auto &ruleIDs = pairIt->second;
692721
if (llvm::all_of(ruleIDs, isRedundantRule)) {
693722
auto requirement = WrittenRequirements[requirementID];
694723
errors.push_back(

0 commit comments

Comments
 (0)