Skip to content

Commit 02dd1e4

Browse files
committed
[Archetype builder] Use protocol conformances of superclass to resolve nested types.
When a type parameter has a superclass constraint, the conformances of the superclass can resolve some nested types of the type parameter via the type witnesses for the corresponding associated types. Fixes rdar://problem/24730536.
1 parent 3e9bfa1 commit 02dd1e4

File tree

3 files changed

+147
-5
lines changed

3 files changed

+147
-5
lines changed

include/swift/AST/ArchetypeBuilder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ class ArchetypeBuilder {
133133
ProtocolDecl *Proto,
134134
RequirementSource Source,
135135
llvm::SmallPtrSetImpl<ProtocolDecl *> &Visited);
136-
136+
137+
public:
137138
/// \brief Add a new conformance requirement specifying that the given
138139
/// potential archetypes are equivalent.
139140
bool addSameTypeRequirementBetweenArchetypes(PotentialArchetype *T1,
@@ -145,7 +146,8 @@ class ArchetypeBuilder {
145146
bool addSameTypeRequirementToConcrete(PotentialArchetype *T,
146147
Type Concrete,
147148
RequirementSource Source);
148-
149+
150+
private:
149151
/// \brief Add a new superclass requirement specifying that the given
150152
/// potential archetype has the given type as an ancestor.
151153
bool addSuperclassRequirement(PotentialArchetype *T,

lib/AST/ArchetypeBuilder.cpp

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,82 @@ void ArchetypeBuilder::PotentialArchetype::resolveAssociatedType(
253253
--builder.Impl->NumUnresolvedNestedTypes;
254254
}
255255

256+
/// Retrieve the conformance for the superclass constraint of the given
257+
/// potential archetype (if present) to the given protocol.
258+
///
259+
/// \param pa The potential archetype whose superclass constraint is being
260+
/// queried.
261+
///
262+
/// \param proto The protocol to which we are establisting conformance.
263+
///
264+
/// \param cache A cache of the result, so we don't perform this query
265+
/// multiple times.
266+
///
267+
/// \param builder The archetype builder in which the potential archetype
268+
/// resides.
269+
static ProtocolConformance *getSuperConformance(
270+
ArchetypeBuilder::PotentialArchetype *pa,
271+
ProtocolDecl *proto,
272+
Optional<ProtocolConformance *> &cache,
273+
ArchetypeBuilder &builder) {
274+
// If we cached a result already, return it.
275+
if (cache) return *cache;
276+
277+
// Get the superclass constraint.
278+
Type superclass = pa->getSuperclass();
279+
if (!superclass) {
280+
cache = nullptr;
281+
return nullptr;
282+
}
283+
284+
// Lookup the conformance of the superclass to this protocol.
285+
auto conformance =
286+
builder.getModule().lookupConformance(superclass, proto,
287+
builder.getLazyResolver());
288+
switch (conformance.getInt()) {
289+
case ConformanceKind::Conforms:
290+
cache = conformance.getPointer();
291+
return conformance.getPointer();
292+
293+
case ConformanceKind::DoesNotConform:
294+
case ConformanceKind::UncheckedConforms:
295+
cache = nullptr;
296+
return nullptr;
297+
}
298+
}
299+
300+
/// If there is a same-type requirement to be added for the given nested type
301+
/// due to a superclass constraint on the parent type, add it now.
302+
static void maybeAddSameTypeRequirementForNestedType(
303+
ArchetypeBuilder::PotentialArchetype *parentPA,
304+
ArchetypeBuilder::PotentialArchetype *nestedPA,
305+
RequirementSource fromSource,
306+
Optional<ProtocolConformance *> &cache,
307+
ArchetypeBuilder &builder) {
308+
auto assocType = nestedPA->getResolvedAssociatedType();
309+
assert(assocType && "Not resolved to an associated type?");
310+
311+
// Find the superclass conformance.
312+
auto proto = assocType->getProtocol();
313+
auto conformance = getSuperConformance(parentPA, proto, cache, builder);
314+
if (!conformance) return;
315+
316+
// Dig out the type witness.
317+
auto concreteType = conformance->getTypeWitness(assocType,
318+
builder.getLazyResolver())
319+
.getReplacement();
320+
if (!concreteType) return;
321+
322+
// Add the same-type constraint.
323+
RequirementSource source(RequirementSource::Protocol, fromSource.getLoc());
324+
concreteType = ArchetypeBuilder::mapTypeOutOfContext(
325+
conformance->getDeclContext(), concreteType);
326+
if (auto otherPA = builder.resolveArchetype(concreteType))
327+
builder.addSameTypeRequirementBetweenArchetypes(nestedPA, otherPA, source);
328+
else
329+
builder.addSameTypeRequirementToConcrete(nestedPA, concreteType, source);
330+
}
331+
256332
bool ArchetypeBuilder::PotentialArchetype::addConformance(
257333
ProtocolDecl *proto,
258334
const RequirementSource &source,
@@ -275,6 +351,7 @@ bool ArchetypeBuilder::PotentialArchetype::addConformance(
275351

276352
// Check whether any associated types in this protocol resolve
277353
// nested types of this potential archetype.
354+
Optional<ProtocolConformance *> cachedSuperConformance;
278355
for (auto member : proto->getMembers()) {
279356
auto assocType = dyn_cast<AssociatedTypeDecl>(member);
280357
if (!assocType)
@@ -287,6 +364,13 @@ bool ArchetypeBuilder::PotentialArchetype::addConformance(
287364
// If the nested type was not already resolved, do so now.
288365
if (!known->second.front()->getResolvedAssociatedType()) {
289366
known->second.front()->resolveAssociatedType(assocType, builder);
367+
368+
// If there's a superclass constraint that conforms to the protocol,
369+
// add the appropriate same-type relationship.
370+
maybeAddSameTypeRequirementForNestedType(this, known->second.front(),
371+
source,
372+
cachedSuperConformance,
373+
builder);
290374
continue;
291375
}
292376

@@ -299,6 +383,13 @@ bool ArchetypeBuilder::PotentialArchetype::addConformance(
299383
otherPA->SameTypeSource = RequirementSource(RequirementSource::Inferred,
300384
source.getLoc());
301385
known->second.push_back(otherPA);
386+
387+
// If there's a superclass constraint that conforms to the protocol,
388+
// add the appropriate same-type relationship.
389+
maybeAddSameTypeRequirementForNestedType(this, otherPA,
390+
source,
391+
cachedSuperConformance,
392+
builder);
302393
}
303394

304395
return true;
@@ -367,8 +458,8 @@ auto ArchetypeBuilder::PotentialArchetype::getNestedType(
367458
if (Representative != this)
368459
return getRepresentative()->getNestedType(nestedName, builder);
369460

461+
// If we already have a nested type with this name, return it.
370462
llvm::TinyPtrVector<PotentialArchetype *> &nested = NestedTypes[nestedName];
371-
372463
if (!nested.empty()) {
373464
return nested.front();
374465
}
@@ -377,6 +468,8 @@ auto ArchetypeBuilder::PotentialArchetype::getNestedType(
377468
// of one of the protocols to which the parent potential
378469
// archetype conforms.
379470
for (const auto &conforms : ConformsTo) {
471+
Optional<ProtocolConformance *> cachedSuperConformance;
472+
380473
for (auto member : conforms.first->lookupDirect(nestedName)) {
381474
auto assocType = dyn_cast<AssociatedTypeDecl>(member);
382475
if (!assocType)
@@ -387,15 +480,21 @@ auto ArchetypeBuilder::PotentialArchetype::getNestedType(
387480

388481
// If we have resolved this nested type to more than one associated
389482
// type, create same-type constraints between them.
483+
RequirementSource source(RequirementSource::Inferred, SourceLoc());
390484
if (!nested.empty()) {
391485
pa->Representative = nested.front()->getRepresentative();
392486
pa->Representative->EquivalenceClass.push_back(pa);
393-
pa->SameTypeSource = RequirementSource(RequirementSource::Inferred,
394-
SourceLoc());
487+
pa->SameTypeSource = source;
395488
}
396489

397490
// Add this resolved nested type.
398491
nested.push_back(pa);
492+
493+
// If there's a superclass constraint that conforms to the protocol,
494+
// add the appropriate same-type relationship.
495+
maybeAddSameTypeRequirementForNestedType(this, pa, source,
496+
cachedSuperConformance,
497+
builder);
399498
}
400499
}
401500

@@ -876,6 +975,18 @@ bool ArchetypeBuilder::addSuperclassRequirement(PotentialArchetype *T,
876975
T->Superclass = Superclass;
877976
T->SuperclassSource = Source;
878977

978+
// Resolve any nested types via the superclass.
979+
for (auto &nested : T->getNestedTypes()) {
980+
Optional<ProtocolConformance *> superclassConformance;
981+
for (auto nestedPA : nested.second) {
982+
if (nestedPA->getResolvedAssociatedType()) {
983+
maybeAddSameTypeRequirementForNestedType(T, nested.second.front(),
984+
Source,
985+
superclassConformance, *this);
986+
}
987+
}
988+
}
989+
879990
return false;
880991
}
881992

test/Generics/superclass_constraint.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// RUN: %target-parse-verify-swift
22

3+
// RUN: %target-parse-verify-swift -parse -debug-generic-signatures %s > %t.dump 2>&1
4+
// RUN: FileCheck %s < %t.dump
5+
36
class A {
47
func foo() { }
58
}
@@ -32,3 +35,29 @@ func f10<T : GB<A> where T : GA<A>>(_: T) { }
3235
func f11<T : GA<T>>(_: T) { } // expected-error{{superclass constraint 'GA<T>' is recursive}}
3336
func f12<T : GA<U>, U : GB<T>>(_: T, _: U) { } // expected-error{{superclass constraint 'GA<U>' is recursive}}
3437
func f13<T : U, U : GA<T>>(_: T, _: U) { } // expected-error{{inheritance from non-protocol, non-class type 'U'}}
38+
39+
// rdar://problem/24730536
40+
// Superclass constraints can be used to resolve nested types to concrete types.
41+
42+
protocol P3 {
43+
associatedtype T
44+
}
45+
46+
protocol P2 {
47+
associatedtype T : P3
48+
}
49+
50+
class C : P3 {
51+
typealias T = Int
52+
}
53+
54+
class S : P2 {
55+
typealias T = C
56+
}
57+
58+
extension P2 where Self.T : C {
59+
// CHECK: superclass_constraint.(file).P2.concreteTypeWitnessViaSuperclass1
60+
// CHECK: Generic signature: <Self where Self : P2, Self.T : C, Self.T : P3, Self.T.T == T>
61+
// CHECK: Canonical generic signature: <τ_0_0 where τ_0_0 : P2, τ_0_0.T : C, τ_0_0.T : P3, τ_0_0.T.T == Int>
62+
func concreteTypeWitnessViaSuperclass1(x: Self.T.T) {}
63+
}

0 commit comments

Comments
 (0)