1

Here's a snippet of swift test code, why this does not working? How to make it working?

import UIKit

class SwiftTestController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        test()
    }
    
    func test() {
        let resourceViews: Array<any ResourceView> = []
       // type-checking error in this line.
        let resources = resourceViews.map { $0.resource }
    }
}


protocol ResourceComponent {
    
}

// fine
protocol ResourceView: ResourceComponent {
    associatedtype R: Resource
    var resource: R { get set }
}

// does not working
//protocol ResourceView: UIView, ResourceComponent {
//    associatedtype R: Resource
//    var resource: R { get set }
//}

// does not working
//protocol ResourceView: UIView {
//    associatedtype R: Resource
//    var resource: R { get set }
//}

protocol Resource {}

Below is a image for detail error message! enter image description here

let ResourceView does not inherit(constraint to) UIView works fine.

I've submitted a issue.

K.F
  • 97
  • 4
  • in your func test() you declared let resourceViews as an Array of "any ResourceView". Have you tried to use an array of ResourceView? Would be my first idea to declare a correct type for your array. Change let resourceViews : Array = [] to let resourceViews: Array = [] – Iskandir Feb 27 '23 at 07:48
  • It looks like the Swift compiler crashed instead of giving you a proper, readable error message. – Sweeper Feb 27 '23 at 07:51
  • @Iskandir No, not working. Use of protocol `ResourceView` as a type must be written `any ResourceView`. – K.F Feb 27 '23 at 08:01
  • @Sweeper It crashed because it cannot type-checking. – K.F Feb 27 '23 at 08:05
  • Well, crashing is not the expected behaviour for "cannot type check", is it? It should at least output a diagnostic. In any case, this is definitely a compiler bug. – Sweeper Feb 27 '23 at 08:14
  • `Why is it crashing?` - because it's a compiler bug. Report it at https://github.com/apple/swift/issues/ or via Feedback Assistant – Gereon Feb 27 '23 at 08:16
  • @Gereon Have you run this code ever? And does it work fine for you or not? – K.F Feb 27 '23 at 08:22
  • No, it doesn't compile in Xcode 14.2 – Gereon Feb 27 '23 at 08:25
  • @K.F Stop saying "work" and "doesn't work". It's meaningless. The issue here is whether the code compiles or fails to compile and, if the latter, whether the compiler emits a coherent error message or crashes. – matt Feb 27 '23 at 11:06
  • @matt I say working for the compiler side, just as you mean, compiles or fails to compile. – K.F Feb 27 '23 at 11:32
  • So say that, because "work" doesn't mean that. – matt Feb 27 '23 at 11:56

1 Answers1

2

This is apparently issue #63751

According to the bug report, this is an unimplemented part of SE-309 Unlock existentials for all protocols, and happens whenever you open an existential that is also an l-value, like a class-bound protocol type.

Anyway, I've found that declaring resourceViews to be an intersection type between ResourceView and UIView to work:

protocol ResourceView:  ResourceComponent { // remove the class bound
    ...
}

let resourceViews: Array<any ResourceView & UIView> = []

The type constraints on the map line are practically the same here (I think), but somehow Swift is happy with this.

The difference here is of course that non-UIViews can now conform to ResourceView, but at the end of the day they can't go in the resourceViews array, so I think this could be a viable solution for now. There are a few more minor differences that I don't think matters too much in your case, which is answered here.

Maybe a bit of renaming would also help :)

protocol ResourceComponent {
    
}

protocol ResourceBox:  ResourceComponent {
    associatedtype R: Resource
    var resource: R { get set }
}

protocol Resource {
    
}

typealias ResourceView = any ResourceBox & UIView

let resourceViews: Array<ResourceView> = []
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Thx! But this code does not compile: ```func foo(_ resourceView: ResourceView) { resourceView.resource = nil }``` Cannot assign to property: `resourceView` is a `let` constant – K.F Feb 27 '23 at 09:31
  • Yes, in my case. – K.F Feb 27 '23 at 09:42
  • No! Constrained the `ResourceBox` to `class` or `NSObject` will cause that `type checking` issue. – K.F Feb 27 '23 at 10:50
  • Yah, that works! And the `foo` not need to be generic. – K.F Feb 27 '23 at 10:54
  • I didn't know about implicitly opening existentials so I'm still benefitting from your answer in a different way. – matt Feb 27 '23 at 11:04