ponyc icon indicating copy to clipboard operation
ponyc copied to clipboard

Type inference for methods in union types is too simplistic

Open sgebbie opened this issue 2 years ago • 3 comments

While working with union types while is seems reasonable to call u.definition(), on a variable u, declared with a union type, as in the code snippet below. However, this produces the following error:

ponymain.pony:26:16: a member of the union type has an incompatible method signature
                let def_u = u.definition()

Code with method call on a union type

class val B
    fun definition(): None => None

class val A
    fun definition(): String => ""
    // fun definition(): T => "" // however the type inference works if A or B use the union in the definition

type U is ( B | A)

type T is (String val | None)

interface val I
    fun definition(): (String val | None)

actor Main
    new create(env: Env) =>

        let a: A = A
        let b: B = B

        let u: U = b
        let i: I = u
        let def_i = i.definition() // works when first referencing u using the the interface, i
        let def_b = b.definition() // expected to work
        let def_a = a.definition() // expected to work
        let def_u = u.definition() // <--- type inference error on this line i.e. when calling 'definition' directly on the union type

Problem statement

Currently it seems that the type inference requires methods for the same dispatch target to share the same return type, or to have a return type that is a subtype of the return type of one of the methods signatures in the union.

However, in terms of the intent of structural typing, it would seem reasonable to expect that the type signature for the return type of a "common" method, in a union, would first be computed as the union of the potential return types.

@jemc commented in the Zulip chat, and provided a more details description of the potential type handling rules that might be applied, see below.

Zulip chat notes

From: Joe Eli McIlvain (via Zulip chat)

I've run into this same issue before - ponyc doesn't deal well with type unions as method receivers, even though it would be sound to do so. [cut] it basically boils down to what you might expect:

  • a method of that name must exist on each type in the union
  • each argument must be a subtype of the corresponding parameter types from the various union methods
  • the return type is the union of the return types of the various union methods

sgebbie avatar Aug 13 '21 22:08 sgebbie

@jemc , @jasoncarr0 , @SeanTAllen - following on from the Zulip chat regarding type inference for method signatures in a union, please see the issue that has been drafted.

sgebbie avatar Aug 13 '21 22:08 sgebbie

A quick superficial look at the error message and the code leads to here:

sgebbie avatar Aug 17 '21 07:08 sgebbie

We discussed this a bit. Refcaps will also need to match all, One edge case / compilcation could be when one of the methods is tag, and another is box/ref, so that only the latter needs automatic receiver recovery. It's probably not likely but in that case only the ref method arguments need to be sendable.

jasoncarr0 avatar Aug 31 '21 18:08 jasoncarr0