crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.42k stars 1.62k forks source link

Can't cast union of pointers to union member type #9281

Open jhass opened 4 years ago

jhass commented 4 years ago

Feels like this should have come up before but I couldn't find anything.

struct A
end

struct B
end

a = A.new
b = B.new

obj = rand == 0 ? a : b
obj.as(B)

ary = rand == 0 ? [a] : [b]
ary.as(Array(B))

ptr = rand == 0 ? pointerof(a) : pointerof(b)

ptr.as(B*)
Error: can't cast (Pointer(A) | Pointer(B)) to Pointer(B)

What am I missing? What makes the last case different than the other two?

asterite commented 4 years ago

You can cast any reference type to pointer. But you can't cast a struct to a pointer. Im pretty sure we never though about casting a union of pointers to a pointer and the compiler is complaining because that's not a reference type.

It should be easy to fix.

jhass commented 4 years ago

I'm not sure I fully follow. A and B are both structs, obj is a union of both of them, yet the cast works?

asterite commented 4 years ago

This is the relevant code:

https://github.com/crystal-lang/crystal/blob/35c1f41f05144d9645c7bba83cd1b1deaea68fb0/src/compiler/crystal/semantic/cleanup_transformer.cr#L728-L734

In the language you can do:

class A
end

A.new.as(Void*)
A.new.as(Int8*)

You can basically cast any reference type to a pointer, because a reference is represented as a pointer. This is useful for some low level things.

You can't do that with structs. The compiler will complain.

You can then cast a pointer to another pointer.

But we forgot about being able to cast a union of pointers to a pointer. That use case or scenario never came to our minds.

Is that more clear?

HertzDevil commented 1 year ago

This happens not just with a union of pointers, but also with a union of a value and a pointer, say Int32 | Pointer(Int32) (see #13000)