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"
# include "Luau/TypeVar.h"
# include "Fixture.h"
# include "doctest.h"
using namespace Luau ;
2022-04-15 07:57:43 +08:00
LUAU_FASTFLAG ( LuauLowerBoundsCalculation ) ;
2021-10-30 04:25:12 +08:00
TEST_SUITE_BEGIN ( " IntersectionTypes " ) ;
TEST_CASE_FIXTURE ( Fixture , " select_correct_union_fn " )
{
CheckResult result = check ( R " (
type A = ( number ) - > ( string )
type B = ( string ) - > ( number )
local f : A & B
local b = f ( 10 ) - - b is a string
local c = f ( " a " ) - - c is a number
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( requireType ( " b " ) , typeChecker . stringType ) ;
CHECK_EQ ( requireType ( " c " ) , typeChecker . numberType ) ;
}
TEST_CASE_FIXTURE ( Fixture , " table_combines " )
{
CheckResult result = check ( R " (
type A = { a : number }
type B = { b : string }
local c : A & B = { a = 10 , b = " s " }
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " table_combines_missing " )
{
CheckResult result = check ( R " (
type A = { a : number }
type B = { b : string }
local c : A & B = { a = 10 }
) " );
REQUIRE ( result . errors . size ( ) = = 1 ) ;
}
TEST_CASE_FIXTURE ( Fixture , " impossible_type " )
{
CheckResult result = check ( R " (
local c : number & string = 10
) " );
REQUIRE ( result . errors . size ( ) = = 1 ) ;
}
TEST_CASE_FIXTURE ( Fixture , " table_extra_ok " )
{
CheckResult result = check ( R " (
type A = { a : number }
type B = { b : string }
local c : A & B
local d : A = c
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " fx_intersection_as_argument " )
{
CheckResult result = check ( R " (
type A = ( number ) - > ( string )
type B = ( string ) - > ( number )
type C = ( A ) - > ( number )
local f : A & B
local g : C
local b = g ( f )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " fx_union_as_argument_fails " )
{
CheckResult result = check ( R " (
type A = ( number ) - > ( string )
type B = ( string ) - > ( number )
type C = ( A ) - > ( number )
local f : A | B
local g : C
local b = g ( f )
) " );
REQUIRE ( ! result . errors . empty ( ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " argument_is_intersection " )
{
CheckResult result = check ( R " (
type A = ( number | boolean ) - > number
local f : A
f ( 5 )
f ( true )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " should_still_pick_an_overload_whose_arguments_are_unions " )
{
CheckResult result = check ( R " (
type A = ( number | boolean ) - > number
type B = ( string | nil ) - > string
local f : A & B
local a1 , a2 = f ( 1 ) , f ( true )
local b1 , b2 = f ( " foo " ) , f ( nil )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( * requireType ( " a1 " ) , * typeChecker . numberType ) ;
CHECK_EQ ( * requireType ( " a2 " ) , * typeChecker . numberType ) ;
CHECK_EQ ( * requireType ( " b1 " ) , * typeChecker . stringType ) ;
CHECK_EQ ( * requireType ( " b2 " ) , * typeChecker . stringType ) ;
}
TEST_CASE_FIXTURE ( Fixture , " propagates_name " )
{
const std : : string code = R " (
type A = { a : number }
type B = { b : string }
local c : A & B
local b = c
) " ;
const std : : string expected = R " (
type A = { a : number }
type B = { b : string }
local c : A & B
local b : A & B = c
) " ;
CHECK_EQ ( expected , decorateWithTypes ( code ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " index_on_an_intersection_type_with_property_guaranteed_to_exist " )
{
CheckResult result = check ( R " (
type A = { x : { y : number } }
type B = { x : { y : number } }
local t : A & B
local r = t . x
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
const IntersectionTypeVar * r = get < IntersectionTypeVar > ( requireType ( " r " ) ) ;
REQUIRE ( r ) ;
TableTypeVar * a = getMutable < TableTypeVar > ( r - > parts [ 0 ] ) ;
REQUIRE ( a ) ;
CHECK_EQ ( typeChecker . numberType , a - > props [ " y " ] . type ) ;
TableTypeVar * b = getMutable < TableTypeVar > ( r - > parts [ 1 ] ) ;
REQUIRE ( b ) ;
CHECK_EQ ( typeChecker . numberType , b - > props [ " y " ] . type ) ;
}
TEST_CASE_FIXTURE ( Fixture , " index_on_an_intersection_type_works_at_arbitrary_depth " )
{
CheckResult result = check ( R " (
type A = { x : { y : { z : { thing : string } } } }
type B = { x : { y : { z : { thing : string } } } }
local t : A & B
local r = t . x . y . z . thing
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2022-02-25 07:53:37 +08:00
CHECK_EQ ( " string & string " , toString ( requireType ( " r " ) ) ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " index_on_an_intersection_type_with_mixed_types " )
{
CheckResult result = check ( R " (
type A = { x : number }
type B = { x : string }
local t : A & B
local r = t . x
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( " number & string " , toString ( requireType ( " r " ) ) ) ; // TODO(amccord): This should be an error.
}
TEST_CASE_FIXTURE ( Fixture , " index_on_an_intersection_type_with_one_part_missing_the_property " )
{
CheckResult result = check ( R " (
type A = { x : number }
type B = { }
local t : A & B
local r = t . x
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( " number " , toString ( requireType ( " r " ) ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " index_on_an_intersection_type_with_one_property_of_type_any " )
{
CheckResult result = check ( R " (
2022-02-25 07:53:37 +08:00
type A = { y : number }
2021-10-30 04:25:12 +08:00
type B = { x : any }
local t : A & B
local r = t . x
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( * typeChecker . anyType , * requireType ( " r " ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " index_on_an_intersection_type_with_all_parts_missing_the_property " )
{
CheckResult result = check ( R " (
type A = { }
type B = { }
local function f ( t : A & B )
local x = t . x
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
UnknownProperty * up = get < UnknownProperty > ( result . errors [ 0 ] ) ;
REQUIRE_MESSAGE ( up , result . errors [ 0 ] . data ) ;
CHECK_EQ ( up - > key , " x " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " table_intersection_write " )
{
CheckResult result = check ( R " (
type X = { x : number }
type XY = X & { y : number }
local a : XY = { x = 1 , y = 2 }
a . x = 10
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
result = check ( R " (
type X = { }
type XY = X & { x : number , y : number }
local a : XY = { x = 1 , y = 2 }
a . x = 10
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
result = check ( R " (
type X = { x : number }
type Y = { y : number }
type XY = X & Y
local a : XY = { x = 1 , y = 2 }
a . x = 10
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
result = check ( R " (
type A = { x : { y : number } }
type B = { x : { y : number } }
local t : A & B = { x = { y = 1 } }
t . x = { y = 4 }
t . x . y = 40
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " table_intersection_write_sealed " )
{
CheckResult result = check ( R " (
type X = { x : number }
type Y = { y : number }
type XY = X & Y
local a : XY = { x = 1 , y = 2 }
a . z = 10
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2022-04-15 07:57:43 +08:00
if ( FFlag : : LuauLowerBoundsCalculation )
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Cannot add property 'z' to table '{| x: number, y: number |}' " ) ;
else
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Cannot add property 'z' to table 'X & Y' " ) ;
2021-10-30 04:25:12 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " table_intersection_write_sealed_indirect " )
{
CheckResult result = check ( R " (
2022-04-15 07:57:43 +08:00
type X = { x : ( number ) - > number }
type Y = { y : ( string ) - > string }
2021-10-30 04:25:12 +08:00
2022-04-15 07:57:43 +08:00
type XY = X & Y
2021-10-30 04:25:12 +08:00
2022-04-15 07:57:43 +08:00
local xy : XY = {
x = function ( a : number ) return - a end ,
y = function ( a : string ) return a . . " b " end
}
function xy . z ( a : number ) return a * 10 end
function xy : y ( a : number ) return a * 10 end
function xy : w ( a : number ) return a * 10 end
2021-10-30 04:25:12 +08:00
) " );
2022-04-08 05:29:01 +08:00
LUAU_REQUIRE_ERROR_COUNT ( 4 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Type '(string, number) -> string' could not be converted into '(string) -> string'
caused by :
Argument count mismatch . Function expects 2 arguments , but only 1 is specified ) " );
2022-04-15 07:57:43 +08:00
if ( FFlag : : LuauLowerBoundsCalculation )
CHECK_EQ ( toString ( result . errors [ 1 ] ) , " Cannot add property 'z' to table '{| x: (number) -> number, y: (string) -> string |}' " ) ;
else
CHECK_EQ ( toString ( result . errors [ 1 ] ) , " Cannot add property 'z' to table 'X & Y' " ) ;
2022-04-08 05:29:01 +08:00
CHECK_EQ ( toString ( result . errors [ 2 ] ) , " Type 'number' could not be converted into 'string' " ) ;
2022-04-15 07:57:43 +08:00
if ( FFlag : : LuauLowerBoundsCalculation )
CHECK_EQ ( toString ( result . errors [ 3 ] ) , " Cannot add property 'w' to table '{| x: (number) -> number, y: (string) -> string |}' " ) ;
else
CHECK_EQ ( toString ( result . errors [ 3 ] ) , " Cannot add property 'w' to table 'X & Y' " ) ;
2022-04-08 05:29:01 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " table_write_sealed_indirect " )
{
// After normalization, previous 'table_intersection_write_sealed_indirect' is identical to this one
CheckResult result = check ( R " (
type XY = { x : ( number ) - > number , y : ( string ) - > string }
local xy : XY = {
x = function ( a : number ) return - a end ,
y = function ( a : string ) return a . . " b " end
}
function xy . z ( a : number ) return a * 10 end
function xy : y ( a : number ) return a * 10 end
function xy : w ( a : number ) return a * 10 end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 4 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Type '(string, number) -> string' could not be converted into '(string) -> string'
caused by :
Argument count mismatch . Function expects 2 arguments , but only 1 is specified ) " );
CHECK_EQ ( toString ( result . errors [ 1 ] ) , " Cannot add property 'z' to table 'XY' " ) ;
CHECK_EQ ( toString ( result . errors [ 2 ] ) , " Type 'number' could not be converted into 'string' " ) ;
CHECK_EQ ( toString ( result . errors [ 3 ] ) , " Cannot add property 'w' to table 'XY' " ) ;
2021-10-30 04:25:12 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " table_intersection_setmetatable " )
2021-10-30 04:25:12 +08:00
{
CheckResult result = check ( R " (
local t : { } & { }
setmetatable ( t , { } )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2021-11-12 22:27:34 +08:00
TEST_CASE_FIXTURE ( Fixture , " error_detailed_intersection_part " )
{
2022-04-15 07:57:43 +08:00
ScopedFastFlag flags [ ] = { { " LuauLowerBoundsCalculation " , false } } ;
2021-11-12 22:27:34 +08:00
CheckResult result = check ( R " (
type X = { x : number }
type Y = { y : number }
type Z = { z : number }
type XYZ = X & Y & Z
local a : XYZ = 3
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Type 'number' could not be converted into 'X & Y & Z'
caused by :
Not all intersection parts are compatible . Type ' number ' could not be converted into ' X ' ) " );
}
TEST_CASE_FIXTURE ( Fixture , " error_detailed_intersection_all " )
{
2022-04-15 07:57:43 +08:00
ScopedFastFlag flags [ ] = { { " LuauLowerBoundsCalculation " , false } } ;
2021-11-12 22:27:34 +08:00
CheckResult result = check ( R " (
type X = { x : number }
type Y = { y : number }
type Z = { z : number }
type XYZ = X & Y & Z
local a : XYZ
local b : number = a
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Type 'X & Y & Z' could not be converted into 'number'; none of the intersection parts are compatible) " ) ;
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " overload_is_not_a_function " )
{
check ( R " (
- - ! nonstrict
function _ ( . . . ) : ( ( typeof ( not _ ) ) & ( typeof ( not _ ) ) ) & ( ( typeof ( not _ ) ) & ( typeof ( not _ ) ) )
_ ( . . . ) ( setfenv , _ , not _ , " " ) [ _ ] = nil
end
do end
_ ( . . . ) ( . . . , setfenv , _ ) : _G ( )
) " );
}
TEST_CASE_FIXTURE ( Fixture , " no_stack_overflow_from_flattenintersection " )
{
CheckResult result = check ( R " (
local l0 , l0
repeat
type t0 = ( ( any ) | ( ( any ) & ( ( any ) | ( ( any ) & ( ( any ) | ( any ) ) ) ) ) ) & ( t0 )
function _ ( l0 ) : ( t0 ) & ( t0 )
2022-04-15 07:57:43 +08:00
while nil do
end
2022-03-18 08:46:04 +08:00
end
until _ ( _ ) ( _ ) . _
) " );
2022-05-27 06:08:16 +08:00
LUAU_REQUIRE_ERRORS ( result ) ;
2022-03-18 08:46:04 +08:00
}
2022-10-07 08:23:29 +08:00
TEST_CASE_FIXTURE ( Fixture , " intersect_bool_and_false " )
{
CheckResult result = check ( R " (
local x : ( boolean & false )
local y : false = x - - OK
local z : true = x - - Not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type 'boolean & false' could not be converted into 'true'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " intersect_false_and_bool_and_false " )
{
CheckResult result = check ( R " (
local x : false & ( boolean & false )
local y : false = x - - OK
local z : true = x - - Not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
// TODO: odd stringification of `false & (boolean & false)`.)
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type 'boolean & false & false' could not be converted into 'true'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " intersect_saturate_overloaded_functions " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local x : ( ( number ? ) - > number ? ) & ( ( string ? ) - > string ? )
local y : ( nil ) - > nil = x - - OK
local z : ( number ) - > number = x - - Not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((number?) -> number?) & ((string?) -> string?)' could not be converted into '(number) -> number'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " union_saturate_overloaded_functions " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local x : ( ( number ) - > number ) & ( ( string ) - > string )
local y : ( ( number | string ) - > ( number | string ) ) = x - - OK
local z : ( ( number | boolean ) - > ( number | boolean ) ) = x - - Not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((number) -> number) & ((string) -> string)' could not be converted into '(boolean | number) -> boolean | number'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " intersection_of_tables " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local x : { p : number ? , q : string ? } & { p : number ? , q : number ? , r : number ? }
local y : { p : number ? , q : nil , r : number ? } = x - - OK
local z : { p : nil } = x - - Not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '{| p: number?, q: number?, r: number? |} & {| p: number?, q: string? |}' could not be converted into '{| p: nil |}'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " intersection_of_tables_with_top_properties " )
{
CheckResult result = check ( R " (
local x : { p : number ? , q : any } & { p : unknown , q : string ? }
local y : { p : number ? , q : string ? } = x - - OK
local z : { p : string ? , q : number ? } = x - - Not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '{| p: number?, q: any |} & {| p: unknown, q: string? |}' could not be converted into '{| p: string?, q: number? |}'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " intersection_of_tables_with_never_properties " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local x : { p : number ? , q : never } & { p : never , q : string ? }
local y : { p : never , q : never } = x - - OK
local z : never = x - - OK
) " );
// TODO: this should not produce type errors, since never <: { p : never }
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '{| p: never, q: string? |} & {| p: number?, q: never |}' could not be converted into 'never'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloaded_functions_returning_intersections " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local x : ( ( number ? ) - > ( { p : number } & { q : number } ) ) & ( ( string ? ) - > ( { p : number } & { r : number } ) )
local y : ( nil ) - > { p : number , q : number , r : number } = x - - OK
local z : ( number ? ) - > { p : number , q : number , r : number } = x - - Not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((number?) -> {| p: number |} & {| q: number |}) & ((string?) -> {| p: number |} & {| r: number |})' could not be converted into '(number?) -> {| p: number, q: number, r: number |}'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloaded_functions_mentioning_generic " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
function f < a > ( )
local x : ( ( number ? ) - > ( a | number ) ) & ( ( string ? ) - > ( a | string ) )
local y : ( nil ) - > a = x - - OK
local z : ( number ? ) - > a = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((number?) -> a | number) & ((string?) -> a | string)' could not be converted into '(number?) -> a'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloaded_functions_mentioning_generics " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
function f < a , b , c > ( )
local x : ( ( a ? ) - > ( a | b ) ) & ( ( c ? ) - > ( b | c ) )
local y : ( nil ) - > ( ( a & c ) | b ) = x - - OK
local z : ( a ? ) - > ( ( a & c ) | b ) = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((a?) -> a | b) & ((c?) -> b | c)' could not be converted into '(a?) -> (a & c) | b'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloaded_functions_mentioning_generic_packs " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
function f < a . . . , b . . . > ( )
local x : ( ( number ? , a . . . ) - > ( number ? , b . . . ) ) & ( ( string ? , a . . . ) - > ( string ? , b . . . ) )
local y : ( ( nil , a . . . ) - > ( nil , b . . . ) ) = x - - OK
local z : ( ( nil , b . . . ) - > ( nil , a . . . ) ) = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))' could not be converted into '(nil, b...) -> (nil, a...)'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_unknown_result " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
function f < a . . . , b . . . > ( )
local x : ( ( number ) - > number ) & ( ( nil ) - > unknown )
local y : ( number ? ) - > unknown = x - - OK
local z : ( number ? ) - > number ? = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((nil) -> unknown) & ((number) -> number)' could not be converted into '(number?) -> number?'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_unknown_arguments " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
function f < a . . . , b . . . > ( )
local x : ( ( number ) - > number ? ) & ( ( unknown ) - > string ? )
local y : ( number ) - > nil = x - - OK
local z : ( number ? ) - > nil = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((number) -> number?) & ((unknown) -> string?)' could not be converted into '(number?) -> nil'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_never_result " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
function f < a . . . , b . . . > ( )
local x : ( ( number ) - > number ) & ( ( nil ) - > never )
local y : ( number ? ) - > number = x - - OK
local z : ( number ? ) - > never = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((nil) -> never) & ((number) -> number)' could not be converted into '(number?) -> never'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_never_arguments " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
function f < a . . . , b . . . > ( )
local x : ( ( number ) - > number ? ) & ( ( never ) - > string ? )
local y : ( never ) - > nil = x - - OK
local z : ( number ? ) - > nil = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((never) -> string?) & ((number) -> number?)' could not be converted into '(number?) -> nil'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_overlapping_results_and_variadics " )
{
CheckResult result = check ( R " (
local x : ( ( string ? ) - > ( string | number ) ) & ( ( number ? ) - > . . . number )
local y : ( ( nil ) - > ( number , number ? ) ) = x - - OK
local z : ( ( string | number ) - > ( number , number ? ) ) = x - - Not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((number?) -> (...number)) & ((string?) -> number | string)' could not be converted into '(number | string) -> (number, number?)'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_weird_typepacks_1 " )
{
CheckResult result = check ( R " (
function f < a . . . , b . . . > ( )
local x : ( ( ) - > a . . . ) & ( ( ) - > b . . . )
local y : ( ( ) - > b . . . ) & ( ( ) - > a . . . ) = x - - OK
local z : ( ) - > ( ) = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '(() -> (a...)) & (() -> (b...))' could not be converted into '() -> ()'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_weird_typepacks_2 " )
{
CheckResult result = check ( R " (
function f < a . . . , b . . . > ( )
local x : ( ( a . . . ) - > ( ) ) & ( ( b . . . ) - > ( ) )
local y : ( ( b . . . ) - > ( ) ) & ( ( a . . . ) - > ( ) ) = x - - OK
local z : ( ) - > ( ) = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((a...) -> ()) & ((b...) -> ())' could not be converted into '() -> ()'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_weird_typepacks_3 " )
{
CheckResult result = check ( R " (
function f < a . . . > ( )
local x : ( ( ) - > a . . . ) & ( ( ) - > ( number ? , a . . . ) )
local y : ( ( ) - > ( number ? , a . . . ) ) & ( ( ) - > a . . . ) = x - - OK
local z : ( ) - > ( number ) = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '(() -> (a...)) & (() -> (number?, a...))' could not be converted into '() -> number'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " overloadeded_functions_with_weird_typepacks_4 " )
{
CheckResult result = check ( R " (
function f < a . . . > ( )
local x : ( ( a . . . ) - > ( ) ) & ( ( number , a . . . ) - > number )
local y : ( ( number , a . . . ) - > number ) & ( ( a . . . ) - > ( ) ) = x - - OK
local z : ( number ? ) - > ( ) = x - - Not OK
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type '((a...) -> ()) & ((number, a...) -> number)' could not be converted into '(number?) -> ()'; none of the intersection parts are compatible " ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " intersect_metatables " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local a : string ? = nil
local b : number ? = nil
local x = setmetatable ( { } , { p = 5 , q = a } ) ;
local y = setmetatable ( { } , { q = b , r = " hi " } ) ;
local z = setmetatable ( { } , { p = 5 , q = nil , r = " hi " } ) ;
type X = typeof ( x )
type Y = typeof ( y )
type Z = typeof ( z )
local xy : X & Y = z ;
local yx : Y & X = z ;
z = xy ;
z = yx ;
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " intersect_metatable_subtypes " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local x = setmetatable ( { a = 5 } , { p = 5 } ) ;
local y = setmetatable ( { b = " hi " } , { p = 5 , q = " hi " } ) ;
local z = setmetatable ( { a = 5 , b = " hi " } , { p = 5 , q = " hi " } ) ;
type X = typeof ( x )
type Y = typeof ( y )
type Z = typeof ( z )
local xy : X & Y = z ;
local yx : Y & X = z ;
z = xy ;
z = yx ;
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " intersect_metatables_with_properties " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local x = setmetatable ( { a = 5 } , { p = 5 } ) ;
local y = setmetatable ( { b = " hi " } , { q = " hi " } ) ;
local z = setmetatable ( { a = 5 , b = " hi " } , { p = 5 , q = " hi " } ) ;
type X = typeof ( x )
type Y = typeof ( y )
type Z = typeof ( z )
local xy : X & Y = z ;
z = xy ;
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " intersect_metatable_with table " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
local x = setmetatable ( { a = 5 } , { p = 5 } ) ;
local z = setmetatable ( { a = 5 , b = " hi " } , { p = 5 } ) ;
type X = typeof ( x )
type Y = { b : string }
type Z = typeof ( z )
- - TODO : once we have shape types , we should be able to initialize these with z
local xy : X & Y ;
local yx : Y & X ;
z = xy ;
z = yx ;
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " CLI-44817 " )
{
ScopedFastFlag sffs [ ] {
{ " LuauSubtypeNormalizer " , true } ,
{ " LuauTypeNormalization2 " , true } ,
} ;
CheckResult result = check ( R " (
type X = { x : number }
type Y = { y : number }
type Z = { z : number }
type XY = { x : number , y : number }
type XYZ = { x : number , y : number , z : number }
local xy : XY = { x = 0 , y = 0 }
local xyz : XYZ = { x = 0 , y = 0 , z = 0 }
local xNy : X & Y = xy
local xNyNz : X & Y & Z = xyz
local t1 : XY = xNy - - Type ' X & Y ' could not be converted into ' XY '
local t2 : XY = xNyNz - - Type ' X & Y & Z ' could not be converted into ' XY '
local t3 : XYZ = xNyNz - - Type ' X & Y & Z ' could not be converted into ' XYZ '
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2021-10-30 04:25:12 +08:00
TEST_SUITE_END ( ) ;