Merge pull request #39629 from slavapestov/rqm-protocol-signatures

RequirementMachine: Wire up protocol requirement signature minimization
This commit is contained in:
Slava Pestov
2021-10-09 22:30:05 -04:00
committed by GitHub
33 changed files with 841 additions and 243 deletions

View File

@@ -481,6 +481,11 @@ namespace swift {
/// algorithm. /// algorithm.
unsigned RequirementMachineDepthLimit = 10; 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 /// Sets the target we are building for and updates platform conditions
/// to match. /// to match.
/// ///

View File

@@ -616,6 +616,10 @@ def requirement_machine_EQ : Joined<["-"], "requirement-machine=">,
Flags<[FrontendOption, ModuleInterfaceOption]>, Flags<[FrontendOption, ModuleInterfaceOption]>,
HelpText<"Control usage of experimental generics implementation: 'on', 'off', or 'verify'">; 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 // Diagnostic control options
def suppress_warnings : Flag<["-"], "suppress-warnings">, def suppress_warnings : Flag<["-"], "suppress-warnings">,
Flags<[FrontendOption]>, Flags<[FrontendOption]>,

View File

@@ -1483,11 +1483,7 @@ int Requirement::compare(const Requirement &other) const {
// We should only have multiple conformance requirements. // We should only have multiple conformance requirements.
assert(getKind() == RequirementKind::Conformance); assert(getKind() == RequirementKind::Conformance);
int compareProtos = return TypeDecl::compare(getProtocolDecl(), other.getProtocolDecl());
TypeDecl::compare(getProtocolDecl(), other.getProtocolDecl());
assert(compareProtos != 0 && "Duplicate conformance requirement");
return compareProtos;
} }
/// Compare two associated types. /// Compare two associated types.

View File

