Can't call protocol requirements on a conforming type

For a public protocol P which has requirement of method p(), if we know type C conforms to P.
Then we should be able to call p() directly on type C. But in some case p() can't be called directly with type C. Instead we need to cast it to a any P box and then call p().

Below is a example from SwiftUI:

import SwiftUI // public protocol View with body requirement and EmptyView is a type conforms to View

let v1: any View = EmptyView()
v1.body // ✅

let v2: EmptyView = EmptyView()
v2.body // ❌

I was wondering if this is a compiler bug or if we can do the same thing as the API design here.

1 Like

Update

The following method is not correct. And we can still call foo() directly on C.

Original Reply

Got the trick here.

Using another internal protocol with public extension for the requirement will achieve the effect above.

public protocol P {
  func foo()
}

internal protocol InternalP: P {}

extension InternalP {
  public func foo() { ... }
}

public struct C: InternalP, P {}

Then the code will compile but we can't call foo() directly on C unless with any P cast.

Is that true?
It seems to me that SwiftUI’s View protocol declares a property named body.
However your last line of code is attempting to access a static property of the type EmptyView. There is no static property named body in the View protocol.

Unless I’m misunderstanding your question.

I actually think this would fail to compile with any "primitive" view component:

let _ = EmptyView().body // ❌
let _ = Text("Hello, world!").body // ❌

What I'm not yet sure about is how to declare my own view component as primitive:

struct NeverView: View {
  init() { }
  var body: Never
}

let _ = NeverView().body // ✅

Here might be some clues:

Is that true?

No. I have updated the status. We can still call it normally in my attempt to reproduce it.

However your last line of code is attempting to access a static property of the type EmptyView .

No. It is not a static property since the callee is an instance of EmptyView().

What I'm not yet sure about is how to declare my own view component as primitive:

This is plain. See PrimitiveView implementation or my example above.

My original issue is not tightly aligned with SwiftUI. And let's focus back on the compiler stuff since SwiftUI implementation is out of the forums's scope.

for your repro that does not depend on SwiftUI – does it require the call to be attempted from another module? i.e. how exactly did you reproduce the issue without additional dependencies?

Your code produces a warning on main, it’s exploiting a compiler bug and should not have been allowed. The extension member is not actually public so it cannot implement the protocol requirement.

3 Likes

Sadly we can't reproduce it without SwiftUI dependency now.

You can reproduce this behavior using @_spi. I explained how here: How does Swift prevent SwiftUI `body: Never` properties from being called? - #12 by mayoff

1 Like