<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Another solution is to change the Collection protocol as follows,<div class=""><br class=""></div><div class="">protocol Collection {</div><div class=""> associatedtype ConformingType = Self</div><div class=""> associatedtype Iterator = IndexingIterator<ConformingType></div><div class=""><br class=""></div><div class=""> …</div><div class="">}</div><div class=""><br class=""></div><div class="">extension Collection where Iterator == IndexingIterator<ConformingType> {</div><div class=""> func makeIterator() -> IndexingIterator<ConformingType> { … }</div><div class="">}</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">Slava</div><div class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Oct 6, 2017, at 12:25 AM, Slava Pestov via swift-dev <<a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hi all,<div class=""><br class=""></div><div class="">Consider this code,</div><div class=""><br class=""></div><div class="">class Base : Collection {<br class=""> var startIndex: Int { return 0 }<br class=""><br class=""> var endIndex: Int { return 10 }<br class=""><br class=""> func index(after i: Int) -> Int { return i + 1 }<br class=""><br class=""> subscript(index: Int) -> Int { return index }<br class="">}<br class=""><div class=""><br class=""></div><div class="">We infer the associated type ‘Iterator’ as ‘IndexingIterator<Base>’. I can use an instance of Base as a sequence just fine:</div><div class=""><br class=""></div>for x in Base() {} // OK</div><br class="">Now if I subclass Base, the associated type is still ‘IndexingIterator<Base>’:<div class=""><br class="">class Derived : Base {}<br class=""><br class="">However the implementation of makeIterator is defined in a constrained extension by the standard library,</div><div class=""><br class=""></div><div class="">extension Collection where Self.Iterator == IndexingIterator<Self> {</div><div class=""> func makeIterator() -> IndexingIterator<Self> { … }</div><div class="">}<br class=""><br class=""></div><div class="">So I cannot call it on a subclass:</div><div class=""><br class=""></div><div class="">for x in Derived() {} // fails</div><div class=""><br class=""></div><div class="">The error is bizarre, "'IndexingIterator<Base>' is not convertible to 'IndexingIterator<Derived>’” — I’m not doing a conversion here.</div><div class=""><br class=""></div><div class="">If you try to call makeIterator() directly, you get an ambiguity error instead:</div><div class=""><br class=""></div><div class="">col.swift:17:5: error: ambiguous reference to member 'makeIterator()'<br class="">_ = Derived().makeIterator()<br class=""> ^~~~~~~~~<br class="">Swift.Collection:6:17: note: found this candidate<br class=""> public func makeIterator() -> IndexingIterator<Self><br class=""> ^<br class="">Swift.Sequence:5:17: note: found this candidate<br class=""> public func makeIterator() -> Self<br class=""> ^<br class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">With my PR here the conformance itself no longer type checks: <a href="https://github.com/apple/swift/pull/12174" class="">https://github.com/apple/swift/pull/12174</a></div><div class=""><br class=""></div><div class="">col.swift:1:7: error: type 'Base' does not conform to protocol 'Collection'<br class="">class Base : Collection {<br class=""> ^<br class="">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>]<br class=""> public func makeIterator() -> Self<br class=""> ^<br class="">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>]<br class=""> public func makeIterator() -> IndexingIterator<Self></div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">This was reported externally as <a href="https://bugs.swift.org/browse/SR-1863" class="">https://bugs.swift.org/browse/SR-1863</a>. 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.</div><div class=""><br class=""></div><div class="">What does everyone think of this?</div><div class=""><br class=""></div><div class="">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?</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">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 <a href="https://bugs.swift.org/browse/SR-617" class="">https://bugs.swift.org/browse/SR-617</a>?</div><div class=""><br class=""></div><div class="">Slava</div><div class=""><br class=""></div></div>_______________________________________________<br class="">swift-dev mailing list<br class=""><a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-dev<br class=""></div></blockquote></div><br class=""></div></body></html>