While I have tried to ensure the logic within the discover() function is sound, I am concerned that I might be missing a fundamental constraint of the Type Function API that triggers this *blocked* state.
Could someone please review the recursive structure below and explain what this internal error indicates?
Heres the full code for reference:
--!strict
type IndexAndValue = {Index:any,Value:any}
export type FlattedTable = {
Tables:{
[string]:{[string]:IndexAndValue}
},
Id:string
}
type function GetFlattedTableType(unflattedTable:type)
local s = types.singleton
local tables = types.newtable()
local id = tostring(unflattedTable)
local function discover(t:type):type?
if not t:is("table") then
return nil
end
local tableId = tostring(t)
local infoTableAlreadyExists = tables:readproperty(s(tableId))
if infoTableAlreadyExists then
return infoTableAlreadyExists
end
local infoTable = types.newtable()
local infoTable_LEN = 0
tables:setproperty(s(tableId),infoTable)
for index, readAndWrite in t:properties() do
local value = t:readproperty(index)
-- type checker do not understand it btw (fix it roblox!!!!)
if value == nil then
continue
end
if index:is("table") then
local convertedIndex = discover(index)
if convertedIndex then
index = convertedIndex
end
end
if value:is("table") then
local convertedValue = discover(value)
if convertedValue then
value = convertedValue
end
end
local indexAndValue = types.newtable{
[s("Index")] = index,
[s("Value")] = value
}
infoTable:setproperty(s(tostring(infoTable_LEN + 1)),indexAndValue)
infoTable_LEN += 1
end
return types.newtable{ [s("Id")] = s(tableId) }
end
discover(unflattedTable)
return types.newtable{
[s("Tables")] = tables,
[s("Id")] = s(id),
}
end
--type function GetUnflattedTableType(flattedTable)
--end
local TableFlattener = {}
function TableFlattener.Encode<T>(tbl:T):GetFlattedTableType<T>
local tables = {}
local id = tostring(tbl)
local function discover(t)
if typeof(t) ~= "table" then
return
end
local tableId = tostring(t)
local infoTableAlreadyExists = tables[tableId]
if infoTableAlreadyExists then
return infoTableAlreadyExists
end
local infoTable = {}
local infoTable_LEN = 0
tables[tableId] = infoTable
for index, value in pairs(t) do
if typeof(index) == "table" then
index = discover(index)
end
if typeof(value) == "table" then
value = discover(value)
end
local indexAndValue = {
Index = index,
Value = value,
}
infoTable[tostring(infoTable_LEN + 1)] = indexAndValue
infoTable_LEN += 1
end
return {Id = tostring(t)}
end
discover(tbl)
return {
Tables = tables,
Id = id
}
end
--function TableFlattener.Decode(flattedTable)
--end
return TableFlattener
That’s a C++ error from within Luau itself. It’s already been reported as an issue on the Luau GitHub, so it should get fixed eventually (it’s a bug).
It’s actually a limitation of the Luau solver. It happens when you use a type function with generic arguments as the return type of a generic function. basically this case:
type function F(t: type): type
return t
end
local function f<T>(p: T): F<T> -- Type Error: Internal error: Lambda has non-function type *blocked-62021*
return {}
end
That error means that, for some reason, the solver couldn’t infer a concrete type. This can happen in different situations, but in this case it’s just a limitation of the solver itself.
You can defer the type function inference, for example by wrapping it in a table
local function f<T>(p: T): {F<T> }
return {}
end
But the most effective approach is to make the solver stop trying to infer the return value by using a type cast
local function f<T>(p: T): F<T>
return {} :: F<T>
end
In your case, it would be something like this.
return {
Tables = tables,
Id = id
} :: GetFlattedTableType<T>
Just a heads-up, there are a few other issues in your code, but that’s a different topic.