Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function struct fields aren't callable. #1379

Open
fahrradflucht opened this issue Feb 18, 2023 · 1 comment
Open

Function struct fields aren't callable. #1379

fahrradflucht opened this issue Feb 18, 2023 · 1 comment

Comments

@fahrradflucht
Copy link
Contributor

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() 
────┴─
@alimpfard
Copy link
Member

alimpfard commented Feb 18, 2023

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)

fahrradflucht added a commit to fahrradflucht/jakt that referenced this issue Feb 19, 2023
Allow creating local references to struct fields in methods.

It turned out the way we were checking scope lifetimes was a bit
backwards. We use this method every to guard against the inverse (the
child scope outliving the parent) but the method wasn't implemented that
way. To be honest I still don't understand a 100% why some tests were
passing in the old implementation.

We I also had to invert the exit condition to account for the fact that
comptime scope parent chains are disjunct from runtime scope parent
chains, but at least we now know that the json parser sample is testing
for that case 😉.

Fixing this also surfaced that the `stores_arguments` attribute test
wasn't actually testing the feature but was passing due to the bug. I
had to adjust it to actually test the failure case for it to continue to
pass.

Progress on SerenityOS#1379.
fahrradflucht added a commit to fahrradflucht/jakt that referenced this issue Feb 19, 2023
Allow creating local references to struct fields in methods.

It turned out the way we were checking scope lifetimes was a bit
backwards. We use this method every to guard against the inverse (the
child scope outliving the parent) but the method wasn't implemented that
way. To be honest I still don't understand a 100% why some tests were
passing in the old implementation.

We I also had to invert the exit condition to account for the fact that
comptime scope parent chains are disjunct from runtime scope parent
chains, but at least we now know that the json parser sample is testing
for that case 😉.

Fixing this also surfaced that the `stores_arguments` attribute test
wasn't actually testing the feature but was passing due to the bug. I
had to adjust it to actually test the failure case for it to continue to
pass.

Progress on SerenityOS#1379.
fahrradflucht added a commit to fahrradflucht/jakt that referenced this issue Feb 19, 2023
Allow creating local references to struct fields in methods.

It turned out the way we were checking scope lifetimes was a bit
backwards. We use this method every to guard against the inverse (the
child scope outliving the parent) but the method wasn't implemented that
way. To be honest I still don't understand a 100% why some tests were
passing in the old implementation.

We I also had to invert the exit condition to account for the fact that
comptime scope parent chains are disjunct from runtime scope parent
chains, but at least we now know that the json parser sample is testing
for that case 😉.

Fixing this also surfaced that the `stores_arguments` attribute test
wasn't actually testing the feature but was passing due to the bug. I
had to adjust it to actually test the failure case for it to continue to
pass.

Progress on SerenityOS#1379.
alimpfard pushed a commit that referenced this issue Feb 20, 2023
Allow creating local references to struct fields in methods.

It turned out the way we were checking scope lifetimes was a bit
backwards. We use this method every to guard against the inverse (the
child scope outliving the parent) but the method wasn't implemented that
way. To be honest I still don't understand a 100% why some tests were
passing in the old implementation.

We I also had to invert the exit condition to account for the fact that
comptime scope parent chains are disjunct from runtime scope parent
chains, but at least we now know that the json parser sample is testing
for that case 😉.

Fixing this also surfaced that the `stores_arguments` attribute test
wasn't actually testing the feature but was passing due to the bug. I
had to adjust it to actually test the failure case for it to continue to
pass.

Progress on #1379.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants