Repository: luau-lang/luau

Issue #779: Weird conflicting behavior with generic functions in generic types?
Created: 2022-12-08T00:55:36Z
Initial Problem
So I have a generic typed implementation of a binary search tree: https://gist.github.com/MaximumADHD/10ff7eaeeca558ae2b8ceec22350c44f
The type seems to work fine within the module itself, but problems start happening when I import and embed the type into other types:
--!strict

local BST = require(workspace.BST)
local data: { BST.Class<number> } = {}

for i, dummy in data do
	dummy:Add(0)
end
Type Error: (6,1) Type '{ @metatable BinarySearchTree, {| _compare: (number, number) -> number, _root: Node<number>? |} }' could not be converted into '{ @metatable BinarySearchTree, {| _compare: (number, number) -> number, _root: Node<number>? |} }'
caused by:
Type 'BinarySearchTree' from 'game.Workspace.BST' could not be converted into 'BinarySearchTree' from 'game.Workspace.BST'
caused by:
Property 'Add' is not compatible. Type '({ @metatable BinarySearchTree, {| _compare: (a, a) -> number, _root: Node<a>? |} }, a) -> boolean' could not be converted into '<T>({ @metatable BinarySearchTree, {| _compare: (T, T) -> number, _root: Node<T>? |} }, T) -> boolean'; different number of generic type parameters

Comparing the two functions types, it seems the function isn't being treated as a generic correctly?
({ @metatable BinarySearchTree, {| _compare: (a, a) -> number, _root: Node<a>? |} }, a) -> boolean
<T>({ @metatable BinarySearchTree, {| _compare: (T, T) -> number, _root: Node<T>? |} }, T) -> boolean

Possible root issue?
While I was trying to work around this bug, I noticed the following type definition is invalid:
type BST<T> = {
	Add: <T>(self: BST<T>, value: T) -> boolean,
	Remove: <T>(self: BST<T>, value: T) -> (),
	Iter: <T>(self: BST<T>, value: T) -> () -> T?,
	Front: <T>(self: BST<T>) -> T?,
	Back: <T>(self: BST<T>) -> T?,
	ToArray: <T>(self: BST<T>) -> {T},
	Clear: <T>(self: BST<T>) -> (),
}
However, it's fine to add a generic function to a generic type as long as the arguments don't include generics?
type Generic<T> = {
	Test: <T>() -> ()
}
I tried adding this Test function to my workaround and started seeing a similar different number of generic type parameters error:

I'm a little stumped on what the exact problem is, but I hope this repro is enough to track it down.

Duplicate issue #1080: Generics being removed from functions
Created: 2023-10-24T22:02:42Z
The below code generates an unexpected type error, I'm not 100% sure of the cause but I believe it's due to the generics on B.Map being removed from it.
type A<T> = {
    Current: T,
}

type B<T> = {
    Current: T,

    Map: <U>(self: B<T>, Callback: (T) -> U) -> A<U>,
}

type AB<T> = A<T> | B<T>

type C = {
    ChildMap: { [string]: C },
    ABMap: { [string]: AB<any> },
}

local function takesC(C: C)
    for _, Child in C.ChildMap do
        takesC(Child) -- Type Error!
    end
end
Error:
TypeError: Type 'C' from 'FILE_PATH' could not be converted into 'C' from 'FILE_PATH'
caused by:
  Property 'ABMap' is not compatible.
Type
    '{| [string]: A<any> | B<any> |}' from 'FILE_PATH'
could not be converted into
    '{| [string]: A<any> | B<any> |}' from 'FILE_PATH'
caused by:
  Property '[indexer value]' is not compatible.
Type
    'A<any> | B<any>'
could not be converted into
    'A<any> | B<any>'
caused by:
  Not all union options are compatible.
Type 'B<any>' could not be converted into 'A<any> | B<any>'
caused by:
  None of the union options are compatible. For example:
Type 'B<any>' from 'FILE_PATH' could not be converted into 'B<any>' from 'FILE_PATH'
caused by:
  Property 'Map' is not compatible.
Type
    '(B<any>, (any) -> a) -> A<a>'
could not be converted into
    '<U>(B<any>, (any) -> U) -> A<U>'; different number of generic type parameters

Closing comment (2024-01-17T21:09:02Z): Closing as a dupe of #779.