In an article on `Collection` today*, Ole Begemann points out that `index(of:)`, along with other `Equatable`- and `Comparable`-constrained `Collection` methods, cannot be overridden. Actually, it *can* be, but only through a private mechanism—there's a `_customIndexOfEquatableElement(_:)` method that's invisible in the generated interface. But that only proves the need for a way to make methods like these overridable.
The problem is that the `index(of:)` method should only be offered when the element is `Equatable`—otherwise it simply won't work. But there's no way to specify this rule in current Swift. In theory, we could describe such a requirement with something like this:
func index(of element: Iterator.Element) -> Index? where Iterator.Element: Equatable
But this is not permitted—you get an error indicating that `where` clauses are only allowed on generic methods. Adding a spurious generic parameter allows this code to compile, but with a deprecation warning indicating that this is deprecated. I don't know if it would actually behave correctly, however.
Is this a feature we should add? Is this the way to add it? Would it have non-additive ABI impact? (The private `index(of:)` override would certainly go away, but that's why it's private, I suppose.) I don't seem to remember seeing something like this in the generics manifesto, so I thought it was worth bringing up.
In Swift 3.1, we generalized where clauses to allow them to add requirements on outer generic parameters. However we did not remove the diagnostic prohibiting a where clause from being attached to a non-generic method. In theory this can be made to work; the only slightly tricky thing is we will get a GenericParamList with zero parameters but non-zero requirements, which would require shuffling some things around to avoid assertions.
This would be a good starter project for someone who wanted to learn more about the generics system.
As for index(of:) and the specific details of the stdlib that are involved here, I have no idea — I’m just talking about the bogus diagnostic itself.
Slava
···
On Feb 8, 2017, at 9:57 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
In an article on `Collection` today*, Ole Begemann points out that `index(of:)`, along with other `Equatable`- and `Comparable`-constrained `Collection` methods, cannot be overridden. Actually, it *can* be, but only through a private mechanism—there's a `_customIndexOfEquatableElement(_:)` method that's invisible in the generated interface. But that only proves the need for a way to make methods like these overridable.
The problem is that the `index(of:)` method should only be offered when the element is `Equatable`—otherwise it simply won't work. But there's no way to specify this rule in current Swift. In theory, we could describe such a requirement with something like this:
func index(of element: Iterator.Element) -> Index? where Iterator.Element: Equatable
But this is not permitted—you get an error indicating that `where` clauses are only allowed on generic methods. Adding a spurious generic parameter allows this code to compile, but with a deprecation warning indicating that this is deprecated. I don't know if it would actually behave correctly, however.
Is this a feature we should add? Is this the way to add it? Would it have non-additive ABI impact? (The private `index(of:)` override would certainly go away, but that's why it's private, I suppose.) I don't seem to remember seeing something like this in the generics manifesto, so I thought it was worth bringing up.
In Swift 3.1, we generalized where clauses to allow them to add requirements on outer generic parameters. However we did not remove the diagnostic prohibiting a where clause from being attached to a non-generic method. In theory this can be made to work; the only slightly tricky thing is we will get a GenericParamList with zero parameters but non-zero requirements, which would require shuffling some things around to avoid assertions.
This would be a good starter project for someone who wanted to learn more about the generics system.
As for index(of:) and the specific details of the stdlib that are involved here, I have no idea — I’m just talking about the bogus diagnostic itself.
Well, I think Brent is talking about doing this on a protocol requirement, which is more interesting because not all conforming types would satisfy the requirement...
- Doug
···
On Feb 8, 2017, at 10:21 PM, Slava Pestov <spestov@apple.com> wrote:
Slava
On Feb 8, 2017, at 9:57 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
In an article on `Collection` today*, Ole Begemann points out that `index(of:)`, along with other `Equatable`- and `Comparable`-constrained `Collection` methods, cannot be overridden. Actually, it *can* be, but only through a private mechanism—there's a `_customIndexOfEquatableElement(_:)` method that's invisible in the generated interface. But that only proves the need for a way to make methods like these overridable.
The problem is that the `index(of:)` method should only be offered when the element is `Equatable`—otherwise it simply won't work. But there's no way to specify this rule in current Swift. In theory, we could describe such a requirement with something like this:
func index(of element: Iterator.Element) -> Index? where Iterator.Element: Equatable
But this is not permitted—you get an error indicating that `where` clauses are only allowed on generic methods. Adding a spurious generic parameter allows this code to compile, but with a deprecation warning indicating that this is deprecated. I don't know if it would actually behave correctly, however.
Is this a feature we should add? Is this the way to add it? Would it have non-additive ABI impact? (The private `index(of:)` override would certainly go away, but that's why it's private, I suppose.) I don't seem to remember seeing something like this in the generics manifesto, so I thought it was worth bringing up.
Funny thing; Nate Cook and I were just discussing the same thing today.
It seems like this hack we did in the standard library points to a
missing language feature; one with possible ABI impact.
···
on Wed Feb 08 2017, Slava Pestov <swift-evolution@swift.org> wrote:
In Swift 3.1, we generalized where clauses to allow them to add requirements on outer generic parameters. However we did not remove the diagnostic prohibiting a where clause from being attached to a non-generic method. In theory this can be made to work; the only slightly tricky thing is we will get a GenericParamList with zero parameters but non-zero requirements, which would require shuffling some things around to avoid assertions.
This would be a good starter project for someone who wanted to learn more about the generics system.
As for index(of:) and the specific details of the stdlib that are involved here, I have no idea — I’m just talking about the bogus diagnostic itself.
Well, I think Brent is talking about doing this on a protocol requirement, which is more interesting because not all conforming types would satisfy the requirement…
Since there would be no way to invoke the requirement on such a type, could we leave the entry blank in the witness table or emit a fatalError() thunk or something?
Slava
···
On Feb 8, 2017, at 10:30 PM, Douglas Gregor <dgregor@apple.com> wrote:
On Feb 8, 2017, at 10:21 PM, Slava Pestov <spestov@apple.com> wrote:
- Doug
Slava
On Feb 8, 2017, at 9:57 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
In an article on `Collection` today*, Ole Begemann points out that `index(of:)`, along with other `Equatable`- and `Comparable`-constrained `Collection` methods, cannot be overridden. Actually, it *can* be, but only through a private mechanism—there's a `_customIndexOfEquatableElement(_:)` method that's invisible in the generated interface. But that only proves the need for a way to make methods like these overridable.
The problem is that the `index(of:)` method should only be offered when the element is `Equatable`—otherwise it simply won't work. But there's no way to specify this rule in current Swift. In theory, we could describe such a requirement with something like this:
func index(of element: Iterator.Element) -> Index? where Iterator.Element: Equatable
But this is not permitted—you get an error indicating that `where` clauses are only allowed on generic methods. Adding a spurious generic parameter allows this code to compile, but with a deprecation warning indicating that this is deprecated. I don't know if it would actually behave correctly, however.
Is this a feature we should add? Is this the way to add it? Would it have non-additive ABI impact? (The private `index(of:)` override would certainly go away, but that's why it's private, I suppose.) I don't seem to remember seeing something like this in the generics manifesto, so I thought it was worth bringing up.
In Swift 3.1, we generalized where clauses to allow them to add requirements on outer generic parameters. However we did not remove the diagnostic prohibiting a where clause from being attached to a non-generic method. In theory this can be made to work; the only slightly tricky thing is we will get a GenericParamList with zero parameters but non-zero requirements, which would require shuffling some things around to avoid assertions.
This would be a good starter project for someone who wanted to learn more about the generics system.
As for index(of:) and the specific details of the stdlib that are involved here, I have no idea — I’m just talking about the bogus diagnostic itself.
Well, I think Brent is talking about doing this on a protocol requirement, which is more interesting because not all conforming types would satisfy the requirement…
Since there would be no way to invoke the requirement on such a type, could we leave the entry blank in the witness table or emit a fatalError() thunk or something?
I’d previously thought so, but retroactive modeling makes this more interesting than that:
// Module A
public protocol P { }
public protocol Q {
associatedtype Assoc
func f() where Assoc: P
}
public struct X { }
public struct Y : Q {
public typealias Assoc = X
// X doesn’t conform to P, so I guess we can omit f()?
}
public func callF<T: Q>(t: T) where T.Assoc: P {
t.f() // should be allowed to call f() because we know that T.Assoc conforms to P
}
// Module B
public extension X: P { }
callF(Y()) // BOOM at runtime, because witness table Y: Q doesn’t include f!
So, I think that means we can’t just leave a stub there
// Back in module A
public struct Z : Q {
public typealias Assoc = X
public func f() where X: P { } // wait, what?
}
That last one is “fun”. The method “f()" is only available when the concrete type X conforms to P. We know it doesn’t right now—but it could in the future. It’s a bit odd to reason about such a function, because you’re evaluating how the concrete type X would behave if it did in fact conform to P! In Swift-compiler-speak, it’s a concrete type with an abstract conformance to P, which is a bit of a model breaker.
- Doug
···
On Feb 8, 2017, at 11:12 PM, Slava Pestov <spestov@apple.com> wrote:
On Feb 8, 2017, at 10:30 PM, Douglas Gregor <dgregor@apple.com> wrote:
On Feb 8, 2017, at 10:21 PM, Slava Pestov <spestov@apple.com> wrote:
Slava
- Doug
Slava
On Feb 8, 2017, at 9:57 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
In an article on `Collection` today*, Ole Begemann points out that `index(of:)`, along with other `Equatable`- and `Comparable`-constrained `Collection` methods, cannot be overridden. Actually, it *can* be, but only through a private mechanism—there's a `_customIndexOfEquatableElement(_:)` method that's invisible in the generated interface. But that only proves the need for a way to make methods like these overridable.
The problem is that the `index(of:)` method should only be offered when the element is `Equatable`—otherwise it simply won't work. But there's no way to specify this rule in current Swift. In theory, we could describe such a requirement with something like this:
func index(of element: Iterator.Element) -> Index? where Iterator.Element: Equatable
But this is not permitted—you get an error indicating that `where` clauses are only allowed on generic methods. Adding a spurious generic parameter allows this code to compile, but with a deprecation warning indicating that this is deprecated. I don't know if it would actually behave correctly, however.
Is this a feature we should add? Is this the way to add it? Would it have non-additive ABI impact? (The private `index(of:)` override would certainly go away, but that's why it's private, I suppose.) I don't seem to remember seeing something like this in the generics manifesto, so I thought it was worth bringing up.