HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit
https://haxe.org
6.16k stars 656 forks source link

Table.create is unable to accept an array as first argument #10874

Open danielo515 opened 1 year ago

danielo515 commented 1 year ago

Hello.

I was trying to start using records rather than positional arguments for the constructor of a class, and I noticed something weird. If I use the record fields to create a table from what it is typed to be an array (it has nothing to do with records, happens with everything). When I try to pass Table.create an array that is not inlined (eg, from arguments) as first argument I get this error:

src/vim/Vim.hx:37: characters 28-34 : __lua_table__ only accepts array or anonymous object arguments

This is probably better explain with a bit of code. So this is the thing that makes the compilation fail:

typedef JArgs = {command:String, args:Array<String>};

abstract JobOpts(Table<String, Dynamic>) {
    public inline function new(x:JArgs) {
        this = Table.create(null, {
            command: x.command,
            arguments: Table.create(x.args)
        });
    }
}

As you can see, Table.create(x.args) is getting a proper (at least in terms of typing) array, however the compiler complains. In order to this to work I have to use Table.fromArray, which has the same signature as Table.create first arg, but that works (with some extra generated code to generate the table)

Why is this? I can inline it and have no problem:

abstract JobOpts(Table<String, Dynamic>) {
    public inline function new(command:String, args:Array<String>) {
        this = Table.create(null, {
            command: command,
            arguments: Table.create(["a"]),
        });
    }
}

That generates the needed code. So why can't I take those as arguments?

sebthom commented 1 year ago

@tobil4sk I am facing the same issue, do you have any suggestions?

tobil4sk commented 1 year ago

Table.create is equivalent to __lua_table__. This is a special compile time function that generates a native table declaration in lua directly from a Haxe array/object literal. On the other hand, Table.fromArray() takes a Haxe array value and uses it to generate a lua table dynamically at run time. They are therefore not interchangeable.

Here is the difference between the generated code:

final table1 = lua.Table.create(["a", "b", "c"]);

final table2 = lua.Table.fromArray(["a", "b", "c"]);
-- table1 native array literal
local table1 = ({"a","b","c"});

-- for table2, first a haxe array is created
local arr = _hx_tab_array({[0]="a", "b", "c"}, 3);
local ret = ({}); -- table is filled in dynamically using a loop
local _g = 0;
local _g1 = arr.length;
while (_g < _g1) do 
  _g = _g + 1;
  local idx = _g - 1;
  ret[idx + 1] = arr[idx];
end;
local table2 = ret;

It would not possible for Table.create to make a table declaration at compile time for an arbitrary array variable, since the compiler doesn't know what elements the array contains. The only way to construct a table from an arbitrary array variable is by looping through all the elements, which is the purpose of Table.fromArray().

Seems to me like the problem here is the lack of documentation explaining the difference between the functions?

Source for __lua_table__: genlua.ml

Source for Table.fromArray(): Table.hx

sebthom commented 1 year ago

Interesting, thanks for the insight. Maybe a more descriptive error message could help.

tobil4sk commented 1 year ago

Agreed, the error message should specifically mention that it requires array literals or object literals.