SerenityOS / jakt

The Jakt Programming Language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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)