Protocol requirement `where` clauses constraining associated types


(Brent Royal-Gordon) #1

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.

* https://oleb.net/blog/2017/02/sorted-array/

···

--
Brent Royal-Gordon
Architechies


(Slava Pestov) #2

Hah, Doug and I were just discussing this.

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.

* https://oleb.net/blog/2017/02/sorted-array/

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #3

Hah, Doug and I were just discussing this.

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.

* https://oleb.net/blog/2017/02/sorted-array/

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #4

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:

Hah, Doug and I were just discussing this.

--
-Dave


(Slava Pestov) #5

Hah, Doug and I were just discussing this.

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.

* https://oleb.net/blog/2017/02/sorted-array/

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #6

Hah, Doug and I were just discussing this.

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.

* https://oleb.net/blog/2017/02/sorted-array/

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution