RequirementMachine: Re-use requirement machines constructed by minimization for queries

Fixes rdar://problem/88135641.
This commit is contained in:
Slava Pestov
2022-03-25 21:48:31 -04:00
parent 3d2559f79e
commit 4d097da73c
28 changed files with 211 additions and 65 deletions

View File

@@ -535,13 +535,16 @@ GenericSignature buildGenericSignature(
/// Summary of error conditions detected by the Requirement Machine.
enum class GenericSignatureErrorFlags {
/// The original requirements referenced a non-existent type parameter.
HasUnresolvedType = (1<<0),
/// The original requirements were in conflict with each other, meaning
/// The original requirements referenced a non-existent type parameter,
/// or the original requirements were in conflict with each other, meaning
/// there are no possible concrete substitutions which statisfy the
/// generic signature.
HasConflict = (1<<1),
HasInvalidRequirements = (1<<0),
/// The generic signature had non-redundant concrete conformance
/// requirements, which means the rewrite system used for minimization
/// must be discarded and a new one built for queries.
HasConcreteConformances = (1<<1),
/// The Knuth-Bendix completion procedure failed to construct a confluent
/// rewrite system.

View File

@@ -1067,7 +1067,9 @@ void swift::validateGenericSignature(ASTContext &context,
GenericSignatureWithError());
// If there were any errors, the signature was invalid.
if (newSigWithError.getInt()) {
auto errorFlags = newSigWithError.getInt();
if (errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements) ||
errorFlags.contains(GenericSignatureErrorFlags::CompletionFailed)) {
context.Diags.diagnose(SourceLoc(), diag::generic_signature_not_valid,
sig->getAsString());
}

View File

@@ -8275,7 +8275,7 @@ AbstractGenericSignatureRequest::evaluate(
if (!rqmResult.getPointer() && !gsbResult.getPointer())
return rqmResult;
if (!rqmResult.getInt().contains(GenericSignatureErrorFlags::HasConflict) &&
if (!rqmResult.getInt().contains(GenericSignatureErrorFlags::HasInvalidRequirements) &&
!rqmResult.getInt().contains(GenericSignatureErrorFlags::CompletionFailed) &&
!rqmResult.getPointer()->isEqual(gsbResult.getPointer())) {
PrintOptions opts;
@@ -8427,7 +8427,7 @@ AbstractGenericSignatureRequestGSB::evaluate(
GenericSignatureErrors errorFlags;
if (builder.hadAnyError())
errorFlags |= GenericSignatureErrorFlags::HasUnresolvedType;
errorFlags |= GenericSignatureErrorFlags::HasInvalidRequirements;
auto result = std::move(builder).computeGenericSignature(
/*allowConcreteGenericParams=*/true);
return GenericSignatureWithError(result, errorFlags);
@@ -8489,7 +8489,7 @@ InferredGenericSignatureRequest::evaluate(
if (!rqmResult.getPointer() && !gsbResult.getPointer())
return rqmResult;
if (!rqmResult.getInt().contains(GenericSignatureErrorFlags::HasConflict) &&
if (!rqmResult.getInt().contains(GenericSignatureErrorFlags::HasInvalidRequirements) &&
!rqmResult.getInt().contains(GenericSignatureErrorFlags::CompletionFailed) &&
!rqmResult.getPointer()->isEqual(gsbResult.getPointer())) {
PrintOptions opts;
@@ -8635,7 +8635,7 @@ InferredGenericSignatureRequestGSB::evaluate(
GenericSignatureErrors errorFlags;
if (builder.hadAnyError())
errorFlags |= GenericSignatureErrorFlags::HasUnresolvedType;
errorFlags |= GenericSignatureErrorFlags::HasInvalidRequirements;
auto result = std::move(builder).computeGenericSignature(
allowConcreteGenericParams);
return GenericSignatureWithError(result, errorFlags);
@@ -8705,7 +8705,7 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator,
auto rqmResult = buildViaRQM();
auto gsbResult = buildViaGSB();
if (!rqmResult.getErrors().contains(GenericSignatureErrorFlags::HasConflict) &&
if (!rqmResult.getErrors().contains(GenericSignatureErrorFlags::HasInvalidRequirements) &&
!rqmResult.getErrors().contains(GenericSignatureErrorFlags::CompletionFailed) &&
!compare(rqmResult.getRequirements(),
gsbResult.getRequirements())) {

View File

@@ -471,6 +471,7 @@ void RewriteSystem::minimizeRewriteSystem() {
assert(Complete);
assert(!Minimized);
assert(!Frozen);
Minimized = 1;
propagateExplicitBits();
@@ -626,7 +627,7 @@ GenericSignatureErrors RewriteSystem::getErrors() const {
GenericSignatureErrors result;
for (const auto &rule : Rules) {
for (const auto &rule : getLocalRules()) {
if (rule.isPermanent())
continue;
@@ -636,10 +637,15 @@ GenericSignatureErrors RewriteSystem::getErrors() const {
if (!rule.isRedundant() &&
!rule.isProtocolTypeAliasRule() &&
rule.containsUnresolvedSymbols())
result |= GenericSignatureErrorFlags::HasUnresolvedType;
result |= GenericSignatureErrorFlags::HasInvalidRequirements;
if (rule.isConflicting())
result |= GenericSignatureErrorFlags::HasConflict;
result |= GenericSignatureErrorFlags::HasInvalidRequirements;
if (!rule.isRedundant())
if (auto property = rule.isPropertyRule())
if (property->getKind() == Symbol::Kind::ConcreteConformance)
result |= GenericSignatureErrorFlags::HasConcreteConformances;
}
return result;

View File

@@ -287,6 +287,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount,
unsigned maxRuleLength) {
assert(Initialized);
assert(!Minimized);
assert(!Frozen);
// Complete might already be set, if we're re-running completion after
// adding new rules in the property map's concrete type unification procedure.

View File

@@ -332,6 +332,10 @@ RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy) {
return std::make_pair(CompletionResult::Success, 0);
}
void RequirementMachine::freeze() {
System.freeze();
}
std::string RequirementMachine::getRuleAsStringForDiagnostics(
unsigned ruleID) const {
const auto &rule = System.getRule(ruleID);

View File

@@ -113,6 +113,8 @@ class RequirementMachine final {
std::pair<CompletionResult, unsigned>
computeCompletion(RewriteSystem::ValidityPolicy policy);
void freeze();
MutableTerm getLongestValidPrefix(const MutableTerm &term) const;
void buildRequirementsFromRules(
@@ -159,8 +161,8 @@ public:
llvm::DenseMap<const ProtocolDecl *, RequirementSignature>
computeMinimalProtocolRequirements();
std::vector<Requirement>
computeMinimalGenericSignatureRequirements(bool reconstituteSugar);
GenericSignature
computeMinimalGenericSignature(bool reconstituteSugar);
ArrayRef<Rule> getLocalRules() const;

View File

@@ -288,7 +288,8 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
auto minimalRequirements = machine->computeMinimalProtocolRequirements();
if (!machine->getErrors()) {
if (!machine->getErrors().contains(
GenericSignatureErrorFlags::HasInvalidRequirements)) {
if (shouldSplitConcreteEquivalenceClasses(minimalRequirements, machine.get())) {
++attempt;
splitConcreteEquivalenceClasses(ctx, minimalRequirements,
@@ -345,15 +346,20 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
/*allowConcreteGenericParams=*/false);
}
if (!machine->getErrors())
rewriteCtx.installRequirementMachine(proto, std::move(machine));
// Return the result for the specific protocol this request was kicked off on.
return *result;
}
}
/// Builds the top-level generic signature requirements for this rewrite system.
std::vector<Requirement>
RequirementMachine::computeMinimalGenericSignatureRequirements(
GenericSignature
RequirementMachine::computeMinimalGenericSignature(
bool reconstituteSugar) {
assert(!Sig &&
"Already computed minimal generic signature");
assert(System.getProtocols().empty() &&
"Not a top-level generic signature rewrite system");
assert(!Params.empty() &&
@@ -375,7 +381,14 @@ RequirementMachine::computeMinimalGenericSignatureRequirements(
reconstituteSugar, reqs, aliases);
assert(aliases.empty());
return reqs;
auto sig = GenericSignature::get(getGenericParams(), reqs);
// Remember the signature for generic signature queries. In particular,
// getConformanceAccessPath() needs the current requirement machine's
// generic signature.
Sig = sig.getCanonicalSignature();
return sig;
}
/// Check whether the inputs to the \c AbstractGenericSignatureRequest are
@@ -549,14 +562,11 @@ AbstractGenericSignatureRequestRQM::evaluate(
// We pass reconstituteSugar=false to ensure that if the original
// requirements were canonical, the final signature remains canonical.
auto minimalRequirements =
machine->computeMinimalGenericSignatureRequirements(
auto result = machine->computeMinimalGenericSignature(
/*reconstituteSugar=*/false);
auto result = GenericSignature::get(genericParams, minimalRequirements);
auto errorFlags = machine->getErrors();
if (!errorFlags) {
if (!errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements)) {
if (shouldSplitConcreteEquivalenceClasses(result.getRequirements(),
/*proto=*/nullptr,
machine.get())) {
@@ -567,7 +577,19 @@ AbstractGenericSignatureRequestRQM::evaluate(
requirements, attempt);
continue;
}
}
if (!errorFlags) {
// If this signature was minimized without errors or non-redundant
// concrete conformances, we can re-use the requirement machine for
// subsequent queries, instead of building a new requirement machine
// from the minimized signature. Do this before verify(), which
// performs queries.
rewriteCtx.installRequirementMachine(result.getCanonicalSignature(),
std::move(machine));
}
if (!errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements)) {
// Check invariants.
result.verify();
}
@@ -731,11 +753,8 @@ InferredGenericSignatureRequestRQM::evaluate(
result, GenericSignatureErrorFlags::CompletionFailed);
}
auto minimalRequirements =
machine->computeMinimalGenericSignatureRequirements(
auto result = machine->computeMinimalGenericSignature(
/*reconstituteSugar=*/true);
auto result = GenericSignature::get(genericParams, minimalRequirements);
auto errorFlags = machine->getErrors();
if (attempt == 0 &&
@@ -747,7 +766,7 @@ InferredGenericSignatureRequestRQM::evaluate(
// FIXME: Handle allowConcreteGenericParams
if (!errorFlags) {
if (!errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements)) {
// Check if we need to rebuild the signature.
if (shouldSplitConcreteEquivalenceClasses(result.getRequirements(),
/*proto=*/nullptr,
@@ -759,7 +778,19 @@ InferredGenericSignatureRequestRQM::evaluate(
requirements, attempt);
continue;
}
}
if (!errorFlags) {
// If this signature was minimized without errors or non-redundant
// concrete conformances, we can re-use the requirement machine for
// subsequent queries, instead of building a new requirement machine
// from the minimized signature. Do this before verify(), which
// performs queries.
rewriteCtx.installRequirementMachine(result.getCanonicalSignature(),
std::move(machine));
}
if (!errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements)) {
// Check invariants.
result.verify();
}

View File

@@ -195,6 +195,21 @@ bool RewriteContext::isRecursivelyConstructingRequirementMachine(
return !found->second->isComplete();
}
/// Given a requirement machine that built a minimized signature, attempt to
/// re-use it for subsequent queries against the minimized signature, instead
/// of building a new one later.
void RewriteContext::installRequirementMachine(
CanGenericSignature sig,
std::unique_ptr<RequirementMachine> machine) {
auto *machinePtr = machine.release();
machinePtr->freeze();
auto inserted = Machines.insert(std::make_pair(sig, machinePtr)).second;
if (!inserted)
delete machinePtr;
}
/// Implement Tarjan's algorithm to compute strongly-connected components in
/// the protocol dependency graph.
void RewriteContext::getProtocolComponentRec(
@@ -370,6 +385,11 @@ void RewriteContext::finishComputingRequirementSignatures(
/// for being built with the same component.
RequirementMachine *RewriteContext::getRequirementMachine(
const ProtocolDecl *proto) {
// First, get the requirement signature. If this protocol was written in
// source, we'll minimize it and install the machine below, saving us the
// effort of recomputing it.
(void) proto->getRequirementSignature();
auto &component = getProtocolComponentImpl(proto);
if (component.Machine) {
@@ -435,6 +455,24 @@ bool RewriteContext::isRecursivelyConstructingRequirementMachine(
!component->second.ComputedRequirementSignatures);
}
/// Given a reuirement machine that built the requirement signatures for a
/// protocol connected component, attempt to re-use it for subsequent
/// queries against the connected component, instead of building a new one
/// later.
void RewriteContext::installRequirementMachine(
const ProtocolDecl *proto,
std::unique_ptr<RequirementMachine> machine) {
auto *machinePtr = machine.release();
machinePtr->freeze();
auto &component = getProtocolComponentImpl(proto);
if (component.Machine == nullptr)
component.Machine = machinePtr;
else
delete machinePtr;
}
/// We print stats in the destructor, which should get executed at the end of
/// a compilation job.
RewriteContext::~RewriteContext() {

View File

@@ -206,6 +206,9 @@ public:
RequirementMachine *getRequirementMachine(CanGenericSignature sig);
bool isRecursivelyConstructingRequirementMachine(CanGenericSignature sig);
void installRequirementMachine(CanGenericSignature sig,
std::unique_ptr<RequirementMachine> machine);
ArrayRef<const ProtocolDecl *>
startComputingRequirementSignatures(const ProtocolDecl *proto);
@@ -214,6 +217,9 @@ public:
RequirementMachine *getRequirementMachine(const ProtocolDecl *proto);
bool isRecursivelyConstructingRequirementMachine(const ProtocolDecl *proto);
void installRequirementMachine(const ProtocolDecl *proto,
std::unique_ptr<RequirementMachine> machine);
~RewriteContext();
};

View File

@@ -29,6 +29,7 @@ RewriteSystem::RewriteSystem(RewriteContext &ctx)
Initialized = 0;
Complete = 0;
Minimized = 0;
Frozen = 0;
RecordLoops = 0;
LongestInitialRule = 0;
}
@@ -170,9 +171,7 @@ bool RewriteSystem::simplify(MutableTerm &term, RewritePath *path) const {
/// \p lhs to \p rhs.
bool RewriteSystem::addRule(MutableTerm lhs, MutableTerm rhs,
const RewritePath *path) {
// FIXME:
// assert(!Complete || path != nullptr &&
// "Rules added by completion must have a path");
assert(!Frozen);
assert(!lhs.empty());
assert(!rhs.empty());
@@ -494,6 +493,8 @@ bool RewriteSystem::isInMinimizationDomain(const ProtocolDecl *proto) const {
void RewriteSystem::recordRewriteLoop(MutableTerm basepoint,
RewritePath path) {
assert(!Frozen);
RewriteLoop loop(basepoint, path);
loop.verify(*this);
@@ -727,6 +728,30 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
}
}
/// Free up memory by purging unused data structures after completion
/// (for a rewrite system built from a generic signature) or minimization
/// (for a rewrite system built from user-written requirements).
void RewriteSystem::freeze() {
assert(Complete);
assert(!Frozen);
for (unsigned ruleID = FirstLocalRule, e = Rules.size();
ruleID < e; ++ruleID) {
getRule(ruleID).freeze();
}
WrittenRequirements.clear();
CheckedOverlaps.clear();
RelationMap.clear();
Relations.clear();
DifferenceMap.clear();
Differences.clear();
CheckedDifferences.clear();
Loops.clear();
RedundantRules.clear();
ConflictingRules.clear();
}
void RewriteSystem::dump(llvm::raw_ostream &out) const {
out << "Rewrite system: {\n";
for (const auto &rule : Rules) {

View File

@@ -102,6 +102,10 @@ class RewriteSystem final {
/// Whether we've minimized the rewrite system.
unsigned Minimized : 1;
/// Whether the rewrite system is finalized, immutable, and ready for
/// generic signature queries.
unsigned Frozen : 1;
/// If set, the completion procedure records rewrite loops describing the
/// identities among rewrite rules discovered while resolving critical pairs.
unsigned RecordLoops : 1;
@@ -403,6 +407,8 @@ private:
const llvm::DenseSet<unsigned> &redundantConformances) const;
public:
void freeze();
void dump(llvm::raw_ostream &out) const;
};

View File

@@ -77,6 +77,9 @@ class Rule final {
/// dropped from the minimal set of requirements.
unsigned Conflicting : 1;
/// Whether this rule is now finalized and immutable.
unsigned Frozen : 1;
public:
Rule(Term lhs, Term rhs)
: LHS(lhs), RHS(rhs) {
@@ -87,6 +90,7 @@ public:
SubstitutionSimplified = false;
Redundant = false;
Conflicting = false;
Frozen = false;
}
const Term &getLHS() const { return LHS; }
@@ -97,6 +101,7 @@ public:
}
void setRequirementID(Optional<unsigned> requirementID) {
assert(!Frozen);
this->requirementID = requirementID;
}
@@ -141,6 +146,10 @@ public:
return Conflicting;
}
bool isFrozen() const {
return Frozen;
}
bool containsUnresolvedSymbols() const {
return (LHS.containsUnresolvedSymbols() ||
RHS.containsUnresolvedSymbols());
@@ -151,44 +160,57 @@ public:
bool isDerivedFromConcreteProtocolTypeAliasRule() const;
void markLHSSimplified() {
assert(!Frozen);
assert(!LHSSimplified);
LHSSimplified = true;
}
void markRHSSimplified() {
assert(!Frozen);
assert(!RHSSimplified);
RHSSimplified = true;
}
void markSubstitutionSimplified() {
assert(!Frozen);
assert(!SubstitutionSimplified);
SubstitutionSimplified = true;
}
void markPermanent() {
assert(!Frozen);
assert(!Explicit && !Permanent &&
"Permanent and explicit are mutually exclusive");
Permanent = true;
}
void markExplicit() {
assert(!Frozen);
assert(!Explicit && !Permanent &&
"Permanent and explicit are mutually exclusive");
Explicit = true;
}
void markRedundant() {
assert(!Frozen);
assert(!Redundant);
Redundant = true;
}
void markConflicting() {
assert(!Frozen);
// It's okay to mark a rule as conflicting multiple times, but it must not
// be a permanent rule.
assert(!Permanent && "Permanent rule should not conflict with anything");
Conflicting = true;
}
void freeze() {
Redundant = false;
requirementID = None;
Frozen = true;
}
unsigned getDepth() const;
unsigned getNesting() const;

View File

@@ -259,8 +259,15 @@ void RewriteSystem::processTypeDifference(const TypeDifference &difference,
// the same rewrite loop in concretelySimplifyLeftHandSideSubstitutions().
auto &lhsRule = getRule(lhsRuleID);
if (lhsRule.getRHS() == difference.BaseTerm &&
!lhsRule.isSubstitutionSimplified())
!lhsRule.isSubstitutionSimplified()) {
if (lhsRule.isFrozen()) {
llvm::errs() << "Frozen rule should already be subst-simplified: "
<< lhsRule << "\n\n";
dump(llvm::errs());
abort();
}
lhsRule.markSubstitutionSimplified();
}
}
/// Simplify terms appearing in the substitutions of the last symbol of \p term,

View File

@@ -11,7 +11,7 @@ func foo<T : Generic<U>, U>(_: T, _: U) {
_ = U.self
}
// CHECK-LABEL: Requirement machine for <τ_0_0, τ_0_1 where τ_0_0 : Generic<τ_0_1>>
// CHECK: Requirement machine for fresh signature < T U >
// CHECK-NEXT: Rewrite system: {
// CHECK-NEXT: - τ_0_0.[superclass: Generic<τ_0_1>] => τ_0_0
// CHECK-NEXT: - τ_0_0.[layout: AnyObject] => τ_0_0

View File

@@ -21,19 +21,19 @@ public func test<T : P>(_ t: T) where T == T.A {
//
// The rewrite system handles this correctly though:
// CHECK-LABEL: Requirement machine for <τ_0_0, τ_0_1 where τ_0_0 == τ_0_0.A, τ_0_1 : P, τ_0_0.A == τ_0_1.A>
// CHECK-LABEL: Requirement machine for fresh signature < T U >
// CHECK-NEXT: Rewrite system: {
// CHECK-NEXT: - [P].[P] => [P] [permanent]
// CHECK-NEXT: - [P].A => [P:A] [permanent]
// CHECK-NEXT: - [P:A].[P] => [P:A]
// CHECK-NEXT: - [P].[P:A] => [P:A]
// CHECK-NEXT: - [P:A].A => [P:A].[P:A]
// CHECK-NEXT: - τ_0_0.[P:A] => τ_0_0
// CHECK-NEXT: - τ_0_0.A => τ_0_0
// CHECK-NEXT: - τ_0_1.[P] => τ_0_1
// CHECK-NEXT: - τ_0_1.A => τ_0_0
// CHECK-NEXT: - τ_0_1.[P:A] => τ_0_0
// CHECK-NEXT: - τ_0_0.[P] => τ_0_0
// CHECK-NEXT: - τ_0_0.A => τ_0_0
// CHECK-NEXT: - τ_0_1.A => τ_0_0
// CHECK-NEXT: - τ_0_0.[P:A] => τ_0_0
// CHECK-NEXT: }
// CHECK: Property map: {
// CHECK-NEXT: [P] => { conforms_to: [P] }

View File

@@ -26,11 +26,11 @@ protocol PBad {
// FIXME: Terrible diagnostics.
protocol PWorse {
// expected-error@-1 4{{circular reference}}
// expected-note@-2 6{{through reference here}}
// expected-error@-1 5{{circular reference}}
// expected-note@-2 9{{through reference here}}
typealias A = C
associatedtype T : Self.A
// expected-note@-1 4{{while resolving type 'Self.A'}}
// expected-note@-2 4{{through reference here}}
// expected-note@-1 5{{while resolving type 'Self.A'}}
// expected-note@-2 5{{through reference here}}
}

View File

@@ -20,7 +20,7 @@ protocol P2a {
struct MergeTest<G : P1a & P2a> {}
// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1a, τ_0_0 : P2a> {
// CHECK-LABEL: Requirement machine for fresh signature < G >
// CHECK-LABEL: Rewrite system: {
// CHECK: - τ_0_0.[P2a:T] => τ_0_0.[P1a:T]
// CHECK: - τ_0_0.[P1a:T].[P2] => τ_0_0.[P1a:T]
@@ -30,5 +30,3 @@ struct MergeTest<G : P1a & P2a> {}
// CHECK: τ_0_0 => { conforms_to: [P1a P2a] }
// CHECK: τ_0_0.[P1a:T] => { conforms_to: [P1 P2] }
// CHECK: }
// CHECK: }

View File

@@ -19,7 +19,7 @@ struct MergeTest<G : P1 & P2> {
func foo2(x: G.Z1) -> G.Z2 { return x }
}
// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> {
// CHECK-LABEL: Requirement machine for fresh signature < G >
// CHECK-LABEL: Rewrite system: {
// CHECK: - τ_0_0.[P2:Y2] => τ_0_0.[P1:Y1]
// CHECK: - τ_0_0.[P2:Z2] => τ_0_0.[P1:Z1]

View File

@@ -27,10 +27,9 @@ struct MergeTest<G : P1a & P2a> {
func foo2(x: G.T.Z1) -> G.T.Z2 { return x }
}
// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1a, τ_0_0 : P2a> {
// CHECK-LABEL: Requirement machine for fresh signature < G >
// CHECK-LABEL: Rewrite system: {
// CHECK: - τ_0_0.[P2a:T] => τ_0_0.[P1a:T]
// CHECK: - τ_0_0.[P1a:T].[P2:Y2] => τ_0_0.[P1a:T].[P1:Y1]
// CHECK: - τ_0_0.[P1a:T].[P2:Z2] => τ_0_0.[P1a:T].[P1:Z1]
// CHECK: }
// CHECK: }

View File

@@ -17,10 +17,9 @@ struct G<T : P1 & P2> {}
// Since G.T.T == G.T.T.T == G.T.T.T.T = ... = Int, we tie off the
// recursion by introducing a same-type requirement G.T.T => G.T.
// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> {
// CHECK-LABEL: Requirement machine for fresh signature < T >
// CHECK-LABEL: Rewrite system: {
// CHECK: - [P1:T].T => [P1:T].[P1:T]
// CHECK: - τ_0_0.[P1:T].[concrete: Int] => τ_0_0.[P1:T]
// CHECK: - τ_0_0.[P1:T].[P1:T] => τ_0_0.[P1:T]
// CHECK: }
// CHECK: }

View File

@@ -22,10 +22,9 @@ struct G<T : P1 & P2> {}
// Since G.T.T == G.T.T.T == G.T.T.T.T = ... = X<T.U>, we tie off the
// recursion by introducing a same-type requirement G.T.T => G.T.
// CHECK-LABEL: Adding generic signature <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> {
// CHECK-LABEL: Requirement machine for fresh signature < T >
// CHECK-LABEL: Rewrite system: {
// CHECK: - [P1:T].T => [P1:T].[P1:T]
// CHECK: - τ_0_0.[P1:T].[concrete: X<τ_0_0.[P2:U]>] => τ_0_0.[P1:T]
// CHECK: - τ_0_0.[P1:T].[P1:T] => τ_0_0.[P1:T]
// CHECK: }
// CHECK: }

View File

@@ -26,7 +26,7 @@ struct G<T : P2a & P3a> {}
// X.T has a DependentMemberType in it; make sure that we build the
// correct substitution schema.
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P2a, τ_0_0 : P3a>
// CHECK-LABEL: Requirement machine for fresh signature < T >
// CHECK-LABEL: Rewrite system: {
// CHECK: - τ_0_0.[P2a:T].[concrete: X<τ_0_0.[P3a:U]>] => τ_0_0.[P2a:T]
// CHECK: - τ_0_0.[P2a:T].[P2:T].[concrete: X<τ_0_0.[P3a:U].[P1:T]>] => τ_0_0.[P2a:T].[P2:T]
@@ -35,4 +35,3 @@ struct G<T : P2a & P3a> {}
// CHECK: τ_0_0.[P2a:T] => { conforms_to: [P2] concrete_type: [concrete: X<τ_0_0.[P3a:U]>] }
// CHECK: τ_0_0.[P2a:T].[P2:T] => { concrete_type: [concrete: X<τ_0_0.[P3a:U].[P1:T]>] }
// CHECK: }
// CHECK: }

View File

@@ -40,7 +40,7 @@ struct G<T : P1 & P2> {}
// T.B.A => T.B
// ...
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
// CHECK-LABEL: Requirement machine for fresh signature < T >
// CHECK-LABEL: Rewrite system: {
// CHECK: - τ_0_0.[P1:A].[concrete: S1 : P1] => τ_0_0.[P1:A]
// CHECK: - τ_0_0.[P1:A].[P1:A] => τ_0_0.[P1:A]
@@ -61,4 +61,3 @@ struct G<T : P1 & P2> {}
// CHECK: τ_0_0.[P1:A].[P1:B] => { conforms_to: [P1] concrete_type: [concrete: S2] }
// CHECK: τ_0_0.[P1:B].[P1:B] => { conforms_to: [P1] concrete_type: [concrete: S1] }
// CHECK: }
// CHECK: }

View File

@@ -13,13 +13,13 @@ extension P where Self : Derived {
func passesDerived() { derivedMethod() }
}
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : Derived, τ_0_0 : P>
// CHECK-LABEL: Requirement machine for fresh signature < Self >
// CHECK-NEXT: Rewrite system: {
// CHECK-NEXT: - [P].[P] => [P] [permanent]
// CHECK-NEXT: - [P].[superclass: Base] => [P]
// CHECK-NEXT: - [P].[layout: _NativeClass] => [P]
// CHECK-NEXT: - τ_0_0.[superclass: Derived] => τ_0_0
// CHECK-NEXT: - τ_0_0.[P] => τ_0_0
// CHECK-NEXT: - τ_0_0.[superclass: Derived] => τ_0_0
// CHECK-NEXT: - τ_0_0.[superclass: Base] => τ_0_0
// CHECK-NEXT: - τ_0_0.[layout: _NativeClass] => τ_0_0
// CHECK-NEXT: }

View File

@@ -38,10 +38,10 @@ func unifySuperclassTest<T : P1 & P2>(_: T) {
// CHECK-RULE-NEXT: Rewrite system: {
// CHECK-RULE: - τ_0_0.[P2:X] => τ_0_0.[P1:X]
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<τ_0_0.[P2:A2], String, τ_0_0.[P2:B2]>] => τ_0_0.[P1:X]
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<Int, String, τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
// CHECK-RULE: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
// CHECK-RULE: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
// CHECK-RULE: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
// CHECK-RULE: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<Int, String, τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
// CHECK-RULE: - τ_0_0.B2 => τ_0_0.[P1:B1]
// CHECK-RULE: }
// CHECK-RULE: Property map: {

View File

@@ -42,10 +42,10 @@ func unifySuperclassTest<T : P1 & P2>(_: T) {
// CHECK-RULE: - [P2:X].[layout: _NativeClass] => [P2:X]
// CHECK-RULE: - τ_0_0.[P2:X] => τ_0_0.[P1:X]
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<Int, τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<Int, String, τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
// CHECK-RULE: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2]
// CHECK-RULE: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
// CHECK-RULE: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1]
// CHECK-RULE: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1]
// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<Int, String, τ_0_0.[P1:B1]>] => τ_0_0.[P1:X]
// CHECK-RULE: - τ_0_0.B2 => τ_0_0.[P1:B1]
// CHECK-RULE: }
// CHECK-RULE: Property map: {

View File

@@ -33,7 +33,7 @@ func unifySuperclassTest<T : P1 & P2>(_: T) {
takesDerived(T.X.self, T.A2.self)
}
// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2>
// CHECK-LABEL: Requirement machine for fresh signature < T >
// CHECK-NEXT: Rewrite system: {
// CHECK: - [P1:X].[superclass: Base<[P1:A1]>] => [P1:X] [explicit]
// CHECK: - [P1:X].[layout: _NativeClass] => [P1:X]
@@ -48,8 +48,8 @@ func unifySuperclassTest<T : P1 & P2>(_: T) {
// CHECK-NEXT: [P1] => { conforms_to: [P1] }
// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Base<[P1:A1]>] }
// CHECK-NEXT: [P2] => { conforms_to: [P2] }
// CHECK-NEXT: [P2:A2] => { conforms_to: [Q] }
// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Derived<[P2:A2]>] }
// CHECK-NEXT: [P2:A2] => { conforms_to: [Q] }
// CHECK-NEXT: [Q] => { conforms_to: [Q] }
// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] }
// CHECK-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0.[P2:A2]>] }