Files
swift-mirror/lib/AST/RequirementMachine/RewriteSystem.h
Slava Pestov 2fc6ec551b RequirementMachine: Use trie for left hand side simplification
In a confluent rewrite system, if the left hand side of a rule
X => Y can be reduced by some other rule X' => Y', then it is
permissible to delete the original rule X => Y altogether.

Confluence means that rewrite rules can be applied in any
order, so it is always valid to apply X' => Y' first, thus
X => Y is obsolete.

This was previously done in the completion procedure via a
quadratic algorithm that attempted to reduce each existing
rule via the newly-added rule obtained by resolving a critical
pair. Instead, we can do this in the post-processing pass
where we reduce right hand sides using a trie to speed up
the lookup.

This increases the amount of work performed by the
completion procedure, but eliminates the quadratic algorithm,
saving time overall.
2021-08-07 01:51:31 -04:00

191 lines
5.5 KiB
C++

//===--- RewriteSystem.h - Generics with term rewriting ---------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_REWRITESYSTEM_H
#define SWIFT_REWRITESYSTEM_H
#include <algorithm>
#include "ProtocolGraph.h"
#include "Symbol.h"
#include "Term.h"
#include "Trie.h"
namespace llvm {
class raw_ostream;
}
namespace swift {
namespace rewriting {
class PropertyMap;
class RewriteContext;
/// A rewrite rule that replaces occurrences of LHS with RHS.
///
/// LHS must be greater than RHS in the linear order over terms.
///
/// Out-of-line methods are documented in RewriteSystem.cpp.
class Rule final {
Term LHS;
Term RHS;
bool deleted;
public:
Rule(Term lhs, Term rhs)
: LHS(lhs), RHS(rhs), deleted(false) {}
const Term &getLHS() const { return LHS; }
const Term &getRHS() const { return RHS; }
OverlapKind checkForOverlap(const Rule &other,
MutableTerm &t,
MutableTerm &v) const {
return LHS.checkForOverlap(other.LHS, t, v);
}
/// Returns if the rule was deleted.
bool isDeleted() const {
return deleted;
}
/// Deletes the rule, which removes it from consideration in term
/// simplification and completion. Deleted rules are simply marked as
/// such instead of being physically removed from the rules vector
/// in the rewrite system, to ensure that indices remain valid across
/// deletion.
void markDeleted() {
assert(!deleted);
deleted = true;
}
/// Returns the length of the left hand side.
unsigned getDepth() const {
return LHS.size();
}
void dump(llvm::raw_ostream &out) const;
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &out,
const Rule &rule) {
rule.dump(out);
return out;
}
};
/// A term rewrite system for working with types in a generic signature.
///
/// Out-of-line methods are documented in RewriteSystem.cpp.
class RewriteSystem final {
/// Rewrite context for memory allocation.
RewriteContext &Context;
/// The rules added so far, including rules from our client, as well
/// as rules introduced by the completion procedure.
std::vector<Rule> Rules;
/// A prefix trie of rule left hand sides to optimize lookup. The value
/// type is an index into the Rules array defined above.
Trie<unsigned, MatchKind::Shortest> Trie;
/// The graph of all protocols transitively referenced via our set of
/// rewrite rules, used for the linear order on symbols.
ProtocolGraph Protos;
/// A list of pending terms for the associated type merging completion
/// heuristic.
///
/// The pair (lhs, rhs) satisfies the following conditions:
/// - lhs > rhs
/// - all symbols but the last are pair-wise equal in lhs and rhs
/// - the last symbol in both lhs and rhs is an associated type symbol
/// - the last symbol in both lhs and rhs has the same name
///
/// See RewriteSystem::processMergedAssociatedTypes() for details.
std::vector<std::pair<MutableTerm, MutableTerm>> MergedAssociatedTypes;
/// A list of pending pairs for checking overlap in the completion
/// procedure.
std::deque<std::pair<unsigned, unsigned>> Worklist;
/// Set these to true to enable debugging output.
unsigned DebugSimplify : 1;
unsigned DebugAdd : 1;
unsigned DebugMerge : 1;
unsigned DebugCompletion : 1;
public:
explicit RewriteSystem(RewriteContext &ctx);
~RewriteSystem();
RewriteSystem(const RewriteSystem &) = delete;
RewriteSystem(RewriteSystem &&) = delete;
RewriteSystem &operator=(const RewriteSystem &) = delete;
RewriteSystem &operator=(RewriteSystem &&) = delete;
/// Return the rewrite context used for allocating memory.
RewriteContext &getRewriteContext() const { return Context; }
/// Return the object recording information about known protocols.
const ProtocolGraph &getProtocols() const { return Protos; }
void initialize(std::vector<std::pair<MutableTerm, MutableTerm>> &&rules,
ProtocolGraph &&protos);
Symbol simplifySubstitutionsInSuperclassOrConcreteSymbol(Symbol symbol) const;
bool addRule(MutableTerm lhs, MutableTerm rhs);
bool simplify(MutableTerm &term) const;
enum class CompletionResult {
/// Confluent completion was computed successfully.
Success,
/// Maximum number of iterations reached.
MaxIterations,
/// Completion produced a rewrite rule whose left hand side has a length
/// exceeding the limit.
MaxDepth
};
std::pair<CompletionResult, unsigned>
computeConfluentCompletion(unsigned maxIterations,
unsigned maxDepth);
void simplifyRewriteSystem();
void verify() const;
std::pair<CompletionResult, unsigned>
buildPropertyMap(PropertyMap &map,
unsigned maxIterations,
unsigned maxDepth);
void dump(llvm::raw_ostream &out) const;
private:
Optional<std::pair<MutableTerm, MutableTerm>>
computeCriticalPair(const Rule &lhs, const Rule &rhs) const;
Symbol mergeAssociatedTypes(Symbol lhs, Symbol rhs) const;
void processMergedAssociatedTypes();
};
} // end namespace rewriting
} // end namespace swift
#endif