2021-10-30 04:25:12 +08:00
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
# include "Luau/TypeInfer.h"
2023-01-05 04:53:17 +08:00
# include "Luau/Type.h"
2022-03-18 08:46:04 +08:00
# include "Luau/Scope.h"
# include <algorithm>
2021-10-30 04:25:12 +08:00
# include "Fixture.h"
# include "doctest.h"
2023-07-28 23:13:53 +08:00
LUAU_FASTFLAG ( LuauInstantiateInSubtyping ) ;
LUAU_FASTFLAG ( DebugLuauDeferredConstraintResolution ) ;
2022-07-08 09:22:39 +08:00
2021-10-30 04:25:12 +08:00
using namespace Luau ;
TEST_SUITE_BEGIN ( " GenericsTests " ) ;
TEST_CASE_FIXTURE ( Fixture , " check_generic_function " )
{
CheckResult result = check ( R " (
function id < a > ( x : a ) : a
return x
end
local x : string = id ( " hi " )
local y : number = id ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2024-02-24 04:08:34 +08:00
CHECK_EQ ( builtinTypes - > stringType , requireType ( " x " ) ) ;
CHECK_EQ ( builtinTypes - > numberType , requireType ( " y " ) ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " check_generic_local_function " )
{
CheckResult result = check ( R " (
local function id < a > ( x : a ) : a
return x
end
local x : string = id ( " hi " )
local y : number = id ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2024-02-24 04:08:34 +08:00
CHECK_EQ ( builtinTypes - > stringType , requireType ( " x " ) ) ;
CHECK_EQ ( builtinTypes - > numberType , requireType ( " y " ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " check_generic_local_function2 " )
{
CheckResult result = check ( R " (
local function id < a > ( x : a ) : a
return x
end
local x = id ( " hi " )
local y = id ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( builtinTypes - > stringType , requireType ( " x " ) ) ;
CHECK_EQ ( builtinTypes - > numberType , requireType ( " y " ) ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " unions_and_generics " )
{
CheckResult result = check ( R " (
type foo = < T > ( T | { T } ) - > T
local foo = ( nil : : any ) : : foo
type Test = number | { number }
local res = foo ( 1 : : Test )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
if ( FFlag : : DebugLuauDeferredConstraintResolution )
CHECK_EQ ( " number | {number} " , toString ( requireType ( " res " ) ) ) ;
else // in the old solver, this just totally falls apart
CHECK_EQ ( " a " , toString ( requireType ( " res " ) ) ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " check_generic_typepack_function " )
{
CheckResult result = check ( R " (
function id < a . . . > ( . . . : a . . . ) : ( a . . . ) return . . . end
local x : string , y : boolean = id ( " hi " , true )
local z : number = id ( 37 )
id ( )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " types_before_typepacks " )
{
CheckResult result = check ( R " (
function f < a , b . . . > ( ) end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " local_vars_can_be_polytypes " )
{
CheckResult result = check ( R " (
local function id < a > ( x : a ) : a return x end
local f : < a > ( a ) - > a = id
local x : string = f ( " hi " )
local y : number = f ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " inferred_local_vars_can_be_polytypes " )
2021-10-30 04:25:12 +08:00
{
CheckResult result = check ( R " (
local function id ( x ) return x end
print ( " This is bogus " ) - - TODO : CLI - 39916
local f = id
local x : string = f ( " hi " )
local y : number = f ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " local_vars_can_be_instantiated_polytypes " )
2021-10-30 04:25:12 +08:00
{
CheckResult result = check ( R " (
local function id ( x ) return x end
print ( " This is bogus " ) - - TODO : CLI - 39916
local f : ( number ) - > number = id
local g : ( string ) - > string = id
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " properties_can_be_polytypes " )
{
CheckResult result = check ( R " (
local t = { }
t . m = function < a > ( x : a ) : a return x end
local x : string = t . m ( " hi " )
local y : number = t . m ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " properties_can_be_instantiated_polytypes " )
{
CheckResult result = check ( R " (
local t : { m : ( number ) - > number } = { m = function ( x : number ) return x + 1 end }
local function id < a > ( x : a ) : a return x end
t . m = id
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " check_nested_generic_function " )
{
CheckResult result = check ( R " (
local function f ( )
local function id < a > ( x : a ) : a
return x
end
local x : string = id ( " hi " )
local y : number = id ( 37 )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " check_recursive_generic_function " )
{
CheckResult result = check ( R " (
local function id < a > ( x : a ) : a
local y : string = id ( " hi " )
local z : number = id ( 37 )
return x
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " check_mutual_generic_functions " )
{
CheckResult result = check ( R " (
2024-02-10 01:51:12 +08:00
function id1 < a > ( x : a ) : a
2021-10-30 04:25:12 +08:00
local y : string = id2 ( " hi " )
local z : number = id2 ( 37 )
return x
end
2024-02-10 01:51:12 +08:00
2021-10-30 04:25:12 +08:00
function id2 < a > ( x : a ) : a
local y : string = id1 ( " hi " )
local z : number = id1 ( 37 )
return x
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-02-10 01:51:12 +08:00
TEST_CASE_FIXTURE ( Fixture , " check_mutual_generic_functions_unannotated " )
{
if ( ! FFlag : : DebugLuauDeferredConstraintResolution )
return ;
CheckResult result = check ( R " (
function id1 ( x )
local y : string = id2 ( " hi " )
local z : number = id2 ( 37 )
return x
end
function id2 ( x )
local y : string = id1 ( " hi " )
local z : number = id1 ( 37 )
return x
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " check_mutual_generic_functions_errors " )
{
if ( ! FFlag : : DebugLuauDeferredConstraintResolution )
return ;
CheckResult result = check ( R " (
function id1 ( x )
local y : string = id2 ( 37 ) - - odd
local z : number = id2 ( " hi " ) - - even
return x
end
function id2 ( x )
local y : string = id1 ( 37 ) - - odd
local z : number = id1 ( " hi " ) - - even
return x
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 4 , result ) ;
// odd errors
for ( int i = 0 ; i < 4 ; i + = 2 )
{
TypeMismatch * tm = get < TypeMismatch > ( result . errors [ i ] ) ;
REQUIRE ( tm ) ;
CHECK_EQ ( " string " , toString ( tm - > wantedType ) ) ;
CHECK_EQ ( " number " , toString ( tm - > givenType ) ) ;
}
// even errors
for ( int i = 1 ; i < 4 ; i + = 2 )
{
TypeMismatch * tm = get < TypeMismatch > ( result . errors [ i ] ) ;
REQUIRE ( tm ) ;
CHECK_EQ ( " number " , toString ( tm - > wantedType ) ) ;
CHECK_EQ ( " string " , toString ( tm - > givenType ) ) ;
}
}
2021-10-30 04:25:12 +08:00
TEST_CASE_FIXTURE ( Fixture , " generic_functions_in_types " )
{
CheckResult result = check ( R " (
type T = { id : < a > ( a ) - > a }
local x : T = { id = function < a > ( x : a ) : a return x end }
local y : string = x . id ( " hi " )
local z : number = x . id ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " generic_factories " )
{
CheckResult result = check ( R " (
type T < a > = { id : ( a ) - > a }
type Factory = { build : < a > ( ) - > T < a > }
local f : Factory = {
build = function < a > ( ) : T < a >
return {
id = function ( x : a ) : a
return x
end
}
end
}
local y : string = f . build ( ) . id ( " hi " )
local z : number = f . build ( ) . id ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " factories_of_generics " )
{
CheckResult result = check ( R " (
type T = { id : < a > ( a ) - > a }
type Factory = { build : ( ) - > T }
local f : Factory = {
build = function ( ) : T
return {
id = function < a > ( x : a ) : a
return x
end
}
end
}
local x : T = f . build ( )
local y : string = x . id ( " hi " )
local z : number = x . id ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " infer_generic_function " )
{
CheckResult result = check ( R " (
function id ( x )
return x
end
local x : string = id ( " hi " )
local y : number = id ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
TypeId idType = requireType ( " id " ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * idFun = get < FunctionType > ( idType ) ;
2021-10-30 04:25:12 +08:00
REQUIRE ( idFun ) ;
auto [ args , varargs ] = flatten ( idFun - > argTypes ) ;
2022-06-17 09:05:14 +08:00
auto [ rets , varrets ] = flatten ( idFun - > retTypes ) ;
2021-10-30 04:25:12 +08:00
CHECK_EQ ( idFun - > generics . size ( ) , 1 ) ;
CHECK_EQ ( idFun - > genericPacks . size ( ) , 0 ) ;
2022-04-15 07:57:43 +08:00
CHECK_EQ ( follow ( args [ 0 ] ) , follow ( idFun - > generics [ 0 ] ) ) ;
CHECK_EQ ( follow ( rets [ 0 ] ) , follow ( idFun - > generics [ 0 ] ) ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " infer_generic_local_function " )
{
CheckResult result = check ( R " (
local function id ( x )
return x
end
local x : string = id ( " hi " )
local y : number = id ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
TypeId idType = requireType ( " id " ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * idFun = get < FunctionType > ( idType ) ;
2021-10-30 04:25:12 +08:00
REQUIRE ( idFun ) ;
auto [ args , varargs ] = flatten ( idFun - > argTypes ) ;
2022-06-17 09:05:14 +08:00
auto [ rets , varrets ] = flatten ( idFun - > retTypes ) ;
2021-10-30 04:25:12 +08:00
CHECK_EQ ( idFun - > generics . size ( ) , 1 ) ;
CHECK_EQ ( idFun - > genericPacks . size ( ) , 0 ) ;
2022-04-15 07:57:43 +08:00
CHECK_EQ ( follow ( args [ 0 ] ) , follow ( idFun - > generics [ 0 ] ) ) ;
CHECK_EQ ( follow ( rets [ 0 ] ) , follow ( idFun - > generics [ 0 ] ) ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " infer_nested_generic_function " )
{
CheckResult result = check ( R " (
local function f ( )
local function id ( x )
return x
end
local x : string = id ( " hi " )
local y : number = id ( 37 )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " calling_self_generic_methods " )
{
CheckResult result = check ( R " (
local x = { }
function x : id ( x ) return x end
function x : f ( )
local x : string = self : id ( " hi " )
local y : number = self : id ( 37 )
end
) " );
2023-02-11 03:40:38 +08:00
2024-03-31 07:14:44 +08:00
LUAU_REQUIRE_ERRORS ( result ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " infer_generic_property " )
{
CheckResult result = check ( R " (
local t = { }
t . m = function ( x ) return x end
local x : string = t . m ( " hi " )
local y : number = t . m ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " function_arguments_can_be_polytypes " )
{
CheckResult result = check ( R " (
local function f ( g : < a > ( a ) - > a )
local x : number = g ( 37 )
local y : string = g ( " hi " )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " function_results_can_be_polytypes " )
{
CheckResult result = check ( R " (
local function f ( ) : < a > ( a ) - > a
local function id < a > ( x : a ) : a return x end
return id
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " type_parameters_can_be_polytypes " )
{
CheckResult result = check ( R " (
local function id < a > ( x : a ) : a return x end
local f : < a > ( a ) - > a = id ( id )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " dont_leak_generic_types " )
{
CheckResult result = check ( R " (
local function f ( y )
- - this will only typecheck if we infer z : any
- - so f : ( any ) - > ( any )
local z = y
local function id ( x )
z = x - - this assignment is what forces z : any
return x
end
local x : string = id ( " hi " )
local y : number = id ( 37 )
return z
end
- - so this assignment should fail
local b : boolean = f ( true )
) " );
2024-02-24 04:08:34 +08:00
2021-11-20 00:10:07 +08:00
LUAU_REQUIRE_ERRORS ( result ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " dont_leak_inferred_generic_types " )
{
CheckResult result = check ( R " (
local function f ( y )
local z = y
local function id ( x )
z = x
return x
end
local x : string = id ( " hi " )
local y : number = id ( 37 )
end
) " );
2021-11-20 00:10:07 +08:00
LUAU_REQUIRE_ERRORS ( result ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " dont_substitute_bound_types " )
{
CheckResult result = check ( R " (
type T = { m : < a > ( a ) - > T }
function f ( t : T )
local x : T = t . m ( 37 )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " dont_unify_bound_types " )
{
CheckResult result = check ( R " (
type F = < a > ( ) - > < b > ( a , b ) - > a
type G = < b > ( b , b ) - > b
local f : F = function < a > ( )
local x
return function < b > ( y : a , z : b ) : a
if not ( x ) then x = y end
return x
end
end
- - This assignment shouldn ' t typecheck
- - If it does , it means we instantiated
- - f as ( ) - > < b > ( X , b ) - > X , then unified X to be b
local g : G = f ( )
- - Oh dear , if that works then the type system is unsound
local a : string = g ( " not a number " , " hi " )
local b : number = g ( 5 , 37 )
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " mutable_state_polymorphism " )
{
// Replaying the classic problem with polymorphism and mutable state in Luau
// See, e.g. Tofte (1990)
// https://www.sciencedirect.com/science/article/pii/089054019090018D.
CheckResult result = check ( R " (
- - ! strict
- - Our old friend the polymorphic identity function
local function id ( x ) return x end
local a : string = id ( " hi " )
local b : number = id ( 37 )
- - This allows < a > ( a ) - > a to be expressed without generic function syntax
type Id = typeof ( id )
- - This function should have type
- - < a > ( ) - > ( a ) - > a
- - not type
- - ( ) - > < a > ( a ) - > a
local function ohDear ( ) : Id
local y
function oh ( x )
- - Returns the same x every time it ' s called
if not ( y ) then y = x end
return y
end
return oh
end
- - oh dear , f claims to polymorphic which it shouldn ' t be
local f : Id = ohDear ( )
- - the first call sets y
local a : string = f ( " not a number " )
- - so b has value " not a number " at run time
local b : number = f ( 37 )
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " rank_N_types_via_typeof " )
{
CheckResult result = check ( R " (
- - ! strict
local function id ( x ) return x end
local x : string = id ( " hi " )
local y : number = id ( 37 )
- - This allows < a > ( a ) - > a to be expressed without generic function syntax
type Id = typeof ( id )
- - The rank 1 restriction causes this not to typecheck , since it ' s
- - declared as returning a polytype .
local function returnsId ( ) : Id
return id
end
- - So this won ' t typecheck
local f : Id = returnsId ( )
local a : string = f ( " hi " )
local b : number = f ( 37 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " duplicate_generic_types " )
{
CheckResult result = check ( R " (
function f < a , a > ( x : a ) : a return x end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " duplicate_generic_type_packs " )
{
CheckResult result = check ( R " (
function f < a . . . , a . . . > ( ) end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " typepacks_before_types " )
{
CheckResult result = check ( R " (
function f < a . . . , b > ( ) end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " variadic_generics " )
{
CheckResult result = check ( R " (
function f < a > ( . . . : a ) end
type F < a > = ( . . . a ) - > . . . a
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " generic_type_pack_syntax " )
{
CheckResult result = check ( R " (
function f < a . . . > ( . . . : a . . . ) : ( a . . . ) return . . . end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( toString ( requireType ( " f " ) ) , " <a...>(a...) -> (a...) " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " generic_type_pack_parentheses " )
{
CheckResult result = check ( R " (
function f < a . . . > ( . . . : a . . . ) : any return ( . . . ) end
) " );
2024-07-27 01:47:49 +08:00
// This should really error, but the error from the old solver is wrong.
// `a...` is a generic type pack, and we don't know that it will be non-empty, thus this code may not work.
if ( FFlag : : DebugLuauDeferredConstraintResolution )
LUAU_REQUIRE_NO_ERRORS ( result ) ;
else
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " better_mismatch_error_messages " )
{
CheckResult result = check ( R " (
function f < T > ( . . . : T . . . )
return . . .
end
function g < T . . . > ( a : T )
return a
end
) " );
2024-07-27 01:47:49 +08:00
SwappedGenericTypeParameter * fErr ;
SwappedGenericTypeParameter * gErr ;
if ( FFlag : : DebugLuauDeferredConstraintResolution )
{
LUAU_REQUIRE_ERROR_COUNT ( 3 , result ) ;
// The first error here is an unknown symbol that is redundant with the `fErr`.
fErr = get < SwappedGenericTypeParameter > ( result . errors [ 1 ] ) ;
gErr = get < SwappedGenericTypeParameter > ( result . errors [ 2 ] ) ;
}
else
{
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
fErr = get < SwappedGenericTypeParameter > ( result . errors [ 0 ] ) ;
gErr = get < SwappedGenericTypeParameter > ( result . errors [ 1 ] ) ;
}
2021-10-30 04:25:12 +08:00
REQUIRE ( fErr ) ;
CHECK_EQ ( fErr - > name , " T " ) ;
CHECK_EQ ( fErr - > kind , SwappedGenericTypeParameter : : Pack ) ;
REQUIRE ( gErr ) ;
CHECK_EQ ( gErr - > name , " T " ) ;
CHECK_EQ ( gErr - > kind , SwappedGenericTypeParameter : : Type ) ;
}
TEST_CASE_FIXTURE ( Fixture , " reject_clashing_generic_and_pack_names " )
{
CheckResult result = check ( R " (
function f < a , a . . . > ( ) end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
DuplicateGenericParameter * err = get < DuplicateGenericParameter > ( result . errors [ 0 ] ) ;
REQUIRE ( err ! = nullptr ) ;
CHECK_EQ ( err - > parameterName , " a " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " instantiation_sharing_types " )
{
CheckResult result = check ( R " (
function f ( z )
local o = { }
o . x = o
o . y = { 5 }
o . z = z
return o
end
local o1 = f ( true )
local x1 , y1 , z1 = o1 . x , o1 . y , o1 . z
local o2 = f ( " hi " )
local x2 , y2 , z2 = o2 . x , o2 . y , o2 . z
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK ( requireType ( " x1 " ) ! = requireType ( " x2 " ) ) ;
CHECK ( requireType ( " y1 " ) = = requireType ( " y2 " ) ) ;
CHECK ( requireType ( " z1 " ) ! = requireType ( " z2 " ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " quantification_sharing_types " )
{
CheckResult result = check ( R " (
function f ( x ) return { 5 } end
function g ( x , y ) return f ( x ) end
local z1 = f ( 5 )
local z2 = g ( true , " hi " )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK ( requireType ( " z1 " ) = = requireType ( " z2 " ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " typefuns_sharing_types " )
{
CheckResult result = check ( R " (
type T < a > = { x : { a } , y : { number } }
local o1 : T < boolean > = { x = { true } , y = { 5 } }
local x1 , y1 = o1 . x , o1 . y
local o2 : T < string > = { x = { " hi " } , y = { 37 } }
local x2 , y2 = o2 . x , o2 . y
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK ( requireType ( " x1 " ) ! = requireType ( " x2 " ) ) ;
CHECK ( requireType ( " y1 " ) = = requireType ( " y2 " ) ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " bound_tables_do_not_clone_original_fields " )
2021-11-05 23:47:21 +08:00
{
CheckResult result = check ( R " (
local exports = { }
local nested = { }
nested . name = function ( t , k )
local a = t . x . y
return rawget ( t , k )
end
exports . nested = nested
return exports
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2021-12-03 14:41:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " instantiated_function_argument_names " )
{
CheckResult result = check ( R " (
local function f < T , U . . . > ( a : T , . . . : U . . . ) end
f ( 1 , 2 , 3 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
auto ty = findTypeAtPosition ( Position ( 3 , 0 ) ) ;
REQUIRE ( ty ) ;
ToStringOptions opts ;
opts . functionTypeArguments = true ;
CHECK_EQ ( toString ( * ty , opts ) , " (a: number, number, number) -> () " ) ;
}
2021-12-11 06:05:05 +08:00
TEST_CASE_FIXTURE ( Fixture , " error_detailed_function_mismatch_generic_types " )
{
CheckResult result = check ( R " (
type C = ( ) - > ( )
type D = < T > ( ) - > ( )
local c : C
local d : D = c
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2022-01-28 07:46:05 +08:00
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Type '() -> ()' could not be converted into '<T>() -> ()'; different number of generic type parameters) " ) ;
2021-12-11 06:05:05 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " error_detailed_function_mismatch_generic_pack " )
{
CheckResult result = check ( R " (
type C = ( ) - > ( )
type D = < T . . . > ( ) - > ( )
local c : C
local d : D = c
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-08-02 22:30:04 +08:00
CHECK_EQ (
toString ( result . errors [ 0 ] ) ,
R " (Type '() -> ()' could not be converted into '<T...>() -> ()'; different number of generic type pack parameters) "
) ;
2021-12-11 06:05:05 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " generic_functions_dont_cache_type_parameters " )
2022-02-05 04:46:08 +08:00
{
CheckResult result = check ( R " (
2023-12-16 05:29:06 +08:00
- - See https : //github.com/luau-lang/luau/issues/332
2022-02-05 04:46:08 +08:00
- - This function has a type parameter with the same name as clones ,
- - so if we cache type parameter names for functions these get confused .
- - function id < Z > ( x : Z ) : Z
function id < X > ( x : X ) : X
return x
end
function clone < X , Y > ( dict : { [ X ] : Y } ) : { [ X ] : Y }
local copy = { }
for k , v in pairs ( dict ) do
copy [ k ] = v
end
return copy
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-03-05 00:36:33 +08:00
TEST_CASE_FIXTURE ( Fixture , " generic_functions_should_be_memory_safe " )
{
CheckResult result = check ( R " (
- - ! strict
- - At one point this produced a UAF
type T < a > = { a : U < a > , b : a }
type U < a > = { c : T < a > ? , d : a }
local x : T < number > = { a = { c = nil , d = 5 } , b = 37 }
x . a . c = x
local y : T < string > = { a = { c = nil , d = 5 } , b = 37 }
y . a . c = y
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
2023-11-18 02:46:18 +08:00
if ( FFlag : : DebugLuauDeferredConstraintResolution )
2024-02-24 04:08:34 +08:00
CHECK (
toString ( result . errors . at ( 0 ) ) = =
2024-08-02 22:30:04 +08:00
R " (Type '{ a: { c: nil, d: number }, b: number }' could not be converted into 'T<number>'; type { a: { c: nil, d: number }, b: number }[read " a " ][read " c " ] (nil) is not exactly T<number>[read " a " ][read " c " ][0] (T<number>)) "
) ;
2023-11-18 02:46:18 +08:00
else
{
const std : : string expected = R " (Type 'y' could not be converted into 'T<string>'
2022-12-03 02:09:59 +08:00
caused by :
2023-10-14 04:20:12 +08:00
Property ' a ' is not compatible .
2023-08-11 22:42:37 +08:00
Type ' { c : T < string > ? , d : number } ' could not be converted into ' U < string > '
2022-12-03 02:09:59 +08:00
caused by :
2023-10-14 04:20:12 +08:00
Property ' d ' is not compatible .
2023-08-11 22:42:37 +08:00
Type ' number ' could not be converted into ' string ' in an invariant context ) " ;
2023-11-18 02:46:18 +08:00
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
}
2022-03-05 00:36:33 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " generic_type_pack_unification1 " )
{
CheckResult result = check ( R " (
- - ! strict
type Dispatcher = {
useMemo : < T . . . > ( create : ( ) - > T . . . ) - > T . . .
}
local TheDispatcher : Dispatcher = {
useMemo = function < U . . . > ( create : ( ) - > U . . . ) : U . . .
return create ( )
end
}
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " generic_type_pack_unification2 " )
{
CheckResult result = check ( R " (
- - ! strict
type Dispatcher = {
useMemo : < T . . . > ( create : ( ) - > T . . . ) - > T . . .
}
local TheDispatcher : Dispatcher = {
useMemo = function ( create )
return create ( )
end
}
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " generic_type_pack_unification3 " )
{
CheckResult result = check ( R " (
- - ! strict
type Dispatcher = {
useMemo : < S , T . . . > ( arg : S , create : ( S ) - > T . . . ) - > T . . .
}
local TheDispatcher : Dispatcher = {
useMemo = function < T , U . . . > ( arg : T , create : ( T ) - > U . . . ) : U . . .
return create ( arg )
end
}
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-03-12 00:55:02 +08:00
TEST_CASE_FIXTURE ( Fixture , " generic_argument_count_too_few " )
{
CheckResult result = check ( R " (
function test ( a : number )
return 1
end
function wrapper < A . . . > ( f : ( A . . . ) - > number , . . . : A . . . )
end
wrapper ( test )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2022-09-16 06:38:17 +08:00
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Argument count mismatch. Function 'wrapper' expects 2 arguments, but only 1 is specified) " ) ;
2022-03-12 00:55:02 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " generic_argument_count_too_many " )
{
CheckResult result = check ( R " (
function test2 ( a : number , b : string )
return 1
end
function wrapper < A . . . > ( f : ( A . . . ) - > number , . . . : A . . . )
end
wrapper ( test2 , 1 , " " , 3 )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2022-09-16 06:38:17 +08:00
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Argument count mismatch. Function 'wrapper' expects 3 arguments, but 4 are specified) " ) ;
2022-03-12 00:55:02 +08:00
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " generic_function " )
{
CheckResult result = check ( R " (
function id ( x ) return x end
local a = id ( 55 )
local b = id ( nil )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2022-04-15 07:57:43 +08:00
CHECK_EQ ( " <a>(a) -> a " , toString ( requireType ( " id " ) ) ) ;
2023-03-11 04:21:07 +08:00
CHECK_EQ ( * builtinTypes - > numberType , * requireType ( " a " ) ) ;
CHECK_EQ ( * builtinTypes - > nilType , * requireType ( " b " ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " generic_table_method " )
{
CheckResult result = check ( R " (
local T = { }
function T : bar ( i )
return i
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
TypeId tType = requireType ( " T " ) ;
2023-01-05 04:53:17 +08:00
TableType * tTable = getMutable < TableType > ( tType ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( tTable ! = nullptr ) ;
2022-08-12 05:01:33 +08:00
REQUIRE ( tTable - > props . count ( " bar " ) ) ;
2023-04-29 03:55:13 +08:00
TypeId barType = tTable - > props [ " bar " ] . type ( ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( barType ! = nullptr ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * ftv = get < FunctionType > ( follow ( barType ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE_MESSAGE ( ftv ! = nullptr , " Should be a function: " < < * barType ) ;
std : : vector < TypeId > args = flatten ( ftv - > argTypes ) . first ;
TypeId argType = args . at ( 1 ) ;
2023-04-08 05:01:29 +08:00
CHECK_MESSAGE ( get < GenericType > ( argType ) , " Should be generic: " < < * barType ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " correctly_instantiate_polymorphic_member_functions " )
{
CheckResult result = check ( R " (
local T = { }
function T : foo ( )
return T : bar ( 5 )
end
function T : bar ( i )
return i
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
dumpErrors ( result ) ;
2023-01-05 04:53:17 +08:00
const TableType * t = get < TableType > ( requireType ( " T " ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( t ! = nullptr ) ;
std : : optional < Property > fooProp = get ( t - > props , " foo " ) ;
REQUIRE ( bool ( fooProp ) ) ;
2023-04-29 03:55:13 +08:00
const FunctionType * foo = get < FunctionType > ( follow ( fooProp - > type ( ) ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( bool ( foo ) ) ;
2022-06-17 09:05:14 +08:00
std : : optional < TypeId > ret_ = first ( foo - > retTypes ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( bool ( ret_ ) ) ;
TypeId ret = follow ( * ret_ ) ;
2023-01-05 04:53:17 +08:00
REQUIRE_EQ ( getPrimitiveType ( ret ) , PrimitiveType : : Number ) ;
2022-03-18 08:46:04 +08:00
}
/*
* We had a bug in instantiation where the argument types of ' f ' and ' g ' would be inferred as
* f { + method : function ( < CYCLE > ) : ( t2 , T3 . . . ) + }
* g { + method : function ( { + method : function ( < CYCLE > ) : ( t2 , T3 . . . ) + } ) : ( t5 , T6 . . . ) + }
*
* The type of ' g ' is totally wrong as t2 and t5 should be unified , as should T3 with T6 .
*
* The correct unification of the argument to ' g ' is
*
* { + method : function ( < CYCLE > ) : ( t5 , T6 . . . ) + }
*/
TEST_CASE_FIXTURE ( Fixture , " instantiate_cyclic_generic_function " )
{
auto result = check ( R " (
function f ( o )
o : method ( )
end
function g ( o )
f ( o )
end
) " );
TypeId g = requireType ( " g " ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * gFun = get < FunctionType > ( g ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( gFun ! = nullptr ) ;
auto optionArg = first ( gFun - > argTypes ) ;
REQUIRE ( bool ( optionArg ) ) ;
TypeId arg = follow ( * optionArg ) ;
2023-01-05 04:53:17 +08:00
const TableType * argTable = get < TableType > ( arg ) ;
2024-02-10 01:51:12 +08:00
REQUIRE_MESSAGE ( argTable ! = nullptr , " Expected table but got " < < toString ( arg ) ) ;
2022-03-18 08:46:04 +08:00
std : : optional < Property > methodProp = get ( argTable - > props , " method " ) ;
REQUIRE ( bool ( methodProp ) ) ;
2023-08-05 03:18:54 +08:00
const FunctionType * methodFunction = get < FunctionType > ( follow ( methodProp - > type ( ) ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( methodFunction ! = nullptr ) ;
std : : optional < TypeId > methodArg = first ( methodFunction - > argTypes ) ;
REQUIRE ( bool ( methodArg ) ) ;
REQUIRE_EQ ( follow ( * methodArg ) , follow ( arg ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " instantiate_generic_function_in_assignments " )
{
CheckResult result = check ( R " (
function foo ( a , b )
return a ( b )
end
function bar ( )
local c : ( ( number ) - > number , number ) - > number = foo - - no error
c = foo - - no error
local d : ( ( number ) - > number , string ) - > number = foo - - error from arg 2 ( string ) not being convertable to number from the call a ( b )
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
TypeMismatch * tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
CHECK_EQ ( " ((number) -> number, string) -> number " , toString ( tm - > wantedType ) ) ;
2022-10-07 08:23:29 +08:00
if ( FFlag : : LuauInstantiateInSubtyping )
CHECK_EQ ( " <a, b...>((a) -> (b...), a) -> (b...) " , toString ( tm - > givenType ) ) ;
else
CHECK_EQ ( " ((number) -> number, number) -> number " , toString ( tm - > givenType ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " instantiate_generic_function_in_assignments2 " )
{
CheckResult result = check ( R " (
function foo ( a , b )
return a ( b )
end
function bar ( )
local _ : ( string , string ) - > number = foo - - string cannot be converted to ( string ) - > number
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
TypeMismatch * tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
CHECK_EQ ( " (string, string) -> number " , toString ( tm - > wantedType ) ) ;
2022-10-07 08:23:29 +08:00
if ( FFlag : : LuauInstantiateInSubtyping )
CHECK_EQ ( " <a, b...>((a) -> (b...), a) -> (b...) " , toString ( tm - > givenType ) ) ;
else
CHECK_EQ ( " ((string) -> number, string) -> number " , toString ( * tm - > givenType ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " self_recursive_instantiated_param " )
{
// Mutability in type function application right now can create strange recursive types
CheckResult result = check ( R " (
type Table = { a : number }
type Self < T > = T
local a : Self < Table >
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2024-07-27 01:47:49 +08:00
if ( FFlag : : DebugLuauDeferredConstraintResolution )
CHECK_EQ ( toString ( requireType ( " a " ) ) , " Table<Table> " ) ;
else
CHECK_EQ ( toString ( requireType ( " a " ) ) , " Table " ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " no_stack_overflow_from_quantifying " )
{
CheckResult result = check ( R " (
function _ ( l0 : t0 ) : ( any , ( ) - > ( ) )
end
type t0 = t0 | { }
) " );
2022-05-27 06:08:16 +08:00
LUAU_REQUIRE_ERRORS ( result ) ;
2022-03-18 08:46:04 +08:00
2023-01-07 05:14:35 +08:00
std : : optional < TypeId > t0 = lookupType ( " t0 " ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( t0 ) ;
2024-07-27 01:47:49 +08:00
if ( FFlag : : DebugLuauDeferredConstraintResolution )
CHECK_EQ ( " any " , toString ( * t0 ) ) ;
else
CHECK_EQ ( " *error-type* " , toString ( * t0 ) ) ;
2022-03-18 08:46:04 +08:00
2024-08-02 22:30:04 +08:00
auto it = std : : find_if (
result . errors . begin ( ) ,
result . errors . end ( ) ,
[ ] ( TypeError & err )
{
return get < OccursCheckFailed > ( err ) ;
}
) ;
2022-03-18 08:46:04 +08:00
CHECK ( it ! = result . errors . end ( ) ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " infer_generic_function_function_argument " )
2022-03-18 08:46:04 +08:00
{
2024-07-27 01:47:49 +08:00
if ( FFlag : : DebugLuauDeferredConstraintResolution )
{
CheckResult result = check ( R " (
local function sum < a > ( x : a , y : a , f : ( a , a ) - > add < a > )
return f ( x , y )
end
return sum ( 2 , 3 , function < T > ( a : T , b : T ) : add < T > return a + b end )
) " );
2024-08-02 22:30:04 +08:00
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2024-07-27 01:47:49 +08:00
}
else
{
CheckResult result = check ( R " (
local function sum < a > ( x : a , y : a , f : ( a , a ) - > a )
return f ( x , y )
end
return sum ( 2 , 3 , function ( a , b ) return a + b end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2023-02-18 07:41:51 +08:00
}
2022-03-18 08:46:04 +08:00
2023-02-18 07:41:51 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " infer_generic_function_function_argument_2 " )
{
CheckResult result = check ( R " (
2024-07-27 01:47:49 +08:00
local function map < a , b > ( arr : { a } , f : ( a ) - > b ) : { b }
2022-04-15 07:57:43 +08:00
local r = { }
for i , v in ipairs ( arr ) do
table . insert ( r , f ( v ) )
end
return r
end
local a = { 1 , 2 , 3 }
2024-07-27 01:47:49 +08:00
local r = map ( a , function ( a : number ) return a + a > 100 end )
2022-03-18 08:46:04 +08:00
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
REQUIRE_EQ ( " {boolean} " , toString ( requireType ( " r " ) ) ) ;
2023-02-18 07:41:51 +08:00
}
2022-03-18 08:46:04 +08:00
2023-02-18 07:41:51 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " infer_generic_function_function_argument_3 " )
{
CheckResult result = check ( R " (
2022-04-15 07:57:43 +08:00
local function foldl < a , b > ( arr : { a } , init : b , f : ( b , a ) - > b )
local r = init
for i , v in ipairs ( arr ) do
r = f ( r , v )
end
return r
end
local a = { 1 , 2 , 3 }
2024-07-27 01:47:49 +08:00
local r = foldl ( a , { s = 0 , c = 0 } , function ( a : { s : number , c : number } , b : number ) return { s = a . s + b , c = a . c + 1 } end )
2022-03-18 08:46:04 +08:00
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2024-07-27 01:47:49 +08:00
if ( FFlag : : DebugLuauDeferredConstraintResolution )
REQUIRE_EQ ( " { c: number, s: number } | { c: number, s: number } " , toString ( requireType ( " r " ) ) ) ;
else
REQUIRE_EQ ( " { c: number, s: number } " , toString ( requireType ( " r " ) ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " infer_generic_function_function_argument_overloaded " )
{
CheckResult result = check ( R " (
2022-04-15 07:57:43 +08:00
local g12 : ( < T > ( T , ( T ) - > T ) - > T ) & ( < T > ( T , T , ( T , T ) - > T ) - > T )
2022-03-18 08:46:04 +08:00
2022-04-15 07:57:43 +08:00
g12 ( 1 , function ( x ) return x + x end )
g12 ( 1 , 2 , function ( x , y ) return x + y end )
2022-03-18 08:46:04 +08:00
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
result = check ( R " (
2022-04-15 07:57:43 +08:00
local g12 : ( < T > ( T , ( T ) - > T ) - > T ) & ( < T > ( T , T , ( T , T ) - > T ) - > T )
2022-03-18 08:46:04 +08:00
2022-04-15 07:57:43 +08:00
g12 ( { x = 1 } , function ( x ) return { x = - x . x } end )
g12 ( { x = 1 } , { x = 2 } , function ( x , y ) return { x = x . x + y . x } end )
2022-03-18 08:46:04 +08:00
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " infer_generic_lib_function_function_argument " )
2022-03-18 08:46:04 +08:00
{
CheckResult result = check ( R " (
local a = { { x = 4 } , { x = 7 } , { x = 1 } }
table . sort ( a , function ( x , y ) return x . x < y . x end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-07-27 01:47:49 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " do_not_infer_generic_functions " )
2022-03-18 08:46:04 +08:00
{
2024-07-27 01:47:49 +08:00
CheckResult result ;
2022-03-18 08:46:04 +08:00
2024-07-27 01:47:49 +08:00
if ( FFlag : : DebugLuauDeferredConstraintResolution )
{
result = check ( R " (
local function sum < a > ( x : a , y : a , f : ( a , a ) - > a ) return f ( x , y ) end
2022-03-18 08:46:04 +08:00
2024-07-27 01:47:49 +08:00
local function sumrec ( f : typeof ( sum ) )
return sum ( 2 , 3 , function < T > ( a : T , b : T ) : add < T > return a + b end )
end
local b = sumrec ( sum ) - - ok
local c = sumrec ( function ( x , y , f ) return f ( x , y ) end ) - - type binders are not inferred
) " );
2024-08-02 22:30:04 +08:00
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2024-07-27 01:47:49 +08:00
}
else
{
result = check ( R " (
local function sum < a > ( x : a , y : a , f : ( a , a ) - > a ) return f ( x , y ) end
local function sumrec ( f : typeof ( sum ) )
return sum ( 2 , 3 , function ( a , b ) return a + b end )
end
local b = sumrec ( sum ) - - ok
local c = sumrec ( function ( x , y , f ) return f ( x , y ) end ) - - type binders are not inferred
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-03-18 08:46:04 +08:00
}
2024-07-27 01:47:49 +08:00
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " substitution_with_bound_table " )
{
CheckResult result = check ( R " (
2022-04-15 07:57:43 +08:00
type A = { x : number }
local a : A = { x = 1 }
local b = a
type B = typeof ( b )
type X < T > = T
local c : X < B >
2022-03-18 08:46:04 +08:00
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-03-12 00:55:02 +08:00
2022-05-20 08:02:24 +08:00
TEST_CASE_FIXTURE ( Fixture , " apply_type_function_nested_generics1 " )
{
2024-07-27 01:47:49 +08:00
// CLI-114507: temporarily changed to have a cast for `object` to silence false positive error
2023-12-16 05:29:06 +08:00
// https://github.com/luau-lang/luau/issues/484
2022-05-20 08:02:24 +08:00
CheckResult result = check ( R " (
- - ! strict
type MyObject = {
getReturnValue : < V > ( cb : ( ) - > V ) - > V
}
local object : MyObject = {
getReturnValue = function < U > ( cb : ( ) - > U ) : U
return cb ( )
end ,
2024-07-27 01:47:49 +08:00
} : : MyObject
2022-05-20 08:02:24 +08:00
type ComplexObject < T > = {
id : T ,
nested : MyObject
}
local complex : ComplexObject < string > = {
id = " Foo " ,
nested = object ,
}
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " apply_type_function_nested_generics2 " )
{
2023-12-16 05:29:06 +08:00
// https://github.com/luau-lang/luau/issues/484
2022-05-20 08:02:24 +08:00
CheckResult result = check ( R " (
- - ! strict
type MyObject = {
getReturnValue : < V > ( cb : ( ) - > V ) - > V
}
type ComplexObject < T > = {
id : T ,
nested : MyObject
}
2023-12-16 05:29:06 +08:00
function f ( complex : ComplexObject < string > )
local x = complex . nested . getReturnValue ( function ( ) : string
return " "
end )
2022-05-20 08:02:24 +08:00
2023-12-16 05:29:06 +08:00
local y = complex . nested . getReturnValue ( function ( )
return 3
end )
end
2022-05-20 08:02:24 +08:00
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2023-09-23 03:12:15 +08:00
TEST_CASE_FIXTURE ( Fixture , " apply_type_function_nested_generics3 " )
{
// This minimization was useful for debugging a particular issue with
// cyclic types under local type inference.
CheckResult result = check ( R " (
local getReturnValue : < V > ( cb : ( ) - > V ) - > V = nil : : any
local y = getReturnValue ( function ( ) return nil : : any end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-20 08:02:24 +08:00
TEST_CASE_FIXTURE ( Fixture , " quantify_functions_even_if_they_have_an_explicit_generic " )
{
CheckResult result = check ( R " (
function foo < X > ( f , x : X )
return f ( x )
end
) " );
CHECK ( " <X, a...>((X) -> (a...), X) -> (a...) " = = toString ( requireType ( " foo " ) ) ) ;
}
2022-07-08 09:22:39 +08:00
TEST_CASE_FIXTURE ( Fixture , " do_not_always_instantiate_generic_intersection_types " )
{
CheckResult result = check ( R " (
- - ! strict
type Array < T > = { [ number ] : T }
type Array_Statics = {
new : < T > ( ) - > Array < T > ,
}
local _Arr : Array < any > & Array_Statics = { } : : Array_Statics
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-10-07 08:23:29 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " hof_subtype_instantiation_regression " )
{
CheckResult result = check ( R " (
- - ! strict
local function defaultSort < T > ( a : T , b : T )
return true
end
type A = any
return function < T > ( array : { T } ) : { T }
table . sort ( array , defaultSort )
return array
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " higher_rank_polymorphism_should_not_accept_instantiated_arguments " )
{
ScopedFastFlag sffs [ ] = {
2023-12-02 15:46:57 +08:00
{ FFlag : : LuauInstantiateInSubtyping , true } ,
2022-10-07 08:23:29 +08:00
} ;
CheckResult result = check ( R " (
- - ! strict
local function instantiate ( f : < a > ( a ) - > a ) : ( number ) - > number
return f
end
instantiate ( function ( x : string ) return " foo " end )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
auto tm1 = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm1 ) ;
CHECK_EQ ( " <a>(a) -> a " , toString ( tm1 - > wantedType ) ) ;
CHECK_EQ ( " <a>(string) -> string " , toString ( tm1 - > givenType ) ) ;
}
2022-12-03 02:09:59 +08:00
TEST_CASE_FIXTURE ( Fixture , " bidirectional_checking_and_generalization_play_nice " )
{
CheckResult result = check ( R " (
local foo = function ( a )
return a ( )
end
local a = foo ( function ( ) return 1 end )
local b = foo ( function ( ) return " bar " end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK ( " number " = = toString ( requireType ( " a " ) ) ) ;
CHECK ( " string " = = toString ( requireType ( " b " ) ) ) ;
}
2024-02-24 04:08:34 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " generalization_no_cyclic_intersections " )
{
CheckResult result = check ( R " (
local f , t , n = pairs ( { " foo " } )
local k , v = f ( t )
) " );
CHECK ( " ({string}, number?) -> (number?, string) " = = toString ( requireType ( " f " ) ) ) ;
CHECK ( " {string} " = = toString ( requireType ( " t " ) ) ) ;
CHECK ( " number? " = = toString ( requireType ( " k " ) ) ) ;
CHECK ( " string " = = toString ( requireType ( " v " ) ) ) ;
}
2024-02-03 05:32:42 +08:00
TEST_CASE_FIXTURE ( Fixture , " missing_generic_type_parameter " )
{
CheckResult result = check ( R " (
function f ( x : T ) : T return x end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
REQUIRE ( get < UnknownSymbol > ( result . errors [ 0 ] ) ) ;
REQUIRE ( get < UnknownSymbol > ( result . errors [ 1 ] ) ) ;
}
2024-07-20 02:20:47 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " generic_type_functions_work_in_subtyping " )
2024-02-24 04:08:34 +08:00
{
if ( ! FFlag : : DebugLuauDeferredConstraintResolution )
return ;
CheckResult result = check ( R " (
local function addOne < T > ( x : T ) : add < T , number > return x + 1 end
local function six ( ) : number
return addOne ( 5 )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2021-10-30 04:25:12 +08:00
TEST_SUITE_END ( ) ;