Open nisanharamati opened 7 months ago
The match itself isn't the issue. That is fine. In the original example.
This works:
actor Main
let env: Env
new create(env': Env) =>
env = env'
let x = ("hello", USize(123))
match_x(x) // segfault
fun match_x(x: Any val) =>
env.out.print("match_x(x: Any val): ")
// segfault
match x
| let s: Stringable => env.out.print("\t" + s.string())
| (let a: Stringable, let b: USize) =>
env.out.print("\t(" + a.string() + ", " + b.string() +")")
else
env.out.print("\tno-match")
end
So does this:
actor Main
let env: Env
new create(env': Env) =>
env = env'
let x = ("hello", USize(123))
match_x(x) // segfault
fun match_x(x: Any val) =>
env.out.print("match_x(x: Any val): ")
// segfault
match x
| let s: Stringable => env.out.print("\t" + s.string())
| (let a: Stringable, let b: (USize | U64)) =>
env.out.print("hello")
else
env.out.print("\tno-match")
end
The issue in the first match is with the combination of a match through the union AND the message send. What exactly is open to question.
The second example is the same issue. It is matching on Number
that is type Number is (Int | Float)
.
So there's something with the tuple being matched against a union and then PROBABLY the usage of the matched in a message send. More investigation is needed to know if it is message send is in fact fully required. Simple testing says yes.
Here's a more minimal example:
actor Main
let _env: Env
new create(env: Env) =>
_env = env
match_x(("hello", USize(123)))
fun match_x(x: Any val) =>
match x
| (let a: Stringable, let b: (USize | U64)) =>
_env.out.print("\t(" + b.string() +")")
end
Note the following where the match is against the known tuple types is fine:
actor Main
let _env: Env
new create(env: Env) =>
_env = env
match_x(("hello", USize(123)))
fun match_x(x: (Stringable, (USize | U64))) =>
match x
| (let a: Stringable, let b: (USize | U64)) =>
_env.out.print("\t(" + b.string() +")")
end
Here's an even more minimal example:
actor Main
new create(env: Env) =>
let x: Any val = ("hello", (USize(123)))
match x
| (let a: Stringable, let b: (USize | U64)) =>
env.out.print(b.string())
end
The message send isn't required. Here's a still more minimal example:
actor Main
new create(e: Env) =>
let x: Any val = ("a", (U8(1)))
match x
| (let a: String, let b: (U8 | U16)) =>
b.string()
end
At this point what seems to be important is the Any val
when matching and the union type that we for b
and then, calling string
on the matched value.
Whoever picks this up can work more from there.
Discussed in the sync call.
Listen to the sync call recording for a more detailed explanation, but what's going on here is:
x
is a "boxed" tuple (stored via a layer of indirection as an object pointer with a type descriptor)x
is not "boxed" - it is a raw U8 value of 1b
as if it were a pointer, but it isn't - it's just a U8 value of 1 with some junk data in the other bitsTo fix this, we would need the compiled code inside the match to recognize the subtle distinction between the two different tuple shapes, and generate different code for different tuple shape cases, with the one case we need here being a case where we need to generated a boxed pointer for b
, before we can try to treat it as a pointer.
This is non-trivial, but should be possible.
Repro
Minimal version
Some more cases
OS: MacOS, M3 Pony version
Backtrace (of the minimal version)