gleam icon indicating copy to clipboard operation
gleam copied to clipboard

Invalid erlang generated when referencing a function of the same module with @external

Open joshi-monster opened this issue 5 months ago • 1 comments

My original goal was to "trick" the compiler to generate a fully-qualified call to local function, without requiring moving the function to another module. Fully-qualified function references are treated differently when replacing modules, and will always call current code, whereas local or anonymous funs will continue to reference the old code.

app.gleam

import gleam/io

pub fn hello() {
    io.println("Hello!")
}

@external(erlang, "app", "hello")
fn hello2() -> Nil

pub fn main() {
    io.debug(hello2)
}

generates this Erlang code:

-module(app).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).

-export([hello/0, main/0]).

-spec hello() -> nil.
hello() ->
    gleam@io:println(<<"Hello!"/utf8>>).

-spec main() -> nil.
main() ->
    gleam@io:debug(fun hello2/0).

Notice how in main, the hello2 external is not replaced by hello, causing an Erlang compilation error:

Compiling app
/home/mel/Projects/private/gleam/app/build/dev/erlang/app/_gleam_artefacts/app.erl:16:20: function hello2/0 undefined
%   16|     gleam@io:debug(fun hello2/0).
%     |                    ^

error: Shell command failure

This also seems to cause some kind of cache invalidation problem; replacing io.debug(hello2) with io.debug(hello2()) specifically does not re-generate the module; Instead the compiler assumes the last compilation succeeded and tries to run the module without the beam file existing:

   Compiled in 0.04s
    Running app.main
exception error: undefined function app:main/0

Removing build and calling the function (io.debug(hello2())) works, but the call that is generated is not fully qualified, so I had to add the other module anyways :sweat_smile: ~:purple_heart:

joshi-monster avatar Sep 08 '24 11:09 joshi-monster