Function struct fields aren't callable.
fahrradflucht opened this issue · comments
Either I'm holding something wrong or function fields aren't callable at the moment.
Problem
This test will fail to compile even though I would assume it should:
/// Expect:
/// - output: "PASS\nPASS\n"
struct FunctionHaver {
cb: fn() -> String
fn call_cb(this) -> String {
return .cb()
}
}
fn main() {
let haver = FunctionHaver(cb: fn() => "PASS")
println("{}", haver.cb())
println("{}", haver.call_cb())
}
Jakt error:
Error: Could not find ‘cb’
───┬─ src/function_as_a_property.jakt:8:16
7 │ fn call_cb(this) -> String {
8 │ return .cb()
│ ────┬
│ ╰─ Could not find ‘cb’
9 │ }
───┴─
Error: Could not find ‘cb’
────┬─ src/function_as_a_property.jakt:15:19
14 │
15 │ println("{}", haver.cb())
│ ─────────┬
│ ╰─ Could not find ‘cb’
16 │ println("{}", haver.call_cb())
────┴─
Investigations
Copying the callback to a temp var
I tried assigning the function as a temp var first. This will codegen, but the resulting cpp won't compile.
/// Expect:
/// - output: "PASS\nPASS\n"
struct FunctionHaver {
cb: fn() -> String
fn call_cb(this) -> String {
let cb = this.cb
return cb()
}
}
fn main() {
let haver = FunctionHaver(cb: fn() => "PASS")
let cb = haver.cb
println("{}", cb())
println("{}", haver.call_cb())
}
C++ Error:
build/function_as_a_property.cpp:14:40: error: call to deleted constructor of 'const Function<AK::DeprecatedString ()>'
Function<DeprecatedString()> const cb = ((haver).cb);
^ ~~~~~~~~~~~~
jakt/build/include/runtime/AK/Function.h:67:25: note: 'Function' has been explicitly marked deleted here
AK_MAKE_NONCOPYABLE(Function);
^
build/function_as_a_property.cpp:38:40: error: call to deleted constructor of 'const Function<AK::DeprecatedString ()>'
Function<DeprecatedString()> const cb = ((*this).cb);
^ ~~~~~~~~~~~~
jakt/build/include/runtime/AK/Function.h:67:25: note: 'Function' has been explicitly marked deleted here
AK_MAKE_NONCOPYABLE(Function);
Makes sense to me that the function isn't copyable, but probably this should fail earlier? 🤔
Using a pointer indirection
Avoiding copying by using a pointer will work for outside property access.
/// Expect:
/// - output: "PASS\n"
struct FunctionHaver {
cb: fn() -> String
}
fn main() {
let haver = FunctionHaver(cb: fn() => "PASS")
let cb = &haver.cb
println("{}", cb())
}
However, it won't work for the method access case because the compiler thinks the reference is outlived (is that right? 🤔):
/// Expect:
/// - output: "PASS\nPASS\n"
struct FunctionHaver {
cb: fn() -> String
fn call_cb(this) -> String {
let cb = &.cb
return cb()
}
}
fn main() {
let haver = FunctionHaver(cb: fn() => "PASS")
let cb = &haver.cb
println("{}", cb())
println("{}", haver.call_cb())
}
Jakt error:
Error: Cannot assign a reference to a variable that outlives the reference
────┬─ src/function_as_a_property.jakt:8:18
7 │ fn call_cb(this) -> String {
8 │ let cb = &.cb
│╰─ Cannot assign a reference to a variable that outlives the reference
10 │ return cb()
────┴─
Yep, currently only variables with a function type are callable (plus actual declared functions).
That's both a limitation of the grammar, and the typechecker atm.
Regarding the reference one, that's most likely a bug in the lifetime analysis (unless there's something happening that I'm not seeing)