Skip to content

Commit 8db7b08

Browse files
authored
Merge pull request #4791 from slavapestov/cycle-in-archetype-graph-3.0
Don't drop requirements from potential archetypes if they came from a parent archetype
2 parents 789eba5 + 1116f48 commit 8db7b08

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)