mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #39629 from slavapestov/rqm-protocol-signatures
RequirementMachine: Wire up protocol requirement signature minimization
This commit is contained in:
@@ -481,6 +481,11 @@ namespace swift {
|
||||
/// algorithm.
|
||||
unsigned RequirementMachineDepthLimit = 10;
|
||||
|
||||
/// Enable the new experimental protocol requirement signature minimization
|
||||
/// algorithm.
|
||||
RequirementMachineMode RequirementMachineProtocolSignatures =
|
||||
RequirementMachineMode::Disabled;
|
||||
|
||||
/// Sets the target we are building for and updates platform conditions
|
||||
/// to match.
|
||||
///
|
||||
|
||||
@@ -616,6 +616,10 @@ def requirement_machine_EQ : Joined<["-"], "requirement-machine=">,
|
||||
Flags<[FrontendOption, ModuleInterfaceOption]>,
|
||||
HelpText<"Control usage of experimental generics implementation: 'on', 'off', or 'verify'">;
|
||||
|
||||
def requirement_machine_protocol_signatures_EQ : Joined<["-"], "requirement-machine-protocol-signatures=">,
|
||||
Flags<[FrontendOption]>,
|
||||
HelpText<"Control usage of experimental protocol requirement signature minimization: 'on', 'off', or 'verify'">;
|
||||
|
||||
// Diagnostic control options
|
||||
def suppress_warnings : Flag<["-"], "suppress-warnings">,
|
||||
Flags<[FrontendOption]>,
|
||||
|
||||
@@ -1483,11 +1483,7 @@ int Requirement::compare(const Requirement &other) const {
|
||||
// We should only have multiple conformance requirements.
|
||||
assert(getKind() == RequirementKind::Conformance);
|
||||
|
||||
int compareProtos =
|
||||
TypeDecl::compare(getProtocolDecl(), other.getProtocolDecl());
|
||||
|
||||
assert(compareProtos != 0 && "Duplicate conformance requirement");
|
||||
return compareProtos;
|
||||
return TypeDecl::compare(getProtocolDecl(), other.getProtocolDecl());
|
||||
}
|
||||
|
||||
/// Compare two associated types.
|
||||
|
||||
@@ -34,6 +34,26 @@
|
||||
// decompositions can be found for all "derived" conformance rules, producing
|
||||
// a minimal set of generating conformances.
|
||||
//
|
||||
// There are two small complications to handle implementation details of
|
||||
// Swift generics:
|
||||
//
|
||||
// 1) Inherited witness tables must be derivable by following other protocol
|
||||
// refinement requirements only, without looking at non-Self associated
|
||||
// types. This is expressed by saying that the generating conformance
|
||||
// equations for a protocol refinement can only be written in terms of
|
||||
// other protocol refinements; conformance paths involving non-Self
|
||||
// associated types are not considered.
|
||||
//
|
||||
// 2) The subject type of each conformance requirement must be derivable at
|
||||
// runtime as well, so for each generating conformance, it must be
|
||||
// possible to write down a conformance path for the parent type without
|
||||
// using any generating conformance recursively in the parent path of
|
||||
// itself.
|
||||
//
|
||||
// The generating conformances finds fewer conformance requirements to be
|
||||
// redundant than homotopy reduction, which is why homotopy reduction only
|
||||
// deletes non-protocol conformance requirements.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "swift/AST/Decl.h"
|
||||
@@ -283,23 +303,48 @@ void RewriteSystem::computeCandidateConformancePaths(
|
||||
llvm::dbgs() << "\n";
|
||||
}
|
||||
|
||||
// Two conformance rules in empty context (T.[P] => T) and (T'.[P] => T)
|
||||
// are interchangeable, and contribute a trivial pair of conformance
|
||||
// equations expressing that each one can be written in terms of the
|
||||
// other:
|
||||
//
|
||||
// (T.[P] => T) := (T'.[P])
|
||||
// (T'.[P] => T') := (T.[P])
|
||||
for (unsigned candidateRuleID : notInContext) {
|
||||
for (unsigned otherRuleID : notInContext) {
|
||||
if (otherRuleID == candidateRuleID)
|
||||
continue;
|
||||
|
||||
SmallVector<unsigned, 2> path;
|
||||
path.push_back(otherRuleID);
|
||||
conformancePaths[candidateRuleID].push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Suppose a 3-cell contains a conformance rule (T.[P] => T) in an empty
|
||||
// context, and a conformance rule (V.[P] => V) with a possibly non-empty
|
||||
// left context U and empty right context.
|
||||
// context, and a conformance rule (V.[P] => V) with a non-empty left
|
||||
// context U.
|
||||
//
|
||||
// The 3-cell looks something like this:
|
||||
//
|
||||
// ... ⊗ (T.[P] => T) ⊗ ... ⊗ U.(V => V.[P]) ⊗ ...
|
||||
// ^ ^
|
||||
// | |
|
||||
// + basepoint ========================= basepoint +
|
||||
//
|
||||
// We can decompose U into a product of conformance rules:
|
||||
//
|
||||
// (V1.[P1] => V1)...(Vn.[Pn] => Vn),
|
||||
//
|
||||
// Note that (V1)...(Vn) is canonically equivalent to U.
|
||||
//
|
||||
// Now, we can record a candidate decomposition of (T.[P] => T) as a
|
||||
// product of conformance rules:
|
||||
//
|
||||
// (T.[P] => T) := (V1.[P1] => V1)...(Vn.[Pn] => Vn).(V.[P] => V)
|
||||
//
|
||||
// Now if U is empty, this becomes the trivial candidate:
|
||||
//
|
||||
// (T.[P] => T) := (V.[P] => V)
|
||||
SmallVector<SmallVector<unsigned, 2>, 2> candidatePaths;
|
||||
// Again, note that (V1)...(Vn).V is canonically equivalent to U.V,
|
||||
// and therefore T.
|
||||
for (auto pair : inContext) {
|
||||
// We have a term U, and a rule V.[P] => V.
|
||||
SmallVector<unsigned, 2> conformancePath;
|
||||
@@ -313,26 +358,10 @@ void RewriteSystem::computeCandidateConformancePaths(
|
||||
decomposeTermIntoConformanceRuleLeftHandSides(term, pair.second,
|
||||
conformancePath);
|
||||
|
||||
candidatePaths.push_back(conformancePath);
|
||||
}
|
||||
|
||||
for (unsigned candidateRuleID : notInContext) {
|
||||
// If multiple conformance rules appear in an empty context, each one
|
||||
// can be replaced with any other conformance rule.
|
||||
for (unsigned otherRuleID : notInContext) {
|
||||
if (otherRuleID == candidateRuleID)
|
||||
continue;
|
||||
|
||||
SmallVector<unsigned, 2> path;
|
||||
path.push_back(otherRuleID);
|
||||
conformancePaths[candidateRuleID].push_back(path);
|
||||
}
|
||||
|
||||
// If conformance rules appear in non-empty context, they define a
|
||||
// conformance access path for each conformance rule in empty context.
|
||||
for (const auto &path : candidatePaths) {
|
||||
conformancePaths[candidateRuleID].push_back(path);
|
||||
}
|
||||
// This decomposition defines a conformance access path for each
|
||||
// conformance rule we saw in empty context.
|
||||
for (unsigned otherRuleID : notInContext)
|
||||
conformancePaths[otherRuleID].push_back(conformancePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -492,6 +521,48 @@ void RewriteSystem::verifyGeneratingConformanceEquations(
|
||||
#endif
|
||||
}
|
||||
|
||||
static const ProtocolDecl *getParentConformanceForTerm(Term lhs) {
|
||||
// The last element is a protocol symbol, because this is the left hand side
|
||||
// of a conformance rule.
|
||||
assert(lhs.back().getKind() == Symbol::Kind::Protocol);
|
||||
|
||||
// The second to last symbol is either an associated type, protocol or generic
|
||||
// parameter symbol.
|
||||
assert(lhs.size() >= 2);
|
||||
|
||||
auto parentSymbol = lhs[lhs.size() - 2];
|
||||
|
||||
switch (parentSymbol.getKind()) {
|
||||
case Symbol::Kind::AssociatedType: {
|
||||
// In a conformance rule of the form [P:T].[Q] => [P:T], the parent type is
|
||||
// trivial.
|
||||
if (lhs.size() == 2)
|
||||
return nullptr;
|
||||
|
||||
// If we have a rule of the form X.[P:Y].[Q] => X.[P:Y] wih non-empty X,
|
||||
// then the parent type is X.[P].
|
||||
const auto protos = parentSymbol.getProtocols();
|
||||
assert(protos.size() == 1);
|
||||
|
||||
return protos[0];
|
||||
}
|
||||
|
||||
case Symbol::Kind::GenericParam:
|
||||
case Symbol::Kind::Protocol:
|
||||
// The parent type is trivial (either a generic parameter, or the protocol
|
||||
// 'Self' type).
|
||||
return nullptr;
|
||||
|
||||
case Symbol::Kind::Name:
|
||||
case Symbol::Kind::Layout:
|
||||
case Symbol::Kind::Superclass:
|
||||
case Symbol::Kind::ConcreteType:
|
||||
break;
|
||||
}
|
||||
|
||||
llvm_unreachable("Bad symbol kind");
|
||||
}
|
||||
|
||||
/// Computes a minimal set of generating conformances, assuming that homotopy
|
||||
/// reduction has already eliminated all redundant rewrite rules that are not
|
||||
/// conformance rules.
|
||||
@@ -536,49 +607,21 @@ void RewriteSystem::computeGeneratingConformances(
|
||||
|
||||
auto lhs = rule.getLHS();
|
||||
|
||||
auto parentSymbol = lhs[lhs.size() - 2];
|
||||
|
||||
// The last element is a protocol symbol, because this is a conformance rule.
|
||||
// The second to last symbol is either an associated type, protocol or generic
|
||||
// parameter symbol.
|
||||
switch (parentSymbol.getKind()) {
|
||||
case Symbol::Kind::AssociatedType: {
|
||||
// If we have a rule of the form X.[P:Y].[Q] => X.[P:Y] wih non-empty X,
|
||||
// then the parent type is X.[P].
|
||||
if (lhs.size() == 2)
|
||||
continue;
|
||||
|
||||
// Record a parent path if the subject type itself requires a non-trivial
|
||||
// conformance path to derive.
|
||||
if (auto *parentProto = getParentConformanceForTerm(lhs)) {
|
||||
MutableTerm mutTerm(lhs.begin(), lhs.end() - 2);
|
||||
assert(!mutTerm.empty());
|
||||
|
||||
const auto protos = parentSymbol.getProtocols();
|
||||
assert(protos.size() == 1);
|
||||
|
||||
bool simplified = simplify(mutTerm);
|
||||
assert(!simplified || rule.isSimplified());
|
||||
(void) simplified;
|
||||
|
||||
mutTerm.add(Symbol::forProtocol(protos[0], Context));
|
||||
mutTerm.add(Symbol::forProtocol(parentProto, Context));
|
||||
|
||||
// Get a conformance path for X.[P] and record it.
|
||||
decomposeTermIntoConformanceRuleLeftHandSides(mutTerm, parentPaths[ruleID]);
|
||||
continue;
|
||||
}
|
||||
|
||||
case Symbol::Kind::GenericParam:
|
||||
case Symbol::Kind::Protocol:
|
||||
// Don't record a parent path, since the parent type is trivial (either a
|
||||
// generic parameter, or the protocol 'Self' type).
|
||||
continue;
|
||||
|
||||
case Symbol::Kind::Name:
|
||||
case Symbol::Kind::Layout:
|
||||
case Symbol::Kind::Superclass:
|
||||
case Symbol::Kind::ConcreteType:
|
||||
break;
|
||||
}
|
||||
|
||||
llvm_unreachable("Bad symbol kind");
|
||||
}
|
||||
|
||||
computeCandidateConformancePaths(conformancePaths);
|
||||
|
||||
@@ -751,7 +751,7 @@ void RewriteSystem::performHomotopyReduction(
|
||||
redundantConformances,
|
||||
replacementPath);
|
||||
|
||||
// If there no redundant rules remain in this pass, stop.
|
||||
// If no redundant rules remain which can be eliminated by this pass, stop.
|
||||
if (!optRuleID)
|
||||
return;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ using namespace rewriting;
|
||||
void ProtocolGraph::visitRequirements(ArrayRef<Requirement> reqs) {
|
||||
for (auto req : reqs) {
|
||||
if (req.getKind() == RequirementKind::Conformance) {
|
||||
addProtocol(req.getProtocolDecl());
|
||||
addProtocol(req.getProtocolDecl(), /*initialComponent=*/false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ void ProtocolGraph::visitRequirements(ArrayRef<Requirement> reqs) {
|
||||
/// \p protos.
|
||||
void ProtocolGraph::visitProtocols(ArrayRef<const ProtocolDecl *> protos) {
|
||||
for (auto proto : protos) {
|
||||
addProtocol(proto);
|
||||
addProtocol(proto, /*initialComponent=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +50,15 @@ const ProtocolInfo &ProtocolGraph::getProtocolInfo(
|
||||
}
|
||||
|
||||
/// Record information about a protocol if we have no seen it yet.
|
||||
void ProtocolGraph::addProtocol(const ProtocolDecl *proto) {
|
||||
void ProtocolGraph::addProtocol(const ProtocolDecl *proto,
|
||||
bool initialComponent) {
|
||||
if (Info.count(proto) > 0)
|
||||
return;
|
||||
|
||||
Info[proto] = {proto->getInheritedProtocols(),
|
||||
proto->getAssociatedTypeMembers(),
|
||||
proto->getRequirementSignature()};
|
||||
proto->getProtocolDependencies(),
|
||||
initialComponent};
|
||||
Protocols.push_back(proto);
|
||||
}
|
||||
|
||||
@@ -66,7 +68,9 @@ void ProtocolGraph::computeTransitiveClosure() {
|
||||
unsigned i = 0;
|
||||
while (i < Protocols.size()) {
|
||||
auto *proto = Protocols[i++];
|
||||
visitRequirements(getProtocolInfo(proto).Requirements);
|
||||
for (auto *proto : getProtocolInfo(proto).Dependencies) {
|
||||
addProtocol(proto, /*initialComponent=*/false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ struct ProtocolInfo {
|
||||
/// ProtocolGraph::computeInheritedAssociatedTypes().
|
||||
llvm::TinyPtrVector<AssociatedTypeDecl *> InheritedAssociatedTypes;
|
||||
|
||||
/// The protocol's requirement signature.
|
||||
ArrayRef<Requirement> Requirements;
|
||||
/// The protocol's dependencies.
|
||||
ArrayRef<ProtocolDecl *> Dependencies;
|
||||
|
||||
/// Used by ProtocolGraph::computeProtocolDepth() to detect circularity.
|
||||
unsigned Mark : 1;
|
||||
@@ -55,27 +55,34 @@ struct ProtocolInfo {
|
||||
|
||||
/// Index of the protocol in the linear order. Computed by
|
||||
/// ProtocolGraph::computeLinearOrder().
|
||||
unsigned Index : 32;
|
||||
unsigned Index : 31;
|
||||
|
||||
/// When building a protocol requirement signature, the initial set of
|
||||
/// protocols are marked with this bit.
|
||||
unsigned InitialComponent : 1;
|
||||
|
||||
ProtocolInfo() {
|
||||
Mark = 0;
|
||||
Depth = 0;
|
||||
Index = 0;
|
||||
InitialComponent = 0;
|
||||
}
|
||||
|
||||
ProtocolInfo(ArrayRef<ProtocolDecl *> inherited,
|
||||
ArrayRef<AssociatedTypeDecl *> &&types,
|
||||
ArrayRef<Requirement> reqs)
|
||||
ArrayRef<ProtocolDecl *> deps,
|
||||
bool initialComponent)
|
||||
: Inherited(inherited),
|
||||
AssociatedTypes(types),
|
||||
Requirements(reqs) {
|
||||
Dependencies(deps) {
|
||||
Mark = 0;
|
||||
Depth = 0;
|
||||
Index = 0;
|
||||
InitialComponent = initialComponent;
|
||||
}
|
||||
};
|
||||
|
||||
/// Stores cached information about all protocols transtively
|
||||
/// Stores cached information about all protocols transitively
|
||||
/// referenced from a set of generic requirements.
|
||||
///
|
||||
/// Out-of-line methods are documented in ProtocolGraph.cpp.
|
||||
@@ -101,7 +108,8 @@ public:
|
||||
const ProtocolDecl *proto) const;
|
||||
|
||||
private:
|
||||
void addProtocol(const ProtocolDecl *proto);
|
||||
void addProtocol(const ProtocolDecl *proto,
|
||||
bool initialComponent);
|
||||
void computeTransitiveClosure();
|
||||
void computeLinearOrder();
|
||||
void computeInheritedAssociatedTypes();
|
||||
|
||||
@@ -243,8 +243,20 @@ void RewriteSystemBuilder::processProtocolDependencies() {
|
||||
for (auto *assocType : info.InheritedAssociatedTypes)
|
||||
addAssociatedType(assocType, proto);
|
||||
|
||||
for (auto req : info.Requirements)
|
||||
// If this protocol is part of the initial connected component, we're
|
||||
// building requirement signatures for all protocols in this component,
|
||||
// and so we must start with the structural requirements.
|
||||
//
|
||||
// Otherwise, we should either already have a requirement signature, or
|
||||
// we can trigger the computation of the requirement signatures of the
|
||||
// next component recursively.
|
||||
if (info.InitialComponent) {
|
||||
for (auto req : proto->getStructuralRequirements())
|
||||
addRequirement(req.req.getCanonical(), proto);
|
||||
} else {
|
||||
for (auto req : proto->getRequirementSignature())
|
||||
addRequirement(req.getCanonical(), proto);
|
||||
}
|
||||
|
||||
if (Dump) {
|
||||
llvm::dbgs() << "}\n";
|
||||
@@ -397,7 +409,8 @@ void RequirementMachine::initWithGenericSignature(CanGenericSignature sig) {
|
||||
|
||||
// Add the initial set of rewrite rules to the rewrite system, also
|
||||
// providing the protocol graph to use for the linear order on terms.
|
||||
System.initialize(std::move(builder.AssociatedTypeRules),
|
||||
System.initialize(/*recordHomotopyGenerators=*/false,
|
||||
std::move(builder.AssociatedTypeRules),
|
||||
std::move(builder.RequirementRules),
|
||||
std::move(builder.Protocols));
|
||||
|
||||
@@ -438,7 +451,8 @@ void RequirementMachine::initWithProtocols(ArrayRef<const ProtocolDecl *> protos
|
||||
|
||||
// Add the initial set of rewrite rules to the rewrite system, also
|
||||
// providing the protocol graph to use for the linear order on terms.
|
||||
System.initialize(std::move(builder.AssociatedTypeRules),
|
||||
System.initialize(/*recordHomotopyGenerators=*/true,
|
||||
std::move(builder.AssociatedTypeRules),
|
||||
std::move(builder.RequirementRules),
|
||||
std::move(builder.Protocols));
|
||||
|
||||
@@ -522,108 +536,3 @@ void RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy)
|
||||
bool RequirementMachine::isComplete() const {
|
||||
return Complete;
|
||||
}
|
||||
|
||||
/// Convert a list of non-permanent, non-redundant rewrite rules into a minimal
|
||||
/// protocol requirement signature for \p proto. The requirements are sorted in
|
||||
/// canonical order, and same-type requirements are canonicalized.
|
||||
std::vector<Requirement>
|
||||
RequirementMachine::buildRequirementSignature(ArrayRef<unsigned> rules,
|
||||
const ProtocolDecl *proto) const {
|
||||
std::vector<Requirement> reqs;
|
||||
llvm::SmallDenseMap<TypeBase *, llvm::SmallVector<Type, 2>> sameTypeReqs;
|
||||
|
||||
auto genericParams = proto->getGenericSignature().getGenericParams();
|
||||
|
||||
// Convert a rewrite rule into a requirement.
|
||||
auto createRequirementFromRule = [&](const Rule &rule) {
|
||||
if (auto prop = rule.isPropertyRule()) {
|
||||
auto subjectType = Context.getTypeForTerm(rule.getRHS(), genericParams,
|
||||
System.getProtocols());
|
||||
|
||||
switch (prop->getKind()) {
|
||||
case Symbol::Kind::Protocol:
|
||||
reqs.emplace_back(RequirementKind::Conformance,
|
||||
subjectType,
|
||||
prop->getProtocol()->getDeclaredInterfaceType());
|
||||
return;
|
||||
|
||||
case Symbol::Kind::Layout:
|
||||
case Symbol::Kind::ConcreteType:
|
||||
case Symbol::Kind::Superclass:
|
||||
// FIXME
|
||||
return;
|
||||
|
||||
case Symbol::Kind::Name:
|
||||
case Symbol::Kind::AssociatedType:
|
||||
case Symbol::Kind::GenericParam:
|
||||
break;
|
||||
}
|
||||
|
||||
llvm_unreachable("Invalid symbol kind");
|
||||
} else if (rule.getLHS().back().getKind() != Symbol::Kind::Protocol) {
|
||||
auto constraintType = Context.getTypeForTerm(rule.getLHS(), genericParams,
|
||||
System.getProtocols());
|
||||
auto subjectType = Context.getTypeForTerm(rule.getRHS(), genericParams,
|
||||
System.getProtocols());
|
||||
|
||||
sameTypeReqs[subjectType.getPointer()].push_back(constraintType);
|
||||
}
|
||||
};
|
||||
|
||||
// Build the list of requirements, storing same-type requirements off
|
||||
// to the side.
|
||||
for (unsigned ruleID : rules) {
|
||||
const auto &rule = System.getRule(ruleID);
|
||||
createRequirementFromRule(rule);
|
||||
}
|
||||
|
||||
// A set of rewrite rules of the form:
|
||||
//
|
||||
// B => A
|
||||
// C => A
|
||||
// D => A
|
||||
//
|
||||
// Become a series of same-type requirements
|
||||
//
|
||||
// A == B, B == C, C == D
|
||||
//
|
||||
for (auto &pair : sameTypeReqs) {
|
||||
std::sort(pair.second.begin(), pair.second.end(),
|
||||
[](Type first, Type second) -> bool {
|
||||
return compareDependentTypes(first, second) < 0;
|
||||
});
|
||||
|
||||
Type subjectType(pair.first);
|
||||
for (auto constraintType : pair.second) {
|
||||
reqs.emplace_back(RequirementKind::SameType, subjectType, constraintType);
|
||||
subjectType = constraintType;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the requirements in canonical order.
|
||||
std::sort(reqs.begin(), reqs.end(),
|
||||
[](const Requirement &lhs, const Requirement &rhs) -> bool {
|
||||
return lhs.compare(rhs) < 0;
|
||||
});
|
||||
|
||||
return reqs;
|
||||
}
|
||||
|
||||
/// Builds the requirement signatures for each protocol in this strongly
|
||||
/// connected component.
|
||||
llvm::DenseMap<const ProtocolDecl *, std::vector<Requirement>>
|
||||
RequirementMachine::computeMinimalRequirements() {
|
||||
assert(Protos.size() > 0);
|
||||
System.minimizeRewriteSystem();
|
||||
|
||||
auto rules = System.getMinimizedRules(Protos);
|
||||
|
||||
// Note that we build 'result' by iterating over 'Protos' rather than
|
||||
// 'rules'; this is intentional, so that even if a protocol has no
|
||||
// rules, we still end up creating an entry for it in 'result'.
|
||||
llvm::DenseMap<const ProtocolDecl *, std::vector<Requirement>> result;
|
||||
for (const auto *proto : Protos)
|
||||
result[proto] = buildRequirementSignature(rules[proto], proto);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -10,19 +10,28 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the main entry points into the requirement machine
|
||||
// via the request evaluator.
|
||||
// This file implements the main entry points for computing minimized generic
|
||||
// signatures using the requirement machine via the request evaluator.
|
||||
//
|
||||
// The actual logic for finding a minimal set of rewrite rules is implemented in
|
||||
// HomotopyReduction.cpp and GeneratingConformances.cpp.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/GenericSignature.h"
|
||||
#include "swift/AST/GenericSignatureBuilder.h"
|
||||
#include "swift/AST/LazyResolver.h"
|
||||
#include "swift/AST/Requirement.h"
|
||||
#include "swift/AST/TypeCheckRequests.h"
|
||||
#include "swift/Basic/Statistic.h"
|
||||
#include <vector>
|
||||
|
||||
#include "RequirementMachine.h"
|
||||
|
||||
using namespace swift;
|
||||
using namespace rewriting;
|
||||
|
||||
#define DEBUG_TYPE "Serialization"
|
||||
|
||||
@@ -31,6 +40,111 @@ STATISTIC(NumLazyRequirementSignaturesLoaded,
|
||||
|
||||
#undef DEBUG_TYPE
|
||||
|
||||
/// Convert a list of non-permanent, non-redundant rewrite rules into a minimal
|
||||
/// protocol requirement signature for \p proto. The requirements are sorted in
|
||||
/// canonical order, and same-type requirements are canonicalized.
|
||||
std::vector<Requirement>
|
||||
RequirementMachine::buildRequirementSignature(ArrayRef<unsigned> rules,
|
||||
const ProtocolDecl *proto) const {
|
||||
std::vector<Requirement> reqs;
|
||||
llvm::SmallDenseMap<TypeBase *, llvm::SmallVector<Type, 2>> sameTypeReqs;
|
||||
|
||||
auto genericParams = proto->getGenericSignature().getGenericParams();
|
||||
|
||||
// Convert a rewrite rule into a requirement.
|
||||
auto createRequirementFromRule = [&](const Rule &rule) {
|
||||
if (auto prop = rule.isPropertyRule()) {
|
||||
auto subjectType = Context.getTypeForTerm(rule.getRHS(), genericParams,
|
||||
System.getProtocols());
|
||||
|
||||
switch (prop->getKind()) {
|
||||
case Symbol::Kind::Protocol:
|
||||
reqs.emplace_back(RequirementKind::Conformance,
|
||||
subjectType,
|
||||
prop->getProtocol()->getDeclaredInterfaceType());
|
||||
return;
|
||||
|
||||
case Symbol::Kind::Layout:
|
||||
case Symbol::Kind::ConcreteType:
|
||||
case Symbol::Kind::Superclass:
|
||||
// FIXME
|
||||
return;
|
||||
|
||||
case Symbol::Kind::Name:
|
||||
case Symbol::Kind::AssociatedType:
|
||||
case Symbol::Kind::GenericParam:
|
||||
break;
|
||||
}
|
||||
|
||||
llvm_unreachable("Invalid symbol kind");
|
||||
} else if (rule.getLHS().back().getKind() != Symbol::Kind::Protocol) {
|
||||
auto constraintType = Context.getTypeForTerm(rule.getLHS(), genericParams,
|
||||
System.getProtocols());
|
||||
auto subjectType = Context.getTypeForTerm(rule.getRHS(), genericParams,
|
||||
System.getProtocols());
|
||||
|
||||
sameTypeReqs[subjectType.getPointer()].push_back(constraintType);
|
||||
}
|
||||
};
|
||||
|
||||
// Build the list of requirements, storing same-type requirements off
|
||||
// to the side.
|
||||
for (unsigned ruleID : rules) {
|
||||
const auto &rule = System.getRule(ruleID);
|
||||
createRequirementFromRule(rule);
|
||||
}
|
||||
|
||||
// A set of rewrite rules of the form:
|
||||
//
|
||||
// B => A
|
||||
// C => A
|
||||
// D => A
|
||||
//
|
||||
// Become a series of same-type requirements
|
||||
//
|
||||
// A == B, B == C, C == D
|
||||
//
|
||||
for (auto &pair : sameTypeReqs) {
|
||||
std::sort(pair.second.begin(), pair.second.end(),
|
||||
[](Type first, Type second) -> bool {
|
||||
return compareDependentTypes(first, second) < 0;
|
||||
});
|
||||
|
||||
Type subjectType(pair.first);
|
||||
for (auto constraintType : pair.second) {
|
||||
reqs.emplace_back(RequirementKind::SameType, subjectType, constraintType);
|
||||
subjectType = constraintType;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the requirements in canonical order.
|
||||
std::sort(reqs.begin(), reqs.end(),
|
||||
[](const Requirement &lhs, const Requirement &rhs) -> bool {
|
||||
return lhs.compare(rhs) < 0;
|
||||
});
|
||||
|
||||
return reqs;
|
||||
}
|
||||
|
||||
/// Builds the requirement signatures for each protocol in this strongly
|
||||
/// connected component.
|
||||
llvm::DenseMap<const ProtocolDecl *, std::vector<Requirement>>
|
||||
RequirementMachine::computeMinimalRequirements() {
|
||||
assert(Protos.size() > 0);
|
||||
System.minimizeRewriteSystem();
|
||||
|
||||
auto rules = System.getMinimizedRules(Protos);
|
||||
|
||||
// Note that we build 'result' by iterating over 'Protos' rather than
|
||||
// 'rules'; this is intentional, so that even if a protocol has no
|
||||
// rules, we still end up creating an entry for it in 'result'.
|
||||
llvm::DenseMap<const ProtocolDecl *, std::vector<Requirement>> result;
|
||||
for (const auto *proto : Protos)
|
||||
result[proto] = buildRequirementSignature(rules[proto], proto);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ArrayRef<Requirement>
|
||||
RequirementSignatureRequest::evaluate(Evaluator &evaluator,
|
||||
ProtocolDecl *proto) const {
|
||||
@@ -54,6 +168,7 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator,
|
||||
return ctx.AllocateCopy(requirements);
|
||||
}
|
||||
|
||||
auto buildViaGSB = [&]() {
|
||||
GenericSignatureBuilder builder(proto->getASTContext());
|
||||
|
||||
// Add all of the generic parameters.
|
||||
@@ -77,4 +192,58 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator,
|
||||
/*allowConcreteGenericParams=*/false,
|
||||
/*requirementSignatureSelfProto=*/proto);
|
||||
return reqSignature.getRequirements();
|
||||
};
|
||||
|
||||
auto buildViaRQM = [&]() {
|
||||
// We build requirement signatures for all protocols in a strongly connected
|
||||
// component at the same time.
|
||||
auto *machine = ctx.getOrCreateRequirementMachine(proto);
|
||||
auto requirements = machine->computeMinimalRequirements();
|
||||
|
||||
bool debug = machine->getDebugOptions().contains(DebugFlags::Minimization);
|
||||
|
||||
// The requirement signature for the actual protocol that the result
|
||||
// was kicked off with.
|
||||
ArrayRef<Requirement> result;
|
||||
|
||||
for (const auto &pair : requirements) {
|
||||
auto *otherProto = pair.first;
|
||||
const auto &reqs = pair.second;
|
||||
|
||||
// setRequirementSignature() doesn't take ownership of the memory, so
|
||||
// we have to make a copy of the std::vector temporary.
|
||||
ArrayRef<Requirement> reqsCopy = ctx.AllocateCopy(reqs);
|
||||
|
||||
// Don't call setRequirementSignature() on the original proto; the
|
||||
// request evaluator will do it for us.
|
||||
if (otherProto == proto)
|
||||
result = reqsCopy;
|
||||
else
|
||||
const_cast<ProtocolDecl *>(otherProto)->setRequirementSignature(reqsCopy);
|
||||
|
||||
// Dump the result if requested.
|
||||
if (debug) {
|
||||
llvm::dbgs() << "Protocol " << otherProto->getName() << ": ";
|
||||
|
||||
auto sig = GenericSignature::get(
|
||||
otherProto->getGenericSignature().getGenericParams(),
|
||||
reqsCopy);
|
||||
llvm::dbgs() << sig << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Return the result for the specific protocol this request was kicked off on.
|
||||
return result;
|
||||
};
|
||||
|
||||
switch (ctx.LangOpts.RequirementMachineProtocolSignatures) {
|
||||
case RequirementMachineMode::Disabled:
|
||||
return buildViaGSB();
|
||||
|
||||
case RequirementMachineMode::Enabled:
|
||||
return buildViaRQM();
|
||||
|
||||
case RequirementMachineMode::Verify:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ RewriteSystem::RewriteSystem(RewriteContext &ctx)
|
||||
Initialized = 0;
|
||||
Complete = 0;
|
||||
Minimized = 0;
|
||||
RecordHomotopyGenerators = 0;
|
||||
}
|
||||
|
||||
RewriteSystem::~RewriteSystem() {
|
||||
@@ -96,12 +97,14 @@ RewriteSystem::~RewriteSystem() {
|
||||
}
|
||||
|
||||
void RewriteSystem::initialize(
|
||||
bool recordHomotopyGenerators,
|
||||
std::vector<std::pair<MutableTerm, MutableTerm>> &&associatedTypeRules,
|
||||
std::vector<std::pair<MutableTerm, MutableTerm>> &&requirementRules,
|
||||
ProtocolGraph &&graph) {
|
||||
assert(!Initialized);
|
||||
Initialized = 1;
|
||||
|
||||
RecordHomotopyGenerators = recordHomotopyGenerators;
|
||||
Protos = graph;
|
||||
|
||||
for (const auto &rule : associatedTypeRules) {
|
||||
@@ -170,7 +173,7 @@ bool RewriteSystem::addRule(MutableTerm lhs, MutableTerm rhs,
|
||||
if (path) {
|
||||
// We already have a loop, since the simplified lhs is identical to the
|
||||
// simplified rhs.
|
||||
HomotopyGenerators.emplace_back(lhs, loop);
|
||||
recordHomotopyGenerator(lhs, loop);
|
||||
|
||||
if (Debug.contains(DebugFlags::Add)) {
|
||||
llvm::dbgs() << "## Recorded trivial loop at " << lhs << ": ";
|
||||
@@ -206,7 +209,7 @@ bool RewriteSystem::addRule(MutableTerm lhs, MutableTerm rhs,
|
||||
// add a rewrite step applying the new rule in reverse to close the loop.
|
||||
loop.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0,
|
||||
newRuleID, /*inverse=*/true));
|
||||
HomotopyGenerators.emplace_back(lhs, loop);
|
||||
recordHomotopyGenerator(lhs, loop);
|
||||
|
||||
if (Debug.contains(DebugFlags::Add)) {
|
||||
llvm::dbgs() << "## Recorded non-trivial loop at " << lhs << ": ";
|
||||
@@ -376,7 +379,7 @@ void RewriteSystem::simplifyRewriteSystem() {
|
||||
loop.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0,
|
||||
newRuleID, /*inverse=*/true));
|
||||
|
||||
HomotopyGenerators.emplace_back(MutableTerm(lhs), loop);
|
||||
recordHomotopyGenerator(MutableTerm(lhs), loop);
|
||||
|
||||
if (Debug.contains(DebugFlags::Completion)) {
|
||||
llvm::dbgs() << "$ Right hand side simplification recorded a loop: ";
|
||||
|
||||
@@ -379,6 +379,9 @@ class RewriteSystem final {
|
||||
/// Whether we've minimized the rewrite system.
|
||||
unsigned Minimized : 1;
|
||||
|
||||
/// If set, record homotopy generators in the completion procedure.
|
||||
unsigned RecordHomotopyGenerators : 1;
|
||||
|
||||
public:
|
||||
explicit RewriteSystem(RewriteContext &ctx);
|
||||
~RewriteSystem();
|
||||
@@ -394,7 +397,8 @@ public:
|
||||
/// Return the object recording information about known protocols.
|
||||
const ProtocolGraph &getProtocols() const { return Protos; }
|
||||
|
||||
void initialize(std::vector<std::pair<MutableTerm, MutableTerm>> &&assocaitedTypeRules,
|
||||
void initialize(bool recordHomotopyGenerators,
|
||||
std::vector<std::pair<MutableTerm, MutableTerm>> &&assocaitedTypeRules,
|
||||
std::vector<std::pair<MutableTerm, MutableTerm>> &&requirementRules,
|
||||
ProtocolGraph &&protos);
|
||||
|
||||
@@ -452,6 +456,21 @@ public:
|
||||
void verifyRewriteRules(ValidityPolicy policy) const;
|
||||
|
||||
private:
|
||||
void recordHomotopyGenerator(HomotopyGenerator loop) {
|
||||
if (!RecordHomotopyGenerators)
|
||||
return;
|
||||
|
||||
HomotopyGenerators.push_back(loop);
|
||||
}
|
||||
|
||||
void recordHomotopyGenerator(MutableTerm basepoint,
|
||||
RewritePath path) {
|
||||
if (!RecordHomotopyGenerators)
|
||||
return;
|
||||
|
||||
HomotopyGenerators.emplace_back(basepoint, path);
|
||||
}
|
||||
|
||||
bool
|
||||
computeCriticalPair(
|
||||
ArrayRef<Symbol>::const_iterator from,
|
||||
|
||||
@@ -291,6 +291,10 @@ void RewriteSystem::processMergedAssociatedTypes() {
|
||||
/// If so, record this rule for later. We'll try to merge the associated
|
||||
/// types in RewriteSystem::processMergedAssociatedTypes().
|
||||
void RewriteSystem::checkMergedAssociatedType(Term lhs, Term rhs) {
|
||||
// FIXME: Figure out 3-cell representation for merged associated types
|
||||
if (RecordHomotopyGenerators)
|
||||
return;
|
||||
|
||||
if (lhs.size() == rhs.size() &&
|
||||
std::equal(lhs.begin(), lhs.end() - 1, rhs.begin()) &&
|
||||
lhs.back().getKind() == Symbol::Kind::AssociatedType &&
|
||||
@@ -581,7 +585,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
|
||||
llvm::dbgs() << "\n\n";
|
||||
|
||||
// Record the trivial loop.
|
||||
HomotopyGenerators.push_back(loop);
|
||||
recordHomotopyGenerator(loop);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -831,8 +831,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
}
|
||||
}
|
||||
|
||||
if (auto A =
|
||||
Args.getLastArg(OPT_requirement_machine_EQ)) {
|
||||
if (auto A = Args.getLastArg(OPT_requirement_machine_EQ)) {
|
||||
auto value = llvm::StringSwitch<Optional<RequirementMachineMode>>(A->getValue())
|
||||
.Case("off", RequirementMachineMode::Disabled)
|
||||
.Case("on", RequirementMachineMode::Enabled)
|
||||
@@ -846,6 +845,20 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
A->getAsString(Args), A->getValue());
|
||||
}
|
||||
|
||||
if (auto A = Args.getLastArg(OPT_requirement_machine_protocol_signatures_EQ)) {
|
||||
auto value = llvm::StringSwitch<Optional<RequirementMachineMode>>(A->getValue())
|
||||
.Case("off", RequirementMachineMode::Disabled)
|
||||
.Case("on", RequirementMachineMode::Enabled)
|
||||
.Case("verify", RequirementMachineMode::Verify)
|
||||
.Default(None);
|
||||
|
||||
if (value)
|
||||
Opts.RequirementMachineProtocolSignatures = *value;
|
||||
else
|
||||
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
|
||||
A->getAsString(Args), A->getValue());
|
||||
}
|
||||
|
||||
Opts.DumpRequirementMachine = Args.hasArg(
|
||||
OPT_dump_requirement_machine);
|
||||
Opts.AnalyzeRequirementMachine = Args.hasArg(
|
||||
|
||||
@@ -1093,7 +1093,12 @@ ProtocolDependenciesRequest::evaluate(Evaluator &evaluator,
|
||||
|
||||
// If we have a serialized requirement signature, deserialize it and
|
||||
// look at conformance requirements.
|
||||
if (proto->hasLazyRequirementSignature()) {
|
||||
//
|
||||
// FIXME: For now we just fall back to the GSB for all protocols
|
||||
// unless -requirement-machine-protocol-signatures=on is passed.
|
||||
if (proto->hasLazyRequirementSignature() ||
|
||||
(ctx.LangOpts.RequirementMachineProtocolSignatures
|
||||
== RequirementMachineMode::Disabled)) {
|
||||
for (auto req : proto->getRequirementSignature()) {
|
||||
if (req.getKind() == RequirementKind::Conformance) {
|
||||
result.push_back(req.getProtocolDecl());
|
||||
|
||||
61
test/Generics/rdar33654588.swift
Normal file
61
test/Generics/rdar33654588.swift
Normal file
@@ -0,0 +1,61 @@
|
||||
// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on 2>&1
|
||||
|
||||
protocol FP {}
|
||||
|
||||
protocol FPSign {
|
||||
associatedtype Positive: FPIntDigit
|
||||
associatedtype Negative: FPIntDigit
|
||||
}
|
||||
|
||||
protocol FPIntDigit: FP {
|
||||
associatedtype Zero: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype One: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype Two: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype Three: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype Four: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype Five: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype Six: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype Seven: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype Eight: FPIntDigit, FPPoint, FPExponent
|
||||
associatedtype Nine: FPIntDigit, FPPoint, FPExponent
|
||||
}
|
||||
|
||||
protocol FPPoint: FP {
|
||||
associatedtype Point: FPFractionDigit, FPExponent
|
||||
}
|
||||
|
||||
protocol FPFractionDigit: FP {
|
||||
associatedtype Zero: FPFractionDigit, FPExponent
|
||||
associatedtype One: FPFractionDigit, FPExponent
|
||||
associatedtype Two: FPFractionDigit, FPExponent
|
||||
associatedtype Three: FPFractionDigit, FPExponent
|
||||
associatedtype Four: FPFractionDigit, FPExponent
|
||||
associatedtype Five: FPFractionDigit, FPExponent
|
||||
associatedtype Six: FPFractionDigit, FPExponent
|
||||
associatedtype Seven: FPFractionDigit, FPExponent
|
||||
associatedtype Eight: FPFractionDigit, FPExponent
|
||||
associatedtype Nine: FPFractionDigit, FPExponent
|
||||
}
|
||||
|
||||
protocol FPExponent {
|
||||
associatedtype E: FPExponentSign, FPExponentDigit
|
||||
}
|
||||
|
||||
protocol FPExponentSign {
|
||||
associatedtype Positive: FPExponentDigit
|
||||
associatedtype Negative: FPExponentDigit
|
||||
}
|
||||
|
||||
protocol FPExponentDigit: FP {
|
||||
associatedtype Zero: FPExponentDigit
|
||||
associatedtype One: FPExponentDigit
|
||||
associatedtype Two: FPExponentDigit
|
||||
associatedtype Three: FPExponentDigit
|
||||
associatedtype Four: FPExponentDigit
|
||||
associatedtype Five: FPExponentDigit
|
||||
associatedtype Six: FPExponentDigit
|
||||
associatedtype Seven: FPExponentDigit
|
||||
associatedtype Eight: FPExponentDigit
|
||||
associatedtype Nine: FPExponentDigit
|
||||
}
|
||||
|
||||
7
test/Generics/rdar65015626.swift
Normal file
7
test/Generics/rdar65015626.swift
Normal file
@@ -0,0 +1,7 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: 65015626.(file).G@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.Word : FixedWidthInteger, Self.Word == Self.Word.Magnitude>
|
||||
public protocol G {
|
||||
associatedtype Word: FixedWidthInteger where Word.Magnitude == Word
|
||||
}
|
||||
41
test/Generics/rdar68084643.swift
Normal file
41
test/Generics/rdar68084643.swift
Normal file
@@ -0,0 +1,41 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: 68084643.(file).R@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.RT : F>
|
||||
protocol R {
|
||||
associatedtype RT: F
|
||||
}
|
||||
|
||||
// CHECK: 68084643.(file).F@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.OT.RT, Self.OT : R>
|
||||
protocol F {
|
||||
associatedtype OT: R where OT.RT == Self
|
||||
}
|
||||
|
||||
// CHECK: 68084643.(file).P@
|
||||
// CHECK-NEXT: Requirement signature: <Self>
|
||||
protocol P {
|
||||
associatedtype PT
|
||||
}
|
||||
|
||||
// CHECK: 68084643.(file).O@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.O : P>
|
||||
protocol O {
|
||||
associatedtype O: P
|
||||
}
|
||||
|
||||
// CHECK: 68084643.(file).B@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.LO : O, Self.OT == Self.LO.O, Self.RT : F, Self.OT.PT == Self.RT.OT>
|
||||
protocol B {
|
||||
associatedtype RT: F
|
||||
associatedtype LO: O where LO.O == OT
|
||||
associatedtype OT where OT.PT == RT.OT
|
||||
}
|
||||
|
||||
// CHECK: 68084643.(file).Boom@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.D : B, Self.E : Sequence, Self.F : Sequence, Self.E.Element == Self.F.Element>
|
||||
protocol Boom {
|
||||
associatedtype D: B
|
||||
associatedtype E: Sequence
|
||||
associatedtype F: Sequence where E.Element == F.Element
|
||||
}
|
||||
38
test/Generics/rdar78643612.swift
Normal file
38
test/Generics/rdar78643612.swift
Normal file
@@ -0,0 +1,38 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: 78643612.(file).Z1@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.T>
|
||||
protocol Z1 {
|
||||
associatedtype T where T == Self
|
||||
}
|
||||
|
||||
// CHECK: 78643612.(file).Z2@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.T.T, Self.T : Z2>
|
||||
protocol Z2 {
|
||||
associatedtype T : Z2 where T.T == Self
|
||||
}
|
||||
|
||||
// CHECK: 78643612.(file).Z3@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.T.T.T, Self.T : Z3>
|
||||
protocol Z3 {
|
||||
associatedtype T : Z3 where T.T.T == Self
|
||||
}
|
||||
|
||||
// CHECK: 78643612.(file).Z4@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.T.T.T.T, Self.T : Z4>
|
||||
protocol Z4 {
|
||||
associatedtype T : Z4 where T.T.T.T == Self
|
||||
}
|
||||
|
||||
// CHECK: 78643612.(file).Z5@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.T.T.T.T.T, Self.T : Z5>
|
||||
protocol Z5 {
|
||||
associatedtype T : Z5 where T.T.T.T.T == Self
|
||||
}
|
||||
|
||||
// CHECK: 78643612.(file).Z6@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.T.T.T.T.T.T, Self.T : Z6>
|
||||
protocol Z6 {
|
||||
associatedtype T : Z6 where T.T.T.T.T.T == Self
|
||||
}
|
||||
|
||||
59
test/Generics/rdar83308672.swift
Normal file
59
test/Generics/rdar83308672.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: rdar83308672.(file).A@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.X.T, Self.X : P1, Self.Y : P2>
|
||||
protocol A {
|
||||
associatedtype X : P1
|
||||
associatedtype Y : P2
|
||||
where X.T == Self
|
||||
}
|
||||
|
||||
// CHECK: rdar83308672.(file).P1@
|
||||
// CHECK-NEXT: Requirement signature: <Self>
|
||||
protocol P1 {
|
||||
associatedtype T
|
||||
}
|
||||
|
||||
// CHECK: rdar83308672.(file).P2@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.T : B>
|
||||
protocol P2 {
|
||||
associatedtype T : B
|
||||
}
|
||||
|
||||
// CHECK: rdar83308672.(file).B@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.X == Self.Y>
|
||||
protocol B {
|
||||
associatedtype X
|
||||
associatedtype Y
|
||||
where X == Y
|
||||
}
|
||||
|
||||
// Note that T.X == T.Y implies T : B, but also T : B implies T.X == T.Y;
|
||||
// we can drop one requirement but not both.
|
||||
//
|
||||
// If T : B was explicitly stated, we drop T.X == T.Y; otherwise we keep
|
||||
// T.X == T.Y and drop T : B.
|
||||
|
||||
// CHECK: rdar83308672.(file).G1@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T.X == Self.T.Y>
|
||||
protocol G1 {
|
||||
associatedtype T : A where T.X == T.Y
|
||||
}
|
||||
|
||||
// CHECK: rdar83308672.(file).G2@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T : B>
|
||||
protocol G2 {
|
||||
associatedtype T : A where T : B, T.X == T.Y
|
||||
}
|
||||
|
||||
// CHECK: rdar83308672.(file).G3@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T : B>
|
||||
protocol G3 {
|
||||
associatedtype T : A where T.X == T.Y, T : B
|
||||
}
|
||||
|
||||
// CHECK: rdar83308672.(file).G4@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.T : A, Self.T : B>
|
||||
protocol G4 {
|
||||
associatedtype T : A where T : B
|
||||
}
|
||||
31
test/Generics/rdar83848546.swift
Normal file
31
test/Generics/rdar83848546.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: 83848546.(file).Reporter@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.SubReporterType : SubReporter>
|
||||
protocol Reporter {
|
||||
associatedtype SubReporterType: SubReporter
|
||||
func makeSubReporter() -> SubReporterType
|
||||
}
|
||||
|
||||
// CHECK: 83848546.(file).SubReporter@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.SubReporterType : SubReporter>
|
||||
protocol SubReporter {
|
||||
associatedtype SubReporterType: SubReporter
|
||||
func makeSubReporter() -> SubReporterType
|
||||
}
|
||||
|
||||
// CHECK: 83848546.(file).CausesCompilerCrash@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.ReporterType : Reporter, Self.SubReporterType == Self.ReporterType.SubReporterType, Self.ReporterType.SubReporterType == Self.SubReporterType.SubReporterType>
|
||||
protocol CausesCompilerCrash {
|
||||
associatedtype ReporterType: Reporter
|
||||
associatedtype SubReporterType
|
||||
where ReporterType.SubReporterType == SubReporterType,
|
||||
SubReporterType.SubReporterType == SubReporterType
|
||||
}
|
||||
|
||||
// CHECK: 83848546.(file).DoesNotCrash@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.ReporterType : Reporter, Self.ReporterType.SubReporterType == Self.ReporterType.SubReporterType.SubReporterType>
|
||||
protocol DoesNotCrash {
|
||||
associatedtype ReporterType: Reporter
|
||||
where ReporterType.SubReporterType == ReporterType.SubReporterType.SubReporterType
|
||||
}
|
||||
12
test/Generics/rdar83955123.swift
Normal file
12
test/Generics/rdar83955123.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: rdar83955123.(file).Cyclo@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.L.R, Self.D : Cyclo, Self.L : Cyclo, Self.R : Cyclo, Self.U : Cyclo, Self.L.R == Self.U.D>
|
||||
protocol Cyclo {
|
||||
associatedtype L: Cyclo
|
||||
associatedtype R: Cyclo
|
||||
associatedtype U: Cyclo
|
||||
associatedtype D: Cyclo
|
||||
where L.R == Self,
|
||||
U.D == Self
|
||||
}
|
||||
28
test/Generics/sr10438.swift
Normal file
28
test/Generics/sr10438.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: sr10438.(file).AuthenticationFlowStateMachine@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.FlowError.StateMachine, Self.FlowError : AuthenticationFlowStateMachineFlowError, Self.NonFinalState : AuthenticationFlowStateMachineNonFinalState, Self.StartState : AuthenticationFlowStateMachineStartState, Self.FlowError.StateMachine == Self.NonFinalState.StateMachine, Self.NonFinalState.StateMachine == Self.StartState.StateMachine>
|
||||
protocol AuthenticationFlowStateMachine {
|
||||
associatedtype StartState: AuthenticationFlowStateMachineStartState where StartState.StateMachine == Self
|
||||
associatedtype NonFinalState: AuthenticationFlowStateMachineNonFinalState where NonFinalState.StateMachine == Self
|
||||
associatedtype FlowError: AuthenticationFlowStateMachineFlowError where FlowError.StateMachine == Self
|
||||
}
|
||||
|
||||
// CHECK: sr10438.(file).AuthenticationFlowStateMachineFlowError@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self : Error, Self == Self.StateMachine.FlowError, Self.StateMachine : AuthenticationFlowStateMachine>
|
||||
protocol AuthenticationFlowStateMachineFlowError: Error {
|
||||
associatedtype StateMachine: AuthenticationFlowStateMachine where StateMachine.FlowError == Self
|
||||
}
|
||||
|
||||
// CHECK: sr10438.(file).AuthenticationFlowStateMachineStartState@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.StateMachine.StartState, Self.StateMachine : AuthenticationFlowStateMachine>
|
||||
protocol AuthenticationFlowStateMachineStartState {
|
||||
associatedtype StateMachine: AuthenticationFlowStateMachine where StateMachine.StartState == Self
|
||||
var nonFinalState: StateMachine.NonFinalState { get }
|
||||
}
|
||||
|
||||
// CHECK: sr10438.(file).AuthenticationFlowStateMachineNonFinalState@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.StateMachine.NonFinalState, Self.StateMachine : AuthenticationFlowStateMachine>
|
||||
protocol AuthenticationFlowStateMachineNonFinalState {
|
||||
associatedtype StateMachine: AuthenticationFlowStateMachine where StateMachine.NonFinalState == Self
|
||||
}
|
||||
27
test/Generics/sr10532.swift
Normal file
27
test/Generics/sr10532.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: sr10532.(file).ScalarProtocol@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self : ScalarMultiplicative, Self == Self.Scalar>
|
||||
public protocol ScalarProtocol: ScalarMultiplicative where Self == Scalar { }
|
||||
|
||||
// CHECK: sr10532.(file).ScalarMultiplicative@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.Scalar : ScalarProtocol>
|
||||
public protocol ScalarMultiplicative {
|
||||
associatedtype Scalar : ScalarProtocol
|
||||
}
|
||||
|
||||
// CHECK: sr10532.(file).MapReduceArithmetic@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self : Collection, Self : ScalarMultiplicative, Self.Element : ScalarMultiplicative, Self.Scalar == Self.Element.Scalar>
|
||||
public protocol MapReduceArithmetic : ScalarMultiplicative, Collection where Element : ScalarMultiplicative, Scalar == Element.Scalar { }
|
||||
|
||||
// CHECK: sr10532.(file).Tensor@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self : MapReduceArithmetic, Self.Element : BinaryFloatingPoint, Self.Element : ScalarProtocol>
|
||||
public protocol Tensor : MapReduceArithmetic where Scalar : BinaryFloatingPoint, Element == Scalar {
|
||||
var magnitude: Scalar { get set }
|
||||
}
|
||||
|
||||
extension Tensor {
|
||||
public var magnitude: Scalar {
|
||||
return self.reduce(0) { $0 + $1 * $1 }.squareRoot()
|
||||
}
|
||||
}
|
||||
14
test/Generics/sr10752.swift
Normal file
14
test/Generics/sr10752.swift
Normal file
@@ -0,0 +1,14 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: sr10752.(file).P@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.A : P, Self.A == Self.A.A>
|
||||
protocol P {
|
||||
associatedtype A : P where A.A == A
|
||||
}
|
||||
|
||||
// CHECK: sr10752.(file).Q@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.A == Self.C.A, Self.C : P>
|
||||
protocol Q {
|
||||
associatedtype A : P
|
||||
associatedtype C : P where A == C.A
|
||||
}
|
||||
16
test/Generics/sr11100.swift
Normal file
16
test/Generics/sr11100.swift
Normal file
@@ -0,0 +1,16 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
protocol P1 {
|
||||
associatedtype A
|
||||
}
|
||||
|
||||
protocol P2 {
|
||||
associatedtype C: P1
|
||||
}
|
||||
|
||||
// CHECK: sr11100.(file).Q@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.X == Self.X.A, Self.Y : P2, Self.X.A == Self.Y.C>
|
||||
protocol Q {
|
||||
associatedtype X
|
||||
associatedtype Y : P2 where X == X.A, X.A == Y.C
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | not %FileCheck %s
|
||||
// RUN: %target-swift-frontend -emit-ir %s
|
||||
|
||||
// XFAIL: asserts
|
||||
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-protocol-signatures=on %s 2>&1 | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -emit-ir -requirement-machine-protocol-signatures=on %s
|
||||
|
||||
// CHECK: Requirement signature: <Self where Self.Field : FieldAlgebra>
|
||||
public protocol VectorSpace {
|
||||
|
||||
30
test/Generics/sr11997.swift
Normal file
30
test/Generics/sr11997.swift
Normal file
@@ -0,0 +1,30 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: sr11997.(file).A@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.X.Y, Self.X : B>
|
||||
protocol A {
|
||||
associatedtype X: B where X.Y == Self
|
||||
}
|
||||
|
||||
// CHECK: sr11997.(file).B@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.Y.X, Self.Y : A>
|
||||
protocol B {
|
||||
associatedtype Y: A where Y.X == Self
|
||||
}
|
||||
|
||||
// CHECK: sr11997.(file).AA@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self : A, Self.X : BB>
|
||||
protocol AA: A where X: BB { }
|
||||
|
||||
// CHECK: sr11997.(file).BB@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self : B, Self == Self.Z.T, Self.Y : AA, Self.Z : C>
|
||||
protocol BB: B where Y: AA {
|
||||
associatedtype Z: C where Z.T == Self
|
||||
}
|
||||
|
||||
// CHECK: sr11997.(file).C@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.T.Z, Self.T : BB>
|
||||
protocol C {
|
||||
associatedtype T: BB where T.Z == Self
|
||||
}
|
||||
|
||||
32
test/Generics/sr12120.swift
Normal file
32
test/Generics/sr12120.swift
Normal file
@@ -0,0 +1,32 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: sr12120.(file).Swappable1@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.Swapped.Swapped, Self.A == Self.Swapped.B, Self.Swapped : Swappable1>
|
||||
protocol Swappable1 {
|
||||
associatedtype A
|
||||
associatedtype B
|
||||
associatedtype Swapped : Swappable1
|
||||
where Swapped.B == A,
|
||||
Swapped.A == B, // FIXME: Diagnose redundancy
|
||||
Swapped.Swapped == Self
|
||||
}
|
||||
|
||||
// CHECK: sr12120.(file).Swappable2@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.Swapped.Swapped, Self.A == Self.Swapped.B, Self.Swapped : Swappable2>
|
||||
protocol Swappable2 {
|
||||
associatedtype A
|
||||
associatedtype B
|
||||
associatedtype Swapped : Swappable2
|
||||
where Swapped.B == A,
|
||||
Swapped.Swapped == Self
|
||||
}
|
||||
|
||||
// CHECK: sr12120.(file).Swappable3@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.Swapped.Swapped, Self.B == Self.Swapped.A, Self.Swapped : Swappable3>
|
||||
protocol Swappable3 {
|
||||
associatedtype A
|
||||
associatedtype B
|
||||
associatedtype Swapped : Swappable3
|
||||
where Swapped.A == B,
|
||||
Swapped.Swapped == Self
|
||||
}
|
||||
22
test/Generics/sr12736.swift
Normal file
22
test/Generics/sr12736.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: sr12736.(file).ColorModel@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.Float32Components.Model, Self.Float32Components : ColorComponents>
|
||||
public protocol ColorModel {
|
||||
associatedtype Float32Components: ColorComponents where Float32Components.Model == Self
|
||||
}
|
||||
|
||||
public protocol ColorComponents {
|
||||
associatedtype Model: ColorModel
|
||||
}
|
||||
|
||||
public protocol ColorPixel {
|
||||
associatedtype Model: ColorModel
|
||||
}
|
||||
|
||||
// CHECK: sr12736.(file).P@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.A : ColorPixel, Self.B : ColorPixel, Self.A.Model == Self.B.Model>
|
||||
public protocol P {
|
||||
associatedtype A: ColorPixel
|
||||
associatedtype B: ColorPixel where B.Model == A.Model
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures 2>&1 | %FileCheck %s
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=off 2>&1 | %FileCheck %s
|
||||
|
||||
// https://bugs.swift.org/browse/SR-13850
|
||||
|
||||
|
||||
10
test/Generics/sr14485.swift
Normal file
10
test/Generics/sr14485.swift
Normal file
@@ -0,0 +1,10 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: sr14485.(file).P@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self.Input == Self.Output.Magnitude, Self.Output : FixedWidthInteger, Self.Output : SignedInteger>
|
||||
protocol P {
|
||||
associatedtype Input: FixedWidthInteger & UnsignedInteger & BinaryInteger
|
||||
associatedtype Output: FixedWidthInteger & SignedInteger & BinaryInteger
|
||||
where Output.Magnitude == Input
|
||||
}
|
||||
|
||||
9
test/Generics/sr15009.swift
Normal file
9
test/Generics/sr15009.swift
Normal file
@@ -0,0 +1,9 @@
|
||||
// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s
|
||||
|
||||
// CHECK: sr15009.(file).P@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self == Self.A.B, Self.A : Q>
|
||||
protocol P { associatedtype A: Q where A.B == Self }
|
||||
|
||||
// CHECK: sr15009.(file).Q@
|
||||
// CHECK-NEXT: Requirement signature: <Self where Self : CaseIterable, Self == Self.B.A, Self.B : P>
|
||||
protocol Q: CaseIterable { associatedtype B: P where B.A == Self }
|
||||
@@ -1,20 +0,0 @@
|
||||
// RUN: %target-swift-frontend -typecheck %s
|
||||
|
||||
protocol AuthenticationFlowStateMachine {
|
||||
associatedtype StartState: AuthenticationFlowStateMachineStartState where StartState.StateMachine == Self
|
||||
associatedtype NonFinalState: AuthenticationFlowStateMachineNonFinalState where NonFinalState.StateMachine == Self
|
||||
associatedtype FlowError: AuthenticationFlowStateMachineFlowError where FlowError.StateMachine == Self
|
||||
}
|
||||
|
||||
protocol AuthenticationFlowStateMachineFlowError: Error {
|
||||
associatedtype StateMachine: AuthenticationFlowStateMachine where StateMachine.FlowError == Self
|
||||
}
|
||||
|
||||
protocol AuthenticationFlowStateMachineStartState {
|
||||
associatedtype StateMachine: AuthenticationFlowStateMachine where StateMachine.StartState == Self
|
||||
var nonFinalState: StateMachine.NonFinalState { get }
|
||||
}
|
||||
|
||||
protocol AuthenticationFlowStateMachineNonFinalState {
|
||||
associatedtype StateMachine: AuthenticationFlowStateMachine where StateMachine.NonFinalState == Self
|
||||
}
|
||||
Reference in New Issue
Block a user