@@ -34,6 +34,26 @@
// decompositions can be found for all "derived" conformance rules, producing // decompositions can be found for all "derived" conformance rules, producing
// a minimal set of generating conformances. // 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" #include "swift/AST/Decl.h"
@@ -283,23 +303,48 @@ void RewriteSystem::computeCandidateConformancePaths(
llvm::dbgs() << "\n"; 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 // 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 // context, and a conformance rule (V.[P] => V) with a non-empty left
// left context U and empty right context. // 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: // We can decompose U into a product of conformance rules:
// //
// (V1.[P1] => V1)...(Vn.[Pn] => Vn), // (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 // Now, we can record a candidate decomposition of (T.[P] => T) as a
// product of conformance rules: // product of conformance rules:
// //
// (T.[P] => T) := (V1.[P1] => V1)...(Vn.[Pn] => Vn).(V.[P] => V) // (T.[P] => T) := (V1.[P1] => V1)...(Vn.[Pn] => Vn).(V.[P] => V)
// //
// Now if U is empty, this becomes the trivial candidate: // Again, note that (V1)...(Vn).V is canonically equivalent to U.V,
// // and therefore T.
// (T.[P] => T) := (V.[P] => V)
SmallVector<SmallVector<unsigned, 2>, 2> candidatePaths;
for (auto pair : inContext) { for (auto pair : inContext) {
// We have a term U, and a rule V.[P] => V. // We have a term U, and a rule V.[P] => V.
SmallVector<unsigned, 2> conformancePath; SmallVector<unsigned, 2> conformancePath;
@@ -313,26 +358,10 @@ void RewriteSystem::computeCandidateConformancePaths(
decomposeTermIntoConformanceRuleLeftHandSides(term, pair.second, decomposeTermIntoConformanceRuleLeftHandSides(term, pair.second,
conformancePath); conformancePath);
candidatePaths.push_back(conformancePath); // This decomposition defines a conformance access path for each
} // conformance rule we saw in empty context.
for (unsigned otherRuleID : notInContext)
for (unsigned candidateRuleID : notInContext) { conformancePaths[otherRuleID].push_back(conformancePath);
// 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);
}
} }
} }
} }
@@ -492,6 +521,48 @@ void RewriteSystem::verifyGeneratingConformanceEquations(
#endif #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 /// Computes a minimal set of generating conformances, assuming that homotopy
/// reduction has already eliminated all redundant rewrite rules that are not /// reduction has already eliminated all redundant rewrite rules that are not
/// conformance rules. /// conformance rules.
@@ -536,49 +607,21 @@ void RewriteSystem::computeGeneratingConformances(
auto lhs = rule.getLHS(); auto lhs = rule.getLHS();
auto parentSymbol = lhs[lhs.size() - 2]; // Record a parent path if the subject type itself requires a non-trivial
// conformance path to derive.
// The last element is a protocol symbol, because this is a conformance rule. if (auto *parentProto = getParentConformanceForTerm(lhs)) {
// 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;
MutableTerm mutTerm(lhs.begin(), lhs.end() - 2); MutableTerm mutTerm(lhs.begin(), lhs.end() - 2);
assert(!mutTerm.empty()); assert(!mutTerm.empty());
const auto protos = parentSymbol.getProtocols();
assert(protos.size() == 1);
bool simplified = simplify(mutTerm); bool simplified = simplify(mutTerm);
assert(!simplified || rule.isSimplified()); assert(!simplified || rule.isSimplified());
(void) simplified; (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. // Get a conformance path for X.[P] and record it.
decomposeTermIntoConformanceRuleLeftHandSides(mutTerm, parentPaths[ruleID]); 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); computeCandidateConformancePaths(conformancePaths);

View File

@@ -751,7 +751,7 @@ void RewriteSystem::performHomotopyReduction(
redundantConformances, redundantConformances,
replacementPath); 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) if (!optRuleID)
return; return;

View File

@@ -23,7 +23,7 @@ using namespace rewriting;
void ProtocolGraph::visitRequirements(ArrayRef<Requirement> reqs) { void ProtocolGraph::visitRequirements(ArrayRef<Requirement> reqs) {
for (auto req : reqs) { for (auto req : reqs) {
if (req.getKind() == RequirementKind::Conformance) { 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. /// \p protos.
void ProtocolGraph::visitProtocols(ArrayRef<const ProtocolDecl *> protos) { void ProtocolGraph::visitProtocols(ArrayRef<const ProtocolDecl *> protos) {
for (auto proto : 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. /// 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) if (Info.count(proto) > 0)
return; return;
Info[proto] = {proto->getInheritedProtocols(), Info[proto] = {proto->getInheritedProtocols(),
proto->getAssociatedTypeMembers(), proto->getAssociatedTypeMembers(),
proto->getRequirementSignature()}; proto->getProtocolDependencies(),
initialComponent};
Protocols.push_back(proto); Protocols.push_back(proto);
} }
@@ -66,7 +68,9 @@ void ProtocolGraph::computeTransitiveClosure() {
unsigned i = 0; unsigned i = 0;
while (i < Protocols.size()) { while (i < Protocols.size()) {
auto *proto = Protocols[i++]; auto *proto = Protocols[i++];
visitRequirements(getProtocolInfo(proto).Requirements); for (auto *proto : getProtocolInfo(proto).Dependencies) {
addProtocol(proto, /*initialComponent=*/false);
}
} }
} }

View File

@@ -42,8 +42,8 @@ struct ProtocolInfo {
/// ProtocolGraph::computeInheritedAssociatedTypes(). /// ProtocolGraph::computeInheritedAssociatedTypes().
llvm::TinyPtrVector<AssociatedTypeDecl *> InheritedAssociatedTypes; llvm::TinyPtrVector<AssociatedTypeDecl *> InheritedAssociatedTypes;
/// The protocol's requirement signature. /// The protocol's dependencies.
ArrayRef<Requirement> Requirements; ArrayRef<ProtocolDecl *> Dependencies;
/// Used by ProtocolGraph::computeProtocolDepth() to detect circularity. /// Used by ProtocolGraph::computeProtocolDepth() to detect circularity.
unsigned Mark : 1; unsigned Mark : 1;
@@ -55,27 +55,34 @@ struct ProtocolInfo {
/// Index of the protocol in the linear order. Computed by /// Index of the protocol in the linear order. Computed by
/// ProtocolGraph::computeLinearOrder(). /// 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() { ProtocolInfo() {
Mark = 0; Mark = 0;
Depth = 0; Depth = 0;
Index = 0; Index = 0;
InitialComponent = 0;
} }
ProtocolInfo(ArrayRef<ProtocolDecl *> inherited, ProtocolInfo(ArrayRef<ProtocolDecl *> inherited,
ArrayRef<AssociatedTypeDecl *> &&types, ArrayRef<AssociatedTypeDecl *> &&types,
ArrayRef<Requirement> reqs) ArrayRef<ProtocolDecl *> deps,
bool initialComponent)
: Inherited(inherited), : Inherited(inherited),
AssociatedTypes(types), AssociatedTypes(types),
Requirements(reqs) { Dependencies(deps) {
Mark = 0; Mark = 0;
Depth = 0; Depth = 0;
Index = 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. /// referenced from a set of generic requirements.
/// ///
/// Out-of-line methods are documented in ProtocolGraph.cpp. /// Out-of-line methods are documented in ProtocolGraph.cpp.
@@ -101,7 +108,8 @@ public:
const ProtocolDecl *proto) const; const ProtocolDecl *proto) const;
private: private:
void addProtocol(const ProtocolDecl *proto); void addProtocol(const ProtocolDecl *proto,
bool initialComponent);
void computeTransitiveClosure(); void computeTransitiveClosure();
void computeLinearOrder(); void computeLinearOrder();
void computeInheritedAssociatedTypes(); void computeInheritedAssociatedTypes();

View File

@@ -243,8 +243,20 @@ void RewriteSystemBuilder::processProtocolDependencies() {
for (auto *assocType : info.InheritedAssociatedTypes) for (auto *assocType : info.InheritedAssociatedTypes)
addAssociatedType(assocType, proto); addAssociatedType(assocType, proto);
for (auto req : info.Requirements) // If this protocol is part of the initial connected component, we're
addRequirement(req.getCanonical(), proto); // 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) { if (Dump) {
llvm::dbgs() << "}\n"; llvm::dbgs() << "}\n";
@@ -397,7 +409,8 @@ void RequirementMachine::initWithGenericSignature(CanGenericSignature sig) {
// Add the initial set of rewrite rules to the rewrite system, also // Add the initial set of rewrite rules to the rewrite system, also
// providing the protocol graph to use for the linear order on terms. // 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.RequirementRules),
std::move(builder.Protocols)); 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 // Add the initial set of rewrite rules to the rewrite system, also
// providing the protocol graph to use for the linear order on terms. // 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.RequirementRules),
std::move(builder.Protocols)); std::move(builder.Protocols));
@@ -522,108 +536,3 @@ void RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy)
bool RequirementMachine::isComplete() const { bool RequirementMachine::isComplete() const {
return Complete; 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;
}

View File

@@ -10,19 +10,28 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// //
// This file implements the main entry points into the requirement machine // This file implements the main entry points for computing minimized generic
// via the request evaluator. // 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/ASTContext.h"
#include "swift/AST/Decl.h" #include "swift/AST/Decl.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/GenericSignatureBuilder.h"
#include "swift/AST/LazyResolver.h" #include "swift/AST/LazyResolver.h"
#include "swift/AST/Requirement.h"
#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Statistic.h" #include "swift/Basic/Statistic.h"
#include <vector>
#include "RequirementMachine.h"
using namespace swift; using namespace swift;
using namespace rewriting;
#define DEBUG_TYPE "Serialization" #define DEBUG_TYPE "Serialization"
@@ -31,6 +40,111 @@ STATISTIC(NumLazyRequirementSignaturesLoaded,
#undef DEBUG_TYPE #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> ArrayRef<Requirement>
RequirementSignatureRequest::evaluate(Evaluator &evaluator, RequirementSignatureRequest::evaluate(Evaluator &evaluator,
ProtocolDecl *proto) const { ProtocolDecl *proto) const {
@@ -54,27 +168,82 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator,
return ctx.AllocateCopy(requirements); return ctx.AllocateCopy(requirements);
} }
GenericSignatureBuilder builder(proto->getASTContext()); auto buildViaGSB = [&]() {
GenericSignatureBuilder builder(proto->getASTContext());
// Add all of the generic parameters. // Add all of the generic parameters.
for (auto gp : *proto->getGenericParams()) for (auto gp : *proto->getGenericParams())
builder.addGenericParameter(gp); builder.addGenericParameter(gp);
// Add the conformance of 'self' to the protocol. // Add the conformance of 'self' to the protocol.
auto selfType = auto selfType =
proto->getSelfInterfaceType()->castTo<GenericTypeParamType>(); proto->getSelfInterfaceType()->castTo<GenericTypeParamType>();
auto requirement = auto requirement =
Requirement(RequirementKind::Conformance, selfType, Requirement(RequirementKind::Conformance, selfType,
proto->getDeclaredInterfaceType()); proto->getDeclaredInterfaceType());
builder.addRequirement( builder.addRequirement(
requirement, requirement,
GenericSignatureBuilder::RequirementSource::forRequirementSignature( GenericSignatureBuilder::RequirementSource::forRequirementSignature(
builder, selfType, proto), builder, selfType, proto),
nullptr); nullptr);
auto reqSignature = std::move(builder).computeGenericSignature( auto reqSignature = std::move(builder).computeGenericSignature(
/*allowConcreteGenericParams=*/false, /*allowConcreteGenericParams=*/false,
/*requirementSignatureSelfProto=*/proto); /*requirementSignatureSelfProto=*/proto);
return reqSignature.getRequirements(); 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();
}
} }

View File

@@ -88,6 +88,7 @@ RewriteSystem::RewriteSystem(RewriteContext &ctx)
Initialized = 0; Initialized = 0;
Complete = 0; Complete = 0;
Minimized = 0; Minimized = 0;
RecordHomotopyGenerators = 0;
} }
RewriteSystem::~RewriteSystem() { RewriteSystem::~RewriteSystem() {
@@ -96,12 +97,14 @@ RewriteSystem::~RewriteSystem() {
} }
void RewriteSystem::initialize( void RewriteSystem::initialize(
bool recordHomotopyGenerators,
std::vector<std::pair<MutableTerm, MutableTerm>> &&associatedTypeRules, std::vector<std::pair<MutableTerm, MutableTerm>> &&associatedTypeRules,
std::vector<std::pair<MutableTerm, MutableTerm>> &&requirementRules, std::vector<std::pair<MutableTerm, MutableTerm>> &&requirementRules,
ProtocolGraph &&graph) { ProtocolGraph &&graph) {
assert(!Initialized); assert(!Initialized);
Initialized = 1; Initialized = 1;
RecordHomotopyGenerators = recordHomotopyGenerators;
Protos = graph; Protos = graph;
for (const auto &rule : associatedTypeRules) { for (const auto &rule : associatedTypeRules) {
@@ -170,7 +173,7 @@ bool RewriteSystem::addRule(MutableTerm lhs, MutableTerm rhs,
if (path) { if (path) {
// We already have a loop, since the simplified lhs is identical to the // We already have a loop, since the simplified lhs is identical to the
// simplified rhs. // simplified rhs.
HomotopyGenerators.emplace_back(lhs, loop); recordHomotopyGenerator(lhs, loop);
if (Debug.contains(DebugFlags::Add)) { if (Debug.contains(DebugFlags::Add)) {
llvm::dbgs() << "## Recorded trivial loop at " << lhs << ": "; 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. // add a rewrite step applying the new rule in reverse to close the loop.
loop.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0, loop.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0,
newRuleID, /*inverse=*/true)); newRuleID, /*inverse=*/true));
HomotopyGenerators.emplace_back(lhs, loop); recordHomotopyGenerator(lhs, loop);
if (Debug.contains(DebugFlags::Add)) { if (Debug.contains(DebugFlags::Add)) {
llvm::dbgs() << "## Recorded non-trivial loop at " << lhs << ": "; llvm::dbgs() << "## Recorded non-trivial loop at " << lhs << ": ";
@@ -376,7 +379,7 @@ void RewriteSystem::simplifyRewriteSystem() {
loop.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0, loop.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0,
newRuleID, /*inverse=*/true)); newRuleID, /*inverse=*/true));
HomotopyGenerators.emplace_back(MutableTerm(lhs), loop); recordHomotopyGenerator(MutableTerm(lhs), loop);
if (Debug.contains(DebugFlags::Completion)) { if (Debug.contains(DebugFlags::Completion)) {
llvm::dbgs() << "$ Right hand side simplification recorded a loop: "; llvm::dbgs() << "$ Right hand side simplification recorded a loop: ";

View File

@@ -379,6 +379,9 @@ class RewriteSystem final {
/// Whether we've minimized the rewrite system. /// Whether we've minimized the rewrite system.
unsigned Minimized : 1; unsigned Minimized : 1;
/// If set, record homotopy generators in the completion procedure.
unsigned RecordHomotopyGenerators : 1;
public: public:
explicit RewriteSystem(RewriteContext &ctx); explicit RewriteSystem(RewriteContext &ctx);
~RewriteSystem(); ~RewriteSystem();
@@ -394,7 +397,8 @@ public:
/// Return the object recording information about known protocols. /// Return the object recording information about known protocols.
const ProtocolGraph &getProtocols() const { return Protos; } 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, std::vector<std::pair<MutableTerm, MutableTerm>> &&requirementRules,
ProtocolGraph &&protos); ProtocolGraph &&protos);
@@ -452,6 +456,21 @@ public:
void verifyRewriteRules(ValidityPolicy policy) const; void verifyRewriteRules(ValidityPolicy policy) const;
private: 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 bool
computeCriticalPair( computeCriticalPair(
ArrayRef<Symbol>::const_iterator from, ArrayRef<Symbol>::const_iterator from,

View File

@@ -291,6 +291,10 @@ void RewriteSystem::processMergedAssociatedTypes() {
/// If so, record this rule for later. We'll try to merge the associated /// If so, record this rule for later. We'll try to merge the associated
/// types in RewriteSystem::processMergedAssociatedTypes(). /// types in RewriteSystem::processMergedAssociatedTypes().
void RewriteSystem::checkMergedAssociatedType(Term lhs, Term rhs) { 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() && if (lhs.size() == rhs.size() &&
std::equal(lhs.begin(), lhs.end() - 1, rhs.begin()) && std::equal(lhs.begin(), lhs.end() - 1, rhs.begin()) &&
lhs.back().getKind() == Symbol::Kind::AssociatedType && lhs.back().getKind() == Symbol::Kind::AssociatedType &&
@@ -581,7 +585,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
llvm::dbgs() << "\n\n"; llvm::dbgs() << "\n\n";
// Record the trivial loop. // Record the trivial loop.
HomotopyGenerators.push_back(loop); recordHomotopyGenerator(loop);
} }
} }
}); });

View File

@@ -831,13 +831,12 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
} }
} }
if (auto A = if (auto A = Args.getLastArg(OPT_requirement_machine_EQ)) {
Args.getLastArg(OPT_requirement_machine_EQ)) {
auto value = llvm::StringSwitch<Optional<RequirementMachineMode>>(A->getValue()) auto value = llvm::StringSwitch<Optional<RequirementMachineMode>>(A->getValue())
.Case("off", RequirementMachineMode::Disabled) .Case("off", RequirementMachineMode::Disabled)
.Case("on", RequirementMachineMode::Enabled) .Case("on", RequirementMachineMode::Enabled)
.Case("verify", RequirementMachineMode::Verify) .Case("verify", RequirementMachineMode::Verify)
.Default(None); .Default(None);
if (value) if (value)
Opts.EnableRequirementMachine = *value; Opts.EnableRequirementMachine = *value;
@@ -846,6 +845,20 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
A->getAsString(Args), A->getValue()); 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( Opts.DumpRequirementMachine = Args.hasArg(
OPT_dump_requirement_machine); OPT_dump_requirement_machine);
Opts.AnalyzeRequirementMachine = Args.hasArg( Opts.AnalyzeRequirementMachine = Args.hasArg(

View File

@@ -1093,7 +1093,12 @@ ProtocolDependenciesRequest::evaluate(Evaluator &evaluator,
// If we have a serialized requirement signature, deserialize it and // If we have a serialized requirement signature, deserialize it and
// look at conformance requirements. // 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()) { for (auto req : proto->getRequirementSignature()) {
if (req.getKind() == RequirementKind::Conformance) { if (req.getKind() == RequirementKind::Conformance) {
result.push_back(req.getProtocolDecl()); result.push_back(req.getProtocolDecl());

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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()
}
}

View 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
}

View 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
}

View File

@@ -1,7 +1,5 @@
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | not %FileCheck %s // RUN: %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-protocol-signatures=on %s 2>&1 | %FileCheck %s
// RUN: %target-swift-frontend -emit-ir %s // RUN: %target-swift-frontend -emit-ir -requirement-machine-protocol-signatures=on %s
// XFAIL: asserts
// CHECK: Requirement signature: <Self where Self.Field : FieldAlgebra> // CHECK: Requirement signature: <Self where Self.Field : FieldAlgebra>
public protocol VectorSpace { public protocol VectorSpace {

View 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
}

View 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
}

View 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
}

View File

@@ -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 // https://bugs.swift.org/browse/SR-13850

View 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
}

View 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 }

View File

@@ -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
}