[GSB] Reimplement self-derived checking for conformance constraints.

The general self-derived check doesn't really make sense for
conformance constraints, because we want to distinguish among
different protocol conformances.
This commit is contained in:
Doug Gregor
2017-03-23 22:53:39 -07:00
parent 7a1a41ff61
commit 8bd863e645
5 changed files with 161 additions and 7 deletions

View File

@@ -889,6 +889,11 @@ public:
/// derived requirement ceases to hold. /// derived requirement ceases to hold.
bool isSelfDerivedSource(PotentialArchetype *pa) const; bool isSelfDerivedSource(PotentialArchetype *pa) const;
/// Determine whether a requirement \c pa: proto, when formed from this
/// requirement source, is dependent on itself.
bool isSelfDerivedConformance(PotentialArchetype *pa,
ProtocolDecl *proto) const;
/// Retrieve a source location that corresponds to the requirement. /// Retrieve a source location that corresponds to the requirement.
SourceLoc getLoc() const; SourceLoc getLoc() const;

View File

@@ -271,6 +271,144 @@ bool RequirementSource::isSelfDerivedSource(PotentialArchetype *pa) const {
return false; return false;
} }
/// Replace 'Self' in the given dependent type (\c depTy) with the given
/// potential archetype, producing a new potential archetype that refers to
/// the nested type. This limited operation makes sure that it does not
/// create any new potential archetypes along the way, so it should only be
/// used in cases where we're reconstructing something that we know exists.
static PotentialArchetype *replaceSelfWithPotentialArchetype(
PotentialArchetype *selfPA, Type depTy) {
if (auto depMemTy = depTy->getAs<DependentMemberType>()) {
// Recurse to produce the potential archetype for the base.
auto basePA = replaceSelfWithPotentialArchetype(selfPA,
depMemTy->getBase());
PotentialArchetype *nestedPAByName = nullptr;
auto assocType = depMemTy->getAssocType();
auto name = depMemTy->getName();
auto findNested = [&](PotentialArchetype *pa) -> PotentialArchetype * {
const auto &nested = pa->getNestedTypes();
auto found = nested.find(name);
if (found == nested.end()) return nullptr;
if (found->second.empty()) return nullptr;
// Note that we've found a nested PA by name.
if (!nestedPAByName) {
nestedPAByName = found->second.front();
}
// If we don't have an associated type to look for, we're done.
if (!assocType) return nestedPAByName;
// Look for a nested PA matching the associated type.
for (auto nestedPA : found->second) {
if (nestedPA->getResolvedAssociatedType() == assocType)
return nestedPA;
}
return nullptr;
};
// First, look in the base potential archetype for the member we want.
if (auto result = findNested(basePA))
return result;
// Otherwise, look elsewhere in the equivalence class of the base potential
// archetype.
for (auto otherBasePA : basePA->getEquivalenceClassMembers()) {
if (otherBasePA == basePA) continue;
if (auto result = findNested(otherBasePA))
return result;
}
assert(nestedPAByName && "Didn't find the associated type we wanted");
return nestedPAByName;
}
assert(depTy->is<GenericTypeParamType>() && "missing Self?");
return selfPA;
}
bool RequirementSource::isSelfDerivedConformance(PotentialArchetype *currentPA,
ProtocolDecl *proto) const {
/// Keep track of all of the requirements we've seen along the way. If
/// we see the same requirement twice, it's a self-derived conformance.
llvm::DenseSet<std::pair<PotentialArchetype *, ProtocolDecl *>>
constraintsSeen;
// Note that we've now seen a new constraint, returning true if we've seen
// it before.
auto addConstraint = [&](PotentialArchetype *pa, ProtocolDecl *proto) {
return !constraintsSeen.insert({pa->getRepresentative(), proto}).second;
};
// Insert our end state.
constraintsSeen.insert({currentPA->getRepresentative(), proto});
// Follow from the root of the
std::function<PotentialArchetype *(const RequirementSource *source)>
followFromRoot;
bool sawProtocolRequirement = false;
followFromRoot = [&](const RequirementSource *source) -> PotentialArchetype *{
// Handle protocol requirements.
if (source->kind == ProtocolRequirement) {
sawProtocolRequirement = true;
// Compute the base potential archetype.
auto basePA = followFromRoot(source->parent);
if (!basePA) return nullptr;
// The base potential archetype must conform to the protocol in which
// this requirement results.
if (addConstraint(basePA, source->getProtocolDecl()))
return nullptr;
// If there's no stored type, return the base.
if (!source->getStoredType()) return basePA;
// Follow the dependent type in the protocol requirement.
return replaceSelfWithPotentialArchetype(basePA, source->getStoredType());
}
if (source->kind == Parent) {
// Compute the base potential archetype.
auto basePA = followFromRoot(source->parent);
if (!basePA) return nullptr;
// Add on this associated type.
return replaceSelfWithPotentialArchetype(
basePA,
source->getAssociatedType()->getDeclaredInterfaceType());
}
if (source->parent)
return followFromRoot(source->parent);
// We are at a root, so the root potential archetype is our result.
auto rootPA = source->getRootPotentialArchetype();
// If we haven't seen a protocol requirement, we're done.
if (!sawProtocolRequirement) return rootPA;
// The root archetype might be a nested type, which implies constraints
// for each of the protocols of the associated types referenced (if any).
for (auto pa = rootPA; pa->getParent(); pa = pa->getParent()) {
if (auto assocType = pa->getResolvedAssociatedType()) {
if (addConstraint(pa->getParent(), assocType->getProtocol()))
return nullptr;
}
}
return rootPA;
};
return followFromRoot(this) == nullptr;
}
#define REQUIREMENT_SOURCE_FACTORY_BODY(ProfileArgs, ConstructorArgs, \ #define REQUIREMENT_SOURCE_FACTORY_BODY(ProfileArgs, ConstructorArgs, \
NumProtocolDecls, WrittenReq) \ NumProtocolDecls, WrittenReq) \
llvm::FoldingSetNodeID nodeID; \ llvm::FoldingSetNodeID nodeID; \
@@ -3312,7 +3450,18 @@ void GenericSignatureBuilder::checkConformanceConstraints(
return; return;
for (auto &entry : equivClass->conformsTo) { for (auto &entry : equivClass->conformsTo) {
checkConstraintList<ProtocolDecl *>( // Remove self-derived constraints.
assert(!entry.second.empty() && "No constraints to work with?");
entry.second.erase(
std::remove_if(entry.second.begin(), entry.second.end(),
[&](const Constraint<ProtocolDecl *> &constraint) {
return constraint.source->isSelfDerivedConformance(
constraint.archetype, entry.first);
}),
entry.second.end());
assert(!entry.second.empty() && "All constraints were self-derived!");
checkConstraintList<ProtocolDecl *, ProtocolDecl *>(
genericParams, entry.second, genericParams, entry.second,
[](const Constraint<ProtocolDecl *> &constraint) { [](const Constraint<ProtocolDecl *> &constraint) {
return true; return true;
@@ -3323,7 +3472,9 @@ void GenericSignatureBuilder::checkConformanceConstraints(
}, },
None, None,
diag::redundant_conformance_constraint, diag::redundant_conformance_constraint,
diag::redundant_conformance_here); diag::redundant_conformance_here,
[](ProtocolDecl *proto) { return proto; },
/*removeSelfDerived=*/false);
} }
} }

View File

@@ -19,7 +19,7 @@ public extension P where Foo == DefaultFoo<Self> {
// <rdar://26873036> IRGen crash with derived class declaring same-type constraint on constrained associatedtype. // <rdar://26873036> IRGen crash with derived class declaring same-type constraint on constrained associatedtype.
public class C1<T: Equatable> { } public class C1<T: Equatable> { }
public class C2<T: Equatable, U: P where T == U.Foo>: C1<T> {} public class C2<T: Equatable, U: P>: C1<T> where T == U.Foo {}
// CHECK: define{{( protected)?}} swiftcc void @_T021same_type_constraints2C1CfD // CHECK: define{{( protected)?}} swiftcc void @_T021same_type_constraints2C1CfD

View File

@@ -5,7 +5,7 @@ class B : A { } // expected-note{{class 'B' declared here}}
class A : C { } // expected-note{{class 'A' declared here}} class A : C { } // expected-note{{class 'A' declared here}}
class TrivialCycle : TrivialCycle {} // expected-error{{circular class inheritance TrivialCycle}} class TrivialCycle : TrivialCycle {} // expected-error{{circular class inheritance TrivialCycle}}
protocol P : P {} // expected-error 3{{circular protocol inheritance P}} protocol P : P {} // expected-error 2{{circular protocol inheritance P}}
class Isomorphism : Automorphism { } class Isomorphism : Automorphism { }
class Automorphism : Automorphism { } // expected-error{{circular class inheritance Automorphism}} class Automorphism : Automorphism { } // expected-error{{circular class inheritance Automorphism}}

View File

@@ -82,10 +82,8 @@ protocol CircleStart : CircleEnd { func circle_start() } // expected-error 2{{ci
// expected-note@-1{{protocol 'CircleStart' declared here}} // expected-note@-1{{protocol 'CircleStart' declared here}}
protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note{{protocol 'CircleEnd' declared here}} protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note{{protocol 'CircleEnd' declared here}}
// expected-warning@+2{{redundant conformance constraint 'Self': 'CircleTrivial'}}
// expected-note@+1{{conformance constraint 'Self': 'CircleTrivial' implied here}}
protocol CircleEntry : CircleTrivial { } protocol CircleEntry : CircleTrivial { }
protocol CircleTrivial : CircleTrivial { } // expected-error 3{{circular protocol inheritance CircleTrivial}} protocol CircleTrivial : CircleTrivial { } // expected-error 2{{circular protocol inheritance CircleTrivial}}
struct Circle { struct Circle {
func circle_start() {} func circle_start() {}