Xcode 10 changed behaviour of class and protocol existential

Hello.
I tried to build project in Xcode 10 (Swift 4, new build system). The following code can't be build (error: ambiguous use of 'alpha'):

protocol B: AnyObject {
    var alpha: CGFloat { get set }
    func doSomething()
}
typealias ViewB = UIView & B
func perform(_ viewB: ViewB) {
    viewB.alpha = 10.0
}

Xcode 9.4 (Swift 4.1, old build system) compiles it without error.
Is it a bug in beta or some intended change?

Since UIView declares a property named 'alpha', do you also need it in the protocol itself? If you remove it from the protocol, the code will compile.

@hamishknight We've had a few reports of this -- I think it was caused by some of your fixes in this area. Do you think it would be possible to get back the old behavior for -swift-version 4?

Thanks for response. Yeah, that's right, it will fix this particular compile error.

However, I think it limits flexibility and power of this feature (class and protocol existential). It is weird that existential of protocol and type that has one of requirement in the interface becomes unusable. More over it is revealed only in use place.

Have you tried only declaring a getter requirement on alpha property?

protocol B: AnyObject {
    var alpha: CGFloat { get }

    func doSomething()
}

Doing so should fix the compiler error you are seeing… I am not sure this will produce the end result you are hoping for.

@Slava_Pestov is correct, this was indeed caused by my patch #15412.

It's worth noting that this ambiguity behaviour was already the case for methods and properties defined in an extensions of generic types, for example these are both illegal in Swift 4.1:

protocol P {
  func foo() -> Int
}

class C {
  func foo() -> Int { return 0 }
}

func foo(_ x: P & C) {
  _ = x.foo() // error: Ambiguous use of 'foo()'
}
protocol P {
  var foo: Int { get }
}

class C<T> {}
extension C {
  var foo: Int { return 0 }
}

func foo<T>(_ x: P & C<T>) {
  _ = x.foo // error: Ambiguous use of 'foo'
}

And IMO (though it's probably worth discussing on Swift evolution) we should keep this ambiguous in the general case if we eventually want some way for types to customise how a protocol's requirement is mapped to their implementation of that requirement (the possibility of which is mentioned by @Joe_Groff in SR-7126). If we had such a feature, then it would be possible for the implementation to have a different name to the requirement – meaning that you could potentially get different results from dispatching to the protocol's requirement vs. the class member.

It would Be Nice though if we could preserve the old behaviour under Swift 4 compat mode – I can look into that.

I'm not sure the mere possibility of a conflicting conformance really justifies rejecting this.

Don't we have a general rule preferring implementations on concrete types over protocol members?

2 Likes

Thinking it over some more, I've changed my mind and agree – it does make sense to follow the "prefer concrete type members over protocol members" rule here, especially as users can always choose the protocol member if they want by coercing to the protocol type. Not quite sure why the rule isn't being applied in this case though.

1 Like

Can we make the rule apply to protocol vs class methods too? IIRC in Swift 4, it was diagnosed as ambiguous for methods but not for properties, which is inconsistent.

I also don't understand why "generic extensions" warrant another set of rules here. Ranking and shadowing rules should not care if they're on a concrete type with or without generic parameters (I do think it's OK to treat protocols and concrete types differently though).

Yes, I definitely agree we should apply this methods too.

I agree we shouldn't have a seperate set of rules for "generic extensions". The difference of rules for properties in extensions of generic types was a quirk of Swift 4.1's implementation of overload types for variables – #15412 cleaned this up so we should now have consistent rules regardless of whether the property is defined in a generic extension or not.

Though unfortunately in this case, the consistency went in the direction of making such cases ambiguous. I think we should consistently make these cases consistently unambiguous – following the rule of favouring a member on a concrete type over a protocol member.

1 Like

I think this is a reasonable plan.

I have an idea of how to tackle this – do you mind if I assign myself to SR-7425?

1 Like