luau-lang / luau

A fast, small, safe, gradually typed embeddable scripting language derived from Lua
https://luau.org
MIT License
3.95k stars 372 forks source link

Incorrect argument count pushed when calling function as a argument to another function #1397

Closed NixAJ closed 1 week ago

NixAJ commented 1 week ago

Luau Version: 0.642

Compile Options

Luau::CompileOptions compileOptions;
{
    compileOptions.optimizationLevel = 1;
    compileOptions.debugLevel = 2;
    compileOptions.coverageLevel = 2;
    compileOptions.vectorLib = "vec3";
    compileOptions.vectorType = "vec3";
    compileOptions.vectorCtor = "new";
}

Function Definitions Foo = 4 Parameters, 1 Return Value Bar = 4 Parameters, 2 Return Values

Edit: After some searching, I found some documentation for lua version 5.4 specifically under 3.4.12 stating the following "When a multires expression is used in a list of expressions without being the last element, or in a place where the syntax expects a single expression, Lua adjusts the result list of that expression to one element. As a particular case, the syntax expects a single expression inside a parenthesized expression; therefore, adding parentheses around a multires expression forces it to produce exactly one result." https://www.lua.org/manual/5.4/manual.html#3.4.12

I am unsure now, if this is also part of the idea for 5.1, as I could not find any statements searching for the same kind of language being used above in the 5.1 documentation. If it is, then this is technically not a bug, and perhaps would turn into a suggestion. I am unsure if there is a good reason for the design.

Original: In the example below, I am calling Foo, as the arguments, I call Bar twice. I believe what should happen is each call to Bar should push 2 numbers to the stack, before calling Foo, however only 3 numbers are pushed in total.

When converting this example to use a C++ side function (To make the debugging process easier for me), it appears that during the handling of the instruction "LOP_CALL" in lvmexecute.cpp, the nresults in the first call is set to 1, and -1 in the second. This appears to me as an incorrect generation of bytecode.

Effectively, the first call to Bar ends up pushing just one of the two numbers it is supposed to, and the second call to Bar actual push both numbers.

Example Code

local function Foo(num1, num2, num3, num4)
    print("Foo -- Num1 : " .. num1)
    print("Foo -- Num2 : " .. num2)
    print("Foo -- Num3 : " .. num3)
    print("Foo -- Num4 : " .. num4)
    return num1 + num2 + num3 + num4
end

local function Bar(num1, num2, num3, num4)
    print("Bar -- Num1 : " .. num1)
    print("Bar -- Num2 : " .. num2)
    print("Bar -- Num3 : " .. num3)
    print("Bar -- Num4 : " .. num4)

    local out1 = num1 + num2
    local out2 = num3 + num4
    return out1, out2
end

Foo(Bar(1, 2, 3, 4), Bar(5, 6, 7, 8))

Example Output

Bar -- Num1 : 1
Bar -- Num2 : 2
Bar -- Num3 : 3
Bar -- Num4 : 4
Bar -- Num1 : 5
Bar -- Num2 : 6
Bar -- Num3 : 7
Bar -- Num4 : 8
Foo -- Num1 : 3
Foo -- Num2 : 11
Foo -- Num3 : 15
-- Error here when attempting to print Num4 in Foo -- [string "test.luau"]: attempt to concatenate string with nil

Expected Output

Bar -- Num1 : 1
Bar -- Num2 : 2
Bar -- Num3 : 3
Bar -- Num4 : 4
Bar -- Num1 : 5
Bar -- Num2 : 6
Bar -- Num3 : 7
Bar -- Num4 : 8
Foo -- Num1 : 3
Foo -- Num2 : 7
Foo -- Num3 : 11
Foo -- Num4 : 15
vegorov-rbx commented 1 week ago

Only the last argument to a function passes multiple return values into a list expression.

You can find an example in the documentation you linked:

print(f(), x)      -- prints the first result from f() and x.