Reproduction Steps
Here is a repro with three examples of regressions to script analysis that appeared in an update a couple of weeks ago:
RegressionsRepro.rbxl (32.6 KB)
Example 1: Type relation no longer works between equivalent generic function types.
--!strict
local LeaksGenericParameterTypes = require(game.ServerScriptService.LeaksGenericParameterTypes)
local object: LeaksGenericParameterTypes.MyType = {
-- Type <g1>(MyType, g1) -> g1 could not be converted into <g2>(MyType, g2) -> g2 ????
-- The type relation on generic functions has regressed; this used to
-- work just fine.
Foo = function(self: LeaksGenericParameterTypes.MyType, x)
return x
end,
Bar = function(self: LeaksGenericParameterTypes.MyType, str: string)
end,
CombineFoo = function(self: LeaksGenericParameterTypes.MyType, ...)
return function() end
end,
}
Example 2: The types of generic parameters are leaking across modules again. This used to be fixed.
--!strict
local LeaksGenericParameterTypes = require(game.ServerScriptService.LeaksGenericParameterTypes)
local object = LeaksGenericParameterTypes.CreateObject()
local i = 2
local alsoI = object:Foo(i)
local j = 'hello'
-- Type 'string' could not be converted into 'number' ???
-- The type of our generic parameter gXXXX is leaking across the module.
-- This did not happen before, and is a regression.
local alsoJ = object:Foo(j)
Example 3: Type relation fails between a named type and itself in a certain example:
--!strict
-- Example 3:
local StateModule = require(game.ServerScriptService.StateModule)
-- Type '(Finalizer) -> ()' could not be converted into '((Finalizer -> ())?' ???
-- All we are doing here is passing a callback where a callback is optionally requested.
-- These types should relate and no warning should be emitted.
-- This is a recent regression, and happens when I try to work around the regression from examples
-- 1 and 2.
StateModule.SetListener('Foo', function(object: StateModule.Finalizer)
object:AddTask(function() end)
object:AddTask(workspace.ChildAdded:Connect(function()end))
end)
Read the code in the repro file carefully for more context as to what is going on here. This is just a minimal version of what I’ve encountered in my game’s code in practice.
Expected Behavior
There should be no warnings present in script analysis for all three examples.
Actual Behavior
There is a warning present for each example; these warnings only started showing up a couple weeks ago.
Workaround
Example 3 uses my attempt to workaround examples 1 and 2 using an actual practical example from my game (the Finalizer) module.
This workaround involves defining the generic function and types in separate statements to avoid generic types leaking across modules:
Unfortunately, this seems to make type relation unsound in a number of practical scenarios that affect my game’s code and some of my public libraries rather extensively.
My workaround for now is to not use generic function types, and instead, in the case of the Finalizer, type AddTask as (Finalizer, any) -> any
. This really sucks as a workaround, because I just had made the switch from that less-typesafe type to the more accurate and typesafe typeof(function(self: Finalizer, t) return t end)
(equivalent to <T>(Finalizer, T) -> T
when generic function types finally get enabled). I have publicly released libraries with AddTask typed as typeof(function(self: Finalizer, t) return t end)
because it used to work, and a recent update has caused this to not work anymore.
Issue Area: Studio
Issue Type: Other
Impact: High
Frequency: Constantly
Date First Experienced: 2021-06-25 00:06:00 (-06:00)
Date Last Experienced: 2021-07-07 00:07:00 (-06:00)