-
Notifications
You must be signed in to change notification settings - Fork 40
Description
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:
-
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.
- in last element of a tuple, i.e.
-
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 asint
,T...
will be treated asT
, 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?