Skip to content

[Requirement Machine] Diagnose redundant requirements. #41664

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 11, 2022

Conversation

hborla
Copy link
Member

@hborla hborla commented Mar 4, 2022

Plumb source location info through the rewrite system by storing the structural requirements used to derive explicit rules, and store the structural requirement ID in the rule itself. Structural requirement IDs are propagated from redundant rules to their replacements via RewriteSystem::propagateRedundantRequirementIDs after homotopy reduction. RewriteSystem::getRedundantRequirements computes the set of redundant requirement diagnostics to be emitted from RequirementSignatureRequestRQM and InferredGenericSignatureRequestRQM.

This change does not (yet) emit notes to show where a requirement is made redundant.

Resolves: rdar://88135415

@hborla hborla requested a review from slavapestov March 4, 2022 06:33
@hborla hborla marked this pull request as ready for review March 8, 2022 06:26
hborla added 6 commits March 9, 2022 12:14
…struction,

and diagnose trivially redundant rules in the rewrite system.
rewrite system.

This ID can be used to index into the WrittenRequirements array in the
rewrite system to retrieve the structural requirement, e.g. for the
purpose of diagnostics.
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.
…edundant

rule has a non-explicit, non-redundant rule in its rewrite path.

This fixes bogus redundancy diagnostics in cases where the canonical form
of a redundant rule is not explicit in source, e.g.

protocol Swappable2 {
  associatedtype A
  associatedtype B
  associatedtype Swapped : Swappable2
    where Swapped.B == A,
          Swapped.Swapped == Self
}

in the above case, the canonical rule for `Swapped.B == A` is the rule
[Swappable2:Swapped].[Swappable2:A] => [Swappable2:B], which is not
explicit.
non-explicit, non-redundant replacement rule is not in the rewrite
system's minimization domain.
Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great!

I have some suggestions for further cleanups, but only two I think are important:

  • Untangling isExplicit() / setRequirementID() since they're now computed in separate places
  • Replacing the std::tuple with a named type

For the rest it's up to you if you want to do them in this PR, another PR or not bother at all.

if (!step.isInContext() && !evaluator.isInContext())
rulesInEmptyContext.insert(step.getRuleID());

++ruleFrequency[step.getRuleID()];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree that 'frequency' is probably better than 'multiplicity' :)

@@ -656,6 +715,8 @@ void RewriteSystem::performHomotopyReduction(

deleteRule(ruleID, replacementPath);
}

propagateRedundantRequirementIDs();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be done once at the end? (performHomotopyReduction() is called three times)

@@ -104,7 +104,12 @@ struct RuleBuilder {

/// New rules derived from requirements written by the user, which can be
/// eliminated by homotopy reduction.
std::vector<std::pair<MutableTerm, MutableTerm>> RequirementRules;
std::vector<std::tuple<MutableTerm, MutableTerm, Optional<unsigned>>>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to define a named type if I need a tuple of length > 2 because std::get<N>() is kind of ugly. Maybe RequirementRule or something?

std::vector<std::tuple<MutableTerm, MutableTerm, Optional<unsigned>>>
RequirementRules;

/// Requirements written in source code. The requirement ID in the above
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These have been desugared at this point right? Might want to note that.

@@ -225,6 +239,12 @@ class RewriteSystem final {
/// top-level generic signature and this array is empty.
ArrayRef<const ProtocolDecl *> Protos;

/// The requirements written in source code.
std::vector<StructuralRequirement> WrittenRequirements;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a similar vein to the Errors vector, I'm wondering if its better to avoid storing this in the rewrite system, and instead pass it down from the request evaluator request to getRedundantRequirements() since it's only used there to get the Requirement and SourceLoc for the diagnostic.


// Map redundant rule IDs to their rewrite path for easy access
// in the `isRedundantRule` lambda.
llvm::SmallDenseMap<unsigned, RewritePath> redundantRules;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt it actually matters for efficiency but it's a bit unfortunate to copy the RewritePath here since it's backed by an std::vector. Since the property of the rewrite path that you use is whether it contains an 'implied rule', can you precompute that in the map value instead?

@hborla hborla force-pushed the redundant-requirements branch from 45cf619 to b5d7a01 Compare March 10, 2022 00:18
hborla added 3 commits March 9, 2022 18:18
…other

diagnostics in the rewrite system, pass down the 'errors' vector from the
top-level requests.
initialization of the rewrite system.

Instead, the rewrite system can determine trivially redundant requirements
by finding structural requirements with no associated rewrite rules.
@hborla
Copy link
Member Author

hborla commented Mar 10, 2022

@swift-ci please smoke test

@hborla
Copy link
Member Author

hborla commented Mar 10, 2022

@swift-ci please smoke test Linux

3 similar comments
@hborla
Copy link
Member Author

hborla commented Mar 10, 2022

@swift-ci please smoke test Linux

@hborla
Copy link
Member Author

hborla commented Mar 10, 2022

@swift-ci please smoke test Linux

@hborla
Copy link
Member Author

hborla commented Mar 11, 2022

@swift-ci please smoke test Linux

@hborla hborla merged commit d3702ba into swiftlang:main Mar 11, 2022
@hborla hborla deleted the redundant-requirements branch March 11, 2022 09:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants