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.
|
/// 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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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}}
|
||||||
|
|||||||
@@ -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() {}
|
||||||
|
|||||||
Reference in New Issue
Block a user