How does this rule work in regard of ambiguity?


(Adrian Zubarev) #1

The simple generic NonEmpty struct is really a powerful type and I wish for its inclusion in the stdlib in the future, but that's not the topic of this thread. Collection has a requirement var first: Self.Element? { get } which must be satisfied by the conforming type. This requirement has however a default implementation, which allows NonEmpty to be implemented in a convenient way by providing a non-optional first property.

At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.

protocol P {
  var e: Int? { get }
}

extension P {
  var e: Int? {
    return 42
  }
}

extension P {
  func foo() {
    print(e as Any)
  }
}

struct T : P {}

extension T {
  var e: Int {
    return 0
  }
}

let t = T()
// prints "0 Optional(42) Optional(42)"
print(
  t.e,
  (t as P).e as Any,
  t.e as Optional as Any
)
t.foo() // prints "Optional(42)"
  • Why does the compiler allow this shadowing?
  • This might have unexpected behavior when static dispatch is applied.
  • Why is t.e not ambiguous for the compiler?
  • Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)
protocol Q {
  init?()
}

struct S : Q {
  init() {} // Okay
}

// Return type must be an optional
func create<T>(_: T.Type) -> T? where T : Q {
  return T.self.init()
}

(Jordan Rose) #2

It's overloading rather than shadowing, since the two entities have different types. You can even select between them with as (well, usually).

It's also potentially important for retroactive conformances, and for library evolution, but if we really cared about that we'd allow you to define overloads of properties all the time. We might need to look into that at some point.

That's true of any overloading or shadowing, yeah.

Members of concrete types are considered "better overloads" than members of protocols.

Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns.