mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -889,6 +889,11 @@ public:
|
||||
/// derived requirement ceases to hold.
|
||||
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.
|
||||
SourceLoc getLoc() const;
|
||||
|
||||
|
||||
@@ -271,6 +271,144 @@ bool RequirementSource::isSelfDerivedSource(PotentialArchetype *pa) const {
|
||||
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, \
|
||||
NumProtocolDecls, WrittenReq) \
|
||||
llvm::FoldingSetNodeID nodeID; \
|
||||
@@ -3312,7 +3450,18 @@ void GenericSignatureBuilder::checkConformanceConstraints(
|
||||
return;
|
||||
|
||||
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,
|
||||
[](const Constraint<ProtocolDecl *> &constraint) {
|
||||
return true;
|
||||
@@ -3323,7 +3472,9 @@ void GenericSignatureBuilder::checkConformanceConstraints(
|
||||
},
|
||||
None,
|
||||
diag::redundant_conformance_constraint,
|
||||
diag::redundant_conformance_here);
|
||||
diag::redundant_conformance_here,
|
||||
[](ProtocolDecl *proto) { return proto; },
|
||||
/*removeSelfDerived=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
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
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ class B : A { } // expected-note{{class 'B' declared here}}
|
||||
class A : C { } // expected-note{{class 'A' declared here}}
|
||||
|
||||
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 Automorphism : Automorphism { } // expected-error{{circular class inheritance Automorphism}}
|
||||
|
||||
@@ -82,10 +82,8 @@ protocol CircleStart : CircleEnd { func circle_start() } // expected-error 2{{ci
|
||||
// expected-note@-1{{protocol 'CircleStart' 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 CircleTrivial : CircleTrivial { } // expected-error 3{{circular protocol inheritance CircleTrivial}}
|
||||
protocol CircleTrivial : CircleTrivial { } // expected-error 2{{circular protocol inheritance CircleTrivial}}
|
||||
|
||||
struct Circle {
|
||||
func circle_start() {}
|
||||
|
||||
Reference in New Issue
Block a user