Open ysbaddaden opened 5 months ago
I have some vague recollection that this had been mentioned before. But as
is not great on searchability 🙈
We talked about it in on slack a while back :speak_no_evil:
The pseudo methods are implemented as the Cast
and NilableCast
AST nodes in src/compiler/crystal/semantic/bindings.cr
. The implementation is identical (calls for a refactor) except that Cast explicitly checks for pointers while NilableCast doesn't (eventually the methods differ to handle a nil type in NilableCast):
Nope, that doesn't fix anything: the type is correctly inferred as Pointer(Void) | Nil
. The issue is more likely to be in the codegen where Cast takes cares of pointers while NilableCast doesn't.
Hum, this is worst than just references: .as?
fails with pointers, unless the pointer type is identical:
foo = Pointer(Int32).new(0xdeadbeef)
p foo.as(Int32*) # => Pointer(Void)@xdeadbeef
p foo.as?(Int32*) # => Pointer(Void)@xdeadbeef
foo = Pointer(Int32).new(0xdeadbeef)
p foo.as(Void*) # => Pointer(Void)@xdeadbeef
p foo.as?(Void*) # => nil
I'm pretty sure the codegen linked above is the culprit: it doesn't account for pointers at all. That being said, I'm not sure how to properly fix it...
The behaviour is identical in the interpreter. So if it's a codegen issue, the interpreter has the same implementation.
Confirmed: the filtered_type
variable is nil
because we can't filter Pointer(Void) from Pointer(Int32) for example, which the compiler interprets as "impossible cast" and always codegens a nil
.
The interpreter behaves just like the LLVM codegen: it immediately returns nil
when filtered_type
doesn't match.
I noticed
Reference#as?(Void*)
doesn't behave likeReference#as(Void*)
. For example:I believe this is an error in
.as?
. The only difference between these two pseudo methods should be in error handling (raise vs. nil), and casting a reference to a pointer is an acceptable behavior (a reference is a pointer).