Generic types have leaks issues since last update, breaking previously valid code

Reproduction Steps

Type the following into a script and examine the script analysis output:


-- Example 1: Explicitly-defined parameter types for generic
-- functions become "freed" and leaks for some reason

--!strict
type MyObject = {
	getReturnValue: <T>(cb: () -> T) -> T
}
local object: MyObject = {
	getReturnValue = function<T>(cb: () -> T): T
		return cb()
	end,
}

-- OK
local x = object.getReturnValue(function()
	return ""
end)

-- OK
local y = object.getReturnValue(function()
	return 2
end)


type ComplexObject<T> = {
	id: T,
	nested: MyObject
}
-- Not OK; "Type '() -> a' could not be converted into
-- '() -> T'" / "Generic supertype escaping scope"
local complex: ComplexObject<string> = {
	id = "Foo",
	nested = object,
}


-- Example 2: The leaked type can be inferred by external
-- code and have its type written over


-- Cheating to exemplify the issue further:
local complex2: ComplexObject<string> = nil

-- OK
local x = complex2.nested.getReturnValue(function(): string
	return ""
end)

-- Not OK; "Type 'number' could not be converted into 'string'"
-- The type of the generic function "getReturnValue" function has leaked
-- and it now thinks it should always return a string even though it's a
-- generic function that accepts/infers a type on every call.
local y = complex2.nested.getReturnValue(function()
	return 3
end)

Expected Behavior

All of the examples should be valid. I am pretty sure this is a once-supported feature that has had a regression, as it’s affecting some of my existing code.
There was a point at which nested generics were not supported, but it had been announced months ago that they are now supported, so this is certainly a bug/regression:

(Quote from thread)

People may remember that back in April we announced generic functions, but then had to disable them. That was because DataBrain discovered a nasty interaction between typeof and generics, which meant that it was possible to write code that needed nested generic functions, which weren’t supported back then.

Well, now we do support nested generic functions, so you can write code like

function mkPoint(x)
  return function(y)
    return { x = x, y = y }
  end
end

and have Luau infer a type where a generic function returns a generic function

function mkPoint<X>(x : X) : <Y>(Y) -> Point<X,Y>
  return function<Y>(y : Y) : Point<X,Y>
    return { x = x, y = y }
  end
end

For people who like jargon, Luau now supports Rank N Types , where previously it only supported Rank 1 Types.People may remember that back in April we announced generic functions, but then had to disable them. That was because DataBrain discovered a nasty interaction between typeof and generics, which meant that it was possible to write code that needed nested generic functions, which weren’t supported back then.

Well, now we do support nested generic functions, so you can write code like

function mkPoint(x)
  return function(y)
    return { x = x, y = y }
  end
end

and have Luau infer a type where a generic function returns a generic function

function mkPoint<X>(x : X) : <Y>(Y) -> Point<X,Y>
  return function<Y>(y : Y) : Point<X,Y>
    return { x = x, y = y }
  end
end

For people who like jargon, Luau now supports Rank N Types , where previously it only supported Rank 1 Types.

Actual Behavior

Internally, <T>() -> T somewhere along the way gets converted to the isomorphic () -> a, and hilarity ensues.

It seems that T becomes a “freed” type (a) and then is leaked when used in further code.

Workaround

Don’t use nested generic functions. Not an option for me at this point, because I have been relying on this being a supported feature.

Issue Area: Studio
Issue Type: Other
Impact: Very High
Frequency: Constantly
Date First Experienced: 2022-05-15 00:05:00 (-06:00)
Date Last Experienced: 2022-05-15 00:05:00 (-06:00)

Thank you again for the report.
We are going to investigate this. Right now we are not sure what recent change caused this regression.

2 Likes

We have a fix ready for this internally, it should arrive with the Studio update next week.

2 Likes

Sorry to bump, we want to close all fixed issues and I forgot to post the final update in this topic!

This indeed was fixed in a late May 2022 update of Roblox Studio.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.