Another solution is to change the Collection protocol as follows, protocol Collection { associatedtype ConformingType = Self associatedtype Iterator = IndexingIterator<ConformingType>
… } extension Collection where Iterator == IndexingIterator<ConformingType> { func makeIterator() -> IndexingIterator<ConformingType> { … } } I believe this will fix the source compatibility issue and also make ‘for x in Derived()’ type check. The downside is that the witness table for a Collection conformance now stores an additional associated type for the static conforming class type. However that’s exactly what you need to store somewhere to make this work for non-final classes. Slava > On Oct 6, 2017, at 12:25 AM, Slava Pestov via swift-dev <swift-dev@swift.org> > wrote: > > Hi all, > > Consider this code, > > class Base : Collection { > var startIndex: Int { return 0 } > > var endIndex: Int { return 10 } > > func index(after i: Int) -> Int { return i + 1 } > > subscript(index: Int) -> Int { return index } > } > > We infer the associated type ‘Iterator’ as ‘IndexingIterator<Base>’. I can > use an instance of Base as a sequence just fine: > > for x in Base() {} // OK > > Now if I subclass Base, the associated type is still ‘IndexingIterator<Base>’: > > class Derived : Base {} > > However the implementation of makeIterator is defined in a constrained > extension by the standard library, > > extension Collection where Self.Iterator == IndexingIterator<Self> { > func makeIterator() -> IndexingIterator<Self> { … } > } > > So I cannot call it on a subclass: > > for x in Derived() {} // fails > > The error is bizarre, "'IndexingIterator<Base>' is not convertible to > 'IndexingIterator<Derived>’” — I’m not doing a conversion here. > > If you try to call makeIterator() directly, you get an ambiguity error > instead: > > col.swift:17:5: error: ambiguous reference to member 'makeIterator()' > _ = Derived().makeIterator() > ^~~~~~~~~ > Swift.Collection:6:17: note: found this candidate > public func makeIterator() -> IndexingIterator<Self> > ^ > Swift.Sequence:5:17: note: found this candidate > public func makeIterator() -> Self > ^ > > Now I couldn’t come up with an example where the code compiles but crashes at > runtime because of a type mismatch, but it’s not outside the realm of > possibility. > > With my PR here the conformance itself no longer type checks: > https://github.com/apple/swift/pull/12174 > <https://github.com/apple/swift/pull/12174> > > col.swift:1:7: error: type 'Base' does not conform to protocol 'Collection' > class Base : Collection { > ^ > Swift.Sequence:5:17: note: candidate has non-matching type '<Self> () -> > Self' [with Element = Int, Index = Int, IndexDistance = Int, Iterator = > IndexingIterator<Base>, SubSequence = Slice<Base>, Indices = > DefaultIndices<Base>] > public func makeIterator() -> Self > ^ > Swift.Collection:6:17: note: candidate has non-matching type '<Self> () -> > IndexingIterator<Self>' [with Element = Int, Index = Int, IndexDistance = > Int, Iterator = IndexingIterator<Base>, SubSequence = Slice<Base>, Indices = > DefaultIndices<Base>] > public func makeIterator() -> IndexingIterator<Self> > > I found one example in our code base where this pattern comes up, and that’s > SyntaxCollection in tools/SwiftSyntax/SyntaxCollection.swift. It has no > subclasses so making it final works there. > > This was reported externally as https://bugs.swift.org/browse/SR-1863 > <https://bugs.swift.org/browse/SR-1863>. I’m not sure if the user expects it > to work or just to produce a reasonable diagnostic instructing them to make > the class final. > > What does everyone think of this? > > 1) Can anyone suggest a way to make it work, so that ‘for x in Derived()’ > type checks and the correct Self type (Base, not Derived) for the > substitution? > > 2) Should we just ban such ’non-covariant’ conformances? There is precedent > for this — in Swift 3, we used to allow non-final classes to conform to > protocols whose requirements had same-type constraints with the right hand > side equal to ‘Self’, and Doug closed this hole in Swift 4. My PR is > essentially a more comprehensive fix for this hole. > > 3) Should we allow the hole to remain in place, admitting non-final classes > that model Collection, at the cost of not being able to ever fix > https://bugs.swift.org/browse/SR-617 <https://bugs.swift.org/browse/SR-617>? > > Slava > > _______________________________________________ > swift-dev mailing list > swift-dev@swift.org > https://lists.swift.org/mailman/listinfo/swift-dev
_______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev