Skip to content

Commit 1116f48

Browse files
committed
Don't drop requirements from potential archetypes if they came from a child
It was possible to create a same-type requirement between a type and one of its associated types. If one of those associated types had inferred conformance constraints, we would drop them from the parent type. This is not actually correct because then we have no way to recover metadata for the parent type. The underlying problem has been present for a long time, however this particular failure mode was new to Swift 3, when we stopped passing metadata for associated types. Fixes <rdar://problem/27018457>.
1 parent 4d09c18 commit 1116f48

File tree

4 files changed

+42
-8
lines changed

4 files changed

+42
-8
lines changed

include/swift/AST/ArchetypeBuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,8 @@ class ArchetypeBuilder::PotentialArchetype {
552552
/// Add a conformance to this potential archetype.
553553
///
554554
/// \returns true if the conformance was new, false if it already existed.
555-
bool addConformance(ProtocolDecl *proto, const RequirementSource &source,
555+
bool addConformance(ProtocolDecl *proto, bool updateExistingSource,
556+
const RequirementSource &source,
556557
ArchetypeBuilder &builder);
557558

558559
/// Retrieve the superclass of this archetype.

lib/AST/ArchetypeBuilder.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -322,18 +322,20 @@ static void maybeAddSameTypeRequirementForNestedType(
322322

323323
bool ArchetypeBuilder::PotentialArchetype::addConformance(
324324
ProtocolDecl *proto,
325+
bool updateExistingSource,
325326
const RequirementSource &source,
326327
ArchetypeBuilder &builder) {
327328
auto rep = getRepresentative();
328329
if (rep != this)
329-
return rep->addConformance(proto, source, builder);
330+
return rep->addConformance(proto, updateExistingSource, source, builder);
330331

331332
// Check whether we already know about this conformance.
332333
auto known = ConformsTo.find(proto);
333334
if (known != ConformsTo.end()) {
334335
// We already have this requirement. Update the requirement source
335336
// appropriately.
336-
updateRequirementSource(known->second, source);
337+
if (updateExistingSource)
338+
updateRequirementSource(known->second, source);
337339
return false;
338340
}
339341

@@ -963,7 +965,7 @@ bool ArchetypeBuilder::addConformanceRequirement(PotentialArchetype *PAT,
963965
auto T = PAT->getRepresentative();
964966

965967
// Add the requirement, if we haven't done so already.
966-
if (!T->addConformance(Proto, Source, *this))
968+
if (!T->addConformance(Proto, /*updateExistingSource=*/true, Source, *this))
967969
return false;
968970

969971
RequirementSource InnerSource(RequirementSource::Protocol, Source.getLoc());
@@ -1159,7 +1161,20 @@ bool ArchetypeBuilder::addSameTypeRequirementBetweenArchetypes(
11591161
T1->ArchetypeOrConcreteType = NestedType::forConcreteType(concrete2);
11601162
T1->SameTypeSource = T2->SameTypeSource;
11611163
}
1162-
1164+
1165+
// Don't mark requirements as redundant if they come from one of our
1166+
// child archetypes. This is a targeted fix -- more general cases
1167+
// continue to break. In general, we need to detect cycles in the
1168+
// archetype graph and not propagate requirement source information
1169+
// along back edges.
1170+
bool creatingCycle = false;
1171+
auto T2Parent = T2;
1172+
while(T2Parent != nullptr) {
1173+
if (T2Parent->getRepresentative() == T1)
1174+
creatingCycle = true;
1175+
T2Parent = T2Parent->getParent();
1176+
}
1177+
11631178
// Make T1 the representative of T2, merging the equivalence classes.
11641179
T2->Representative = T1;
11651180
T2->SameTypeSource = Source;
@@ -1175,7 +1190,8 @@ bool ArchetypeBuilder::addSameTypeRequirementBetweenArchetypes(
11751190

11761191
// Add all of the protocol conformance requirements of T2 to T1.
11771192
for (auto conforms : T2->ConformsTo) {
1178-
T1->addConformance(conforms.first, conforms.second, *this);
1193+
T1->addConformance(conforms.first, /*updateExistingSource=*/!creatingCycle,
1194+
conforms.second, *this);
11791195
}
11801196

11811197
// Recursively merge the associated types of T2 into T1.
@@ -1331,7 +1347,8 @@ bool ArchetypeBuilder::addAbstractTypeParamRequirements(
13311347
assocType->setInvalid();
13321348

13331349
// FIXME: Drop this protocol.
1334-
pa->addConformance(proto, RequirementSource(kind, loc), *this);
1350+
pa->addConformance(proto, /*updateExistingSource=*/true,
1351+
RequirementSource(kind, loc), *this);
13351352
};
13361353

13371354
// If the abstract type parameter already has an archetype assigned,

test/IRGen/same_type_constraints.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,19 @@ public class GenericKlazz<T: DataType, R: E> : E where R.Data == T
4242
d = Dict()
4343
}
4444
}
45+
46+
// This used to hit an infinite loop - <rdar://problem/27018457>
47+
public protocol CodingType {
48+
associatedtype ValueType
49+
}
50+
51+
public protocol ValueCoding {
52+
associatedtype Coder: CodingType
53+
}
54+
55+
func foo<Self>(s: Self)
56+
where Self : CodingType,
57+
Self.ValueType: ValueCoding,
58+
Self.ValueType.Coder == Self {
59+
print(Self.ValueType.self)
60+
}

test/decl/protocol/protocols.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ struct WrongIsEqual : IsEqualComparable { // expected-error{{type 'WrongIsEqual'
235235
//===----------------------------------------------------------------------===//
236236

237237
func existentialSequence(_ e: Sequence) { // expected-error{{has Self or associated type requirements}}
238-
var x = e.makeIterator() // expected-error{{'Sequence' is not convertible to '<<error type>>'}}
238+
var x = e.makeIterator() // expected-error{{type 'Sequence' does not conform to protocol 'IteratorProtocol'}}
239239
x.next()
240240
x.nonexistent()
241241
}

0 commit comments

Comments
 (0)