But actually assigning anything to them doesn't seem to work:
lib Foo
struct Bar
x : (-> Int32)?
end
fun foo(x : (-> Int32)?)
end
Foo.foo(-> { 1 }) # Error: argument 'x' of 'Foo#foo' must be (Proc(Int32) | Nil), not Proc(Int32)
Foo.foo(nil) # Error: argument 'x' of 'Foo#foo' must be (Proc(Int32) | Nil), not Nil
bar = Foo::Bar.new
bar.x = -> { 1 } # Error: field 'x' of struct Foo::Bar has type (Proc(Int32) | Nil), not Proc(Int32)
bar.x = nil # Error: field 'x' of struct Foo::Bar has type (Proc(Int32) | Nil), not Nil
It seems nilable Proc types are allowed in the first place because their binary representations are identical to non-nilable Procs (function pointer + closure data pointer). The workaround, however, is to simply drop the Nil types, because extern Procs are equivalent to single pointers and therefore implicitly nilable:
lib Foo
struct Bar
x : -> Int32
end
fun foo(x : -> Int32)
end
Foo.foo(-> { 1 }) # okay
Foo.foo(nil) # okay
bar = Foo::Bar.new
bar.x = -> { 1 } # okay
bar.x = nil # okay
So maybe we don't need to support nilable Procs here? Returning from a top-level fun seems to be an exception though:
# Error: expected fun to return Proc(Int32) but it returned (Proc(Int32) | Nil)
fun foo : -> Int32
nil
end
# no semantic error, but fails due to #14691
fun bar : (-> Int32)?
nil
end
It is possible to use nilable
Proc
s as parameter types of lib funs or field types of lib structs:https://github.com/crystal-lang/crystal/blob/2d71e3546f700b5ade54d10d455da69f78adb65f/src/compiler/crystal/semantic/lib.cr#L284-L285
But actually assigning anything to them doesn't seem to work:
It seems nilable
Proc
types are allowed in the first place because their binary representations are identical to non-nilableProc
s (function pointer + closure data pointer). The workaround, however, is to simply drop theNil
types, because externProc
s are equivalent to single pointers and therefore implicitly nilable:So maybe we don't need to support nilable
Proc
s here? Returning from a top-level fun seems to be an exception though: