Opaque Result Types Bug?

func test() -> some Collection { // Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
    fatalError() // Cannot convert return expression of type 'Never' to return type 'some Collection'
}

Is this a bug?

No. From the evolution proposal:

4 Likes

Thanks.

Still, I think it‘s quite ugly that the feature creates such a special case :-(

Never as bottom type should help here. Then one day you would write fatalError() as Array<Int>.

return Never doesn't work?

The point is that it does not make sense even when we had the bottom type. The type is opaque only for the user but not for the compiler and in case you want to type check two collections of the same expected type you clearly don‘t want one of them to be Never. That‘s why you likely would upcast Never to a concrete collection of your choice. This mimics (where there is no type erasure):

func foo() -> [Int] {
  fatalError()
}

// the compiler knows that this return type hides `[Int]`
// it‘s not interesting that it actually is `Never` which will 
// cause termination of the program
func bar() -> some Collection {
  return fatalError() as [Int]
} 
func foo() -> [Int] {
  fatalError()
}
// this should work if it doesn’t why not?
func bar() -> some Collection {
  return foo()
} 

As far as I know this would work yes, but the thread was opened because you cannot use fatalError yet in bar just like in foo. For that you‘d need the ability to upcast Never to a type of your choice.

Hi
Not sure if it's related or if this is the right place to ask for help, but I'm facing this issue "Function declares an opaque return type, but the return statements in its body do not have matching underlying types" with the function below:

private static func destinationView(for item: MenuItem) -> some View {
    switch item.example {
    case .helloWorld: return HelloWorldView()
    case .handlingState: return HandlingStateView()
    }
}

item.example is an enum with only two cases, so all cases are handled by the switch/case. I also tried to add a default: branch, but the error persists.

This should be its own thread.

In any case, function returning opaque type requires that the returning type matches (even if users can't see that).
So you must return HelloWorldView on all branches, or return HandlingStateView on all branches, but you can't mix them.

2 Likes

Opaque result types are a stand-in for a single fixed, statically-known return type.

some View is a stand-in, that says to the compiler "please infer the type here for me, so long as it conforms to View". The compiler has 3 options:

  1. Fill it in with HelloWorldView:

    private static func destinationView(for item: MenuItem) -> HelloWorldView {
        switch item.example {
        case .helloWorld: return HelloWorldView()
        case .handlingState: return HandlingStateView() // ❌ error: cannot convert return expression of type 'HandlingStateView' to return type 'HelloWorldView'
        }
    }
    
  2. Fill it in with HandlingStateView:

    private static func destinationView(for item: MenuItem) -> HandlingStateView {
        switch item.example {
        case .helloWorld: return HelloWorldView() // ❌ error: cannot convert return expression of type 'HelloWorldView' to return type 'HandlingStateView'
        case .handlingState: return HandlingStateView() 
        }
    }
    
  3. Error out, which is what it did.