Skip to content

Improve semantics of variadic expansion #701

@taminomara

Description

@taminomara

Right now, we allow variadic type expansion anywhere in a type expression. This leads to potential unsoundness where Variadic<T> are not properly unwrapped:

local t --- @type int...
local a, b, c, d = t
-- `a`, `b`, `c`, `d` are all `int`.

local t --- @type [int..., string]
local a, b, c, d = t[1]
-- `a`, `b`, `c`, `d` are all `int`.
local e = t[2]
-- `e` is `string`

I'd like to fix this.

There are two approaches we could take:

  1. disallow variadic expansion in places where it can't be handled.

    Specifically, using T... will be allowed:

    • in last element of a tuple, i.e. [int, string...],
    • in generic arguments, i.e. Foo<T...> (semantics of this needs revision, but I'll leave it for later),
    • on top level of function/operator parameters and return values, i.e. @param ... T....

    The rest will be reported as an error.

  2. handle variadics everywhere, with default behavior to extract the first value.

    This will make variadic expansion a no-op outside of the cases mentioned above.

    Specifically, int... will be treated as int, T... will be treated as T, and so on.

I like option 1. Option 2 is more error prone, and can lead to unexpected behavior. For example, consider this:

--- @generic T
--- @param ... T...
--- @return [T..., table]
function foo(...) end

local x = foo(1, "")

What should the type of x be? Is it [integer, string, table], [integer, table], or [integer, string]? Current implementation derives [integer, string], but is it reasonable?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions