Skip to content

Commit 8bd863e

Browse files
committed
[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.
1 parent 7a1a41f commit 8bd863e

File tree

5 files changed

+161
-7
lines changed

5 files changed

+161
-7
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,11 @@ class GenericSignatureBuilder::RequirementSource final
889889
/// derived requirement ceases to hold.
890890
bool isSelfDerivedSource(PotentialArchetype *pa) const;
891891

892+
/// Determine whether a requirement \c pa: proto, when formed from this
893+
/// requirement source, is dependent on itself.
894+
bool isSelfDerivedConformance(PotentialArchetype *pa,
895+
ProtocolDecl *proto) const;
896+
892897
/// Retrieve a source location that corresponds to the requirement.
893898
SourceLoc getLoc() const;
894899

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,144 @@ bool RequirementSource::isSelfDerivedSource(PotentialArchetype *pa) const {
271271
return false;
272272
}
273273

274+
/// Replace 'Self' in the given dependent type (\c depTy) with the given
275+
/// potential archetype, producing a new potential archetype that refers to
276+
/// the nested type. This limited operation makes sure that it does not
277+
/// create any new potential archetypes along the way, so it should only be
278+
/// used in cases where we're reconstructing something that we know exists.
279+
static PotentialArchetype *replaceSelfWithPotentialArchetype(
280+
PotentialArchetype *selfPA, Type depTy) {
281+
if (auto depMemTy = depTy->getAs<DependentMemberType>()) {
282+
// Recurse to produce the potential archetype for the base.
283+
auto basePA = replaceSelfWithPotentialArchetype(selfPA,
284+
depMemTy->getBase());
285+
286+
PotentialArchetype *nestedPAByName = nullptr;
287+
288+
auto assocType = depMemTy->getAssocType();
289+
auto name = depMemTy->getName();
290+
auto findNested = [&](PotentialArchetype *pa) -> PotentialArchetype * {
291+
const auto &nested = pa->getNestedTypes();
292+
auto found = nested.find(name);
293+
294+
if (found == nested.end()) return nullptr;
295+
if (found->second.empty()) return nullptr;
296+
297+
// Note that we've found a nested PA by name.
298+
if (!nestedPAByName) {
299+
nestedPAByName = found->second.front();
300+
}
301+
302+
// If we don't have an associated type to look for, we're done.
303+
if (!assocType) return nestedPAByName;
304+
305+
// Look for a nested PA matching the associated type.
306+
for (auto nestedPA : found->second) {
307+
if (nestedPA->getResolvedAssociatedType() == assocType)
308+
return nestedPA;
309+
}
310+
311+
return nullptr;
312+
};
313+
314+
// First, look in the base potential archetype for the member we want.
315+
if (auto result = findNested(basePA))
316+
return result;
317+
318+
// Otherwise, look elsewhere in the equivalence class of the base potential
319+
// archetype.
320+
for (auto otherBasePA : basePA->getEquivalenceClassMembers()) {
321+
if (otherBasePA == basePA) continue;
322+
323+
if (auto result = findNested(otherBasePA))
324+
return result;
325+
}
326+
327+
assert(nestedPAByName && "Didn't find the associated type we wanted");
328+
return nestedPAByName;
329+
}
330+
331+
assert(depTy->is<GenericTypeParamType>() && "missing Self?");
332+
return selfPA;
333+
}
334+
335+
bool RequirementSource::isSelfDerivedConformance(PotentialArchetype *currentPA,
336+
ProtocolDecl *proto) const {
337+
/// Keep track of all of the requirements we've seen along the way. If
338+
/// we see the same requirement twice, it's a self-derived conformance.
339+
llvm::DenseSet<std::pair<PotentialArchetype *, ProtocolDecl *>>
340+
constraintsSeen;
341+
342+
// Note that we've now seen a new constraint, returning true if we've seen
343+
// it before.
344+
auto addConstraint = [&](PotentialArchetype *pa, ProtocolDecl *proto) {
345+
return !constraintsSeen.insert({pa->getRepresentative(), proto}).second;
346+
};
347+
348+
// Insert our end state.
349+
constraintsSeen.insert({currentPA->getRepresentative(), proto});
350+
351+
// Follow from the root of the
352+
std::function<PotentialArchetype *(const RequirementSource *source)>
353+
followFromRoot;
354+
355+
bool sawProtocolRequirement = false;
356+
followFromRoot = [&](const RequirementSource *source) -> PotentialArchetype *{
357+
// Handle protocol requirements.
358+
if (source->kind == ProtocolRequirement) {
359+
sawProtocolRequirement = true;
360+
361+
// Compute the base potential archetype.
362+
auto basePA = followFromRoot(source->parent);
363+
if (!basePA) return nullptr;
364+
365+
// The base potential archetype must conform to the protocol in which
366+
// this requirement results.
367+
if (addConstraint(basePA, source->getProtocolDecl()))
368+
return nullptr;
369+
370+
// If there's no stored type, return the base.
371+
if (!source->getStoredType()) return basePA;
372+
373+
// Follow the dependent type in the protocol requirement.
374+
return replaceSelfWithPotentialArchetype(basePA, source->getStoredType());
375+
}
376+
377+
if (source->kind == Parent) {
378+
// Compute the base potential archetype.
379+
auto basePA = followFromRoot(source->parent);
380+
if (!basePA) return nullptr;
381+
382+
// Add on this associated type.
383+
return replaceSelfWithPotentialArchetype(
384+
basePA,
385+
source->getAssociatedType()->getDeclaredInterfaceType());
386+
}
387+
388+
if (source->parent)
389+
return followFromRoot(source->parent);
390+
391+
// We are at a root, so the root potential archetype is our result.
392+
auto rootPA = source->getRootPotentialArchetype();
393+
394+
// If we haven't seen a protocol requirement, we're done.
395+
if (!sawProtocolRequirement) return rootPA;
396+
397+
// The root archetype might be a nested type, which implies constraints
398+
// for each of the protocols of the associated types referenced (if any).
399+
for (auto pa = rootPA; pa->getParent(); pa = pa->getParent()) {
400+
if (auto assocType = pa->getResolvedAssociatedType()) {
401+
if (addConstraint(pa->getParent(), assocType->getProtocol()))
402+
return nullptr;
403+
}
404+
}
405+
406+
return rootPA;
407+
};
408+
409+
return followFromRoot(this) == nullptr;
410+
}
411+
274412
#define REQUIREMENT_SOURCE_FACTORY_BODY(ProfileArgs, ConstructorArgs, \
275413
NumProtocolDecls, WrittenReq) \
276414
llvm::FoldingSetNodeID nodeID; \
@@ -3312,7 +3450,18 @@ void GenericSignatureBuilder::checkConformanceConstraints(
33123450
return;
33133451

33143452
for (auto &entry : equivClass->conformsTo) {
3315-
checkConstraintList<ProtocolDecl *>(
3453+
// Remove self-derived constraints.
3454+
assert(!entry.second.empty() && "No constraints to work with?");
3455+
entry.second.erase(
3456+
std::remove_if(entry.second.begin(), entry.second.end(),
3457+
[&](const Constraint<ProtocolDecl *> &constraint) {
3458+
return constraint.source->isSelfDerivedConformance(
3459+
constraint.archetype, entry.first);
3460+
}),
3461+
entry.second.end());
3462+
assert(!entry.second.empty() && "All constraints were self-derived!");
3463+
3464+
checkConstraintList<ProtocolDecl *, ProtocolDecl *>(
33163465
genericParams, entry.second,
33173466
[](const Constraint<ProtocolDecl *> &constraint) {
33183467
return true;
@@ -3323,7 +3472,9 @@ void GenericSignatureBuilder::checkConformanceConstraints(
33233472
},
33243473
None,
33253474
diag::redundant_conformance_constraint,
3326-
diag::redundant_conformance_here);
3475+
diag::redundant_conformance_here,
3476+
[](ProtocolDecl *proto) { return proto; },
3477+
/*removeSelfDerived=*/false);
33273478
}
33283479
}
33293480

test/IRGen/same_type_constraints.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public extension P where Foo == DefaultFoo<Self> {
1919

2020
// <rdar://26873036> IRGen crash with derived class declaring same-type constraint on constrained associatedtype.
2121
public class C1<T: Equatable> { }
22-
public class C2<T: Equatable, U: P where T == U.Foo>: C1<T> {}
22+
public class C2<T: Equatable, U: P>: C1<T> where T == U.Foo {}
2323

2424
// CHECK: define{{( protected)?}} swiftcc void @_T021same_type_constraints2C1CfD
2525

test/decl/class/circular_inheritance.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class B : A { } // expected-note{{class 'B' declared here}}
55
class A : C { } // expected-note{{class 'A' declared here}}
66

77
class TrivialCycle : TrivialCycle {} // expected-error{{circular class inheritance TrivialCycle}}
8-
protocol P : P {} // expected-error 3{{circular protocol inheritance P}}
8+
protocol P : P {} // expected-error 2{{circular protocol inheritance P}}
99

1010
class Isomorphism : Automorphism { }
1111
class Automorphism : Automorphism { } // expected-error{{circular class inheritance Automorphism}}

test/decl/protocol/protocols.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,8 @@ protocol CircleStart : CircleEnd { func circle_start() } // expected-error 2{{ci
8282
// expected-note@-1{{protocol 'CircleStart' declared here}}
8383
protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note{{protocol 'CircleEnd' declared here}}
8484

85-
// expected-warning@+2{{redundant conformance constraint 'Self': 'CircleTrivial'}}
86-
// expected-note@+1{{conformance constraint 'Self': 'CircleTrivial' implied here}}
8785
protocol CircleEntry : CircleTrivial { }
88-
protocol CircleTrivial : CircleTrivial { } // expected-error 3{{circular protocol inheritance CircleTrivial}}
86+
protocol CircleTrivial : CircleTrivial { } // expected-error 2{{circular protocol inheritance CircleTrivial}}
8987

9088
struct Circle {
9189
func circle_start() {}

0 commit comments

Comments
 (0)