luau/tests/TypeInfer.typestates.test.cpp

524 lines
13 KiB
C++
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Fixture.h"
#include "doctest.h"
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
using namespace Luau;
namespace
{
struct TypeStateFixture : BuiltinsFixture
{
Sync to upstream/release/605 (#1118) - Implemented [Require by String with Relative Paths](https://github.com/luau-lang/rfcs/blob/master/docs/new-require-by-string-semantics.md) RFC - Implemented [Require by String with Aliases](https://github.com/luau-lang/rfcs/blob/master/docs/require-by-string-aliases.md) RFC with support for `paths` and `alias` arrays in .luarc - Added SUBRK and DIVRK bytecode instructions to speed up constant-number and constant/number operations - Added `--vector-lib`, `--vector-ctor` and `--vector-type` options to luau-compile to support code with vectors New Solver - Correctness fixes to subtyping - Improvements to dataflow analysis Native Code Generation - Added bytecode analysis pass to predict type tags used in operations - Fixed rare cases of numerical loops being generated without an interrupt instruction - Restored optimization data propagation into the linear block - Duplicate buffer length checks are optimized away Miscellaneous - Small performance improvements to new non-strict mode - Introduced more scripts for fuzzing Luau and processing the results, including fuzzer build support for CMake Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-12-02 07:46:57 +00:00
ScopedFastFlag dcr{FFlag::DebugLuauDeferredConstraintResolution, true};
};
} // namespace
TEST_SUITE_BEGIN("TypeStatesTest");
TEST_CASE_FIXTURE(TypeStateFixture, "initialize_x_of_type_string_or_nil_with_nil")
{
CheckResult result = check(R"(
local x: string? = nil
local a = x
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("string?" == toString(requireType("a")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "extraneous_lvalues_are_populated_with_nil")
{
CheckResult result = check(R"(
local function f(): (string, number)
return "hello", 5
end
local x, y, z = f()
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("Function only returns 2 values, but 3 are required here" == toString(result.errors[0]));
CHECK("string" == toString(requireType("x")));
CHECK("number" == toString(requireType("y")));
CHECK("nil" == toString(requireType("z")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "assign_different_values_to_x")
{
CheckResult result = check(R"(
local x: string? = nil
local a = x
x = "hello!"
local b = x
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("string?" == toString(requireType("a")));
CHECK("string" == toString(requireType("b")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "parameter_x_was_constrained_by_two_types")
{
// Parameter `x` has a fresh type `'x` bounded by `never` and `unknown`.
// The first use of `x` constrains `x`'s upper bound by `string | number`.
// The second use of `x`, aliased by `y`, constrains `x`'s upper bound by `string?`.
// This results in `'x <: (string | number) & (string?)`.
// The principal type of the upper bound is `string`.
CheckResult result = check(R"(
local function f(x): string?
local y: string | number = x
return y
end
)");
if (FFlag::DebugLuauDeferredConstraintResolution)
{
// `y` is annotated `string | number` which is explicitly not compatible with `string?`
// as such, we produce an error here for that mismatch.
//
// this is not necessarily the best inference here, since we can indeed produce `string`
// as a type for `x`, but it's a limitation we can accept for now.
LUAU_REQUIRE_ERRORS(result);
TypePackMismatch* tpm = get<TypePackMismatch>(result.errors[0]);
REQUIRE(tpm);
CHECK("string?" == toString(tpm->wantedTp));
CHECK("number | string" == toString(tpm->givenTp));
CHECK("(number | string) -> string?" == toString(requireType("f")));
}
else
{
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("(string) -> string?" == toString(requireType("f")));
}
}
#if 0
TEST_CASE_FIXTURE(TypeStateFixture, "local_that_will_be_assigned_later")
{
CheckResult result = check(R"(
local x: string
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
}
TEST_CASE_FIXTURE(TypeStateFixture, "refine_a_local_and_then_assign_it")
{
CheckResult result = check(R"(
local function f(x: string?)
if typeof(x) == "string" then
x = nil
end
local y: nil = x
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(TypeStateFixture, "assign_a_local_and_then_refine_it")
{
CheckResult result = check(R"(
local function f(x: string?)
x = nil
if typeof(x) == "string" then
local y: typeof(x) = "hello"
end
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("Type 'string' could not be converted into 'never'" == toString(result.errors[0]));
}
Sync to upstream/release/603 (#1097) # What's changed? - Record the location of properties for table types (closes #802) - Implement stricter UTF-8 validations as per the RFC (https://github.com/luau-lang/rfcs/pull/1) - Implement `buffer` as a new type in both the old and new solvers. - Changed errors produced by some `buffer` builtins to be a bit more generic to avoid platform-dependent error messages. - Fixed a bug where `Unifier` would copy some persistent types, tripping some internal assertions. - Type checking rules on relational operators is now a little bit more lax. - Improve dead code elimination for some `if` statements with complex always-false conditions ## New type solver - Dataflow analysis now generates phi nodes on exit of branches. - Dataflow analysis avoids producing a new definition for locals or properties that are not owned by that loop. - If a function parameter has been constrained to `never`, report errors at all uses of that parameter within that function. - Switch to using the new `Luau::Set` to replace `std::unordered_set` to alleviate some poor allocation characteristics which was negatively affecting overall performance. - Subtyping can now report many failing reasons instead of just the first one that we happened to find during the test. - Subtyping now also report reasons for type pack mismatches. - When visiting `if` statements or expressions, the resulting context are the common terms in both branches. ## Native codegen - Implement support for `buffer` builtins to its IR for x64 and A64. - Optimized `table.insert` by not inserting a table barrier if it is fastcalled with a constant. ## Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Arseny Kapoulkine <arseny@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-11-10 21:10:07 +00:00
#endif
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
TEST_CASE_FIXTURE(TypeStateFixture, "recursive_local_function")
{
CheckResult result = check(R"(
local function f(x)
f(5)
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(TypeStateFixture, "recursive_function")
{
CheckResult result = check(R"(
function f(x)
f(5)
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(TypeStateFixture, "compound_assignment")
{
CheckResult result = check(R"(
local x = 5
x += 7
local a = x
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(TypeStateFixture, "assignment_identity")
{
CheckResult result = check(R"(
local x = 5
x = x
local a = x
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("number" == toString(requireType("a")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "assignment_swap")
{
CheckResult result = check(R"(
local x, y = 5, "hello"
x, y = y, x
local a, b = x, y
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("string" == toString(requireType("a")));
CHECK("number" == toString(requireType("b")));
}
Sync to upstream/release/622 (#1232) # What's changed? * Improved the actual message for the type errors for `cannot call non-function` when attempting to call a union of functions/callable tables. The error now correctly explains the issue is an inability to determine the return type of the call in this situation. * Resolve an issue where tables and metatables were not correctly being cloned during instantiation (fixes #1176). * Refactor `luaM_getnextgcopage` to `luaM_getnextpage` (generally removing `gco` prefix where appropriate). * Optimize `table.move` between tables for large ranges of elements. * Reformat a bunch of code automatically using `clang-format`. ### New Type Solver * Clean up minimally-used or unused constraints in the constraint solver (`InstantiationConstraint`, `SetOpConstraint`, `SingletonOrTopTypeConstraint`). * Add a builtin `singleton` type family to replace `SingletonOrTopTypeConstraint` when inferring refinements. * Fixed a crash involving type path reasoning by recording when type family reduction has taken place in the path. * Improved constraint ordering by blocking on unreduced types families that are not yet proven uninhabitable. * Improved the handling of `SetIndexerConstraints` for both better inference quality and to resolve crashes. * Fix a crash when normalizing cyclic unions of intersections. * Fix a crash when normalizing an intersection with the negation of `unknown`. * Fix a number of crashes caused by missing `follow` calls on `TypeId`s. * Changed type family reduction to correctly use a semantic notion of uninhabited types, rather than checking for `never` types specifically. * Refactor the `union` and `intersect` type families to be variadic. ### Native Code Generation * Improve translation for userdata key get/set and userdata/vector namecall. * Provide `[top level]` and `[anonymous]` as function names to `FunctionStats` as appropriate when no function name is available. * Disable unwind support on Android platforms since it is unsupported. * --- ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-04-19 22:48:02 +01:00
TEST_CASE_FIXTURE(TypeStateFixture, "parameter_x_was_constrained_by_two_types_2")
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
{
CheckResult result = check(R"(
local function f(x): number?
local y: string? = nil -- 'y <: string?
y = x -- 'y ~ 'x
return y -- 'y <: number?
-- We therefore infer 'y <: (string | nil) & (number | nil)
-- or 'y <: nil
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("(nil) -> number?" == toString(requireType("f")));
}
Sync to upstream/release/603 (#1097) # What's changed? - Record the location of properties for table types (closes #802) - Implement stricter UTF-8 validations as per the RFC (https://github.com/luau-lang/rfcs/pull/1) - Implement `buffer` as a new type in both the old and new solvers. - Changed errors produced by some `buffer` builtins to be a bit more generic to avoid platform-dependent error messages. - Fixed a bug where `Unifier` would copy some persistent types, tripping some internal assertions. - Type checking rules on relational operators is now a little bit more lax. - Improve dead code elimination for some `if` statements with complex always-false conditions ## New type solver - Dataflow analysis now generates phi nodes on exit of branches. - Dataflow analysis avoids producing a new definition for locals or properties that are not owned by that loop. - If a function parameter has been constrained to `never`, report errors at all uses of that parameter within that function. - Switch to using the new `Luau::Set` to replace `std::unordered_set` to alleviate some poor allocation characteristics which was negatively affecting overall performance. - Subtyping can now report many failing reasons instead of just the first one that we happened to find during the test. - Subtyping now also report reasons for type pack mismatches. - When visiting `if` statements or expressions, the resulting context are the common terms in both branches. ## Native codegen - Implement support for `buffer` builtins to its IR for x64 and A64. - Optimized `table.insert` by not inserting a table barrier if it is fastcalled with a constant. ## Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Arseny Kapoulkine <arseny@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-11-10 21:10:07 +00:00
TEST_CASE_FIXTURE(TypeStateFixture, "parameter_x_is_some_type_or_optional_then_assigned_with_alternate_value")
{
CheckResult result = check(R"(
local function f(x: number?)
x = x or 5
return x
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("(number?) -> number" == toString(requireType("f")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "local_assigned_in_either_branches_that_falls_through")
{
CheckResult result = check(R"(
local x = nil
if math.random() > 0.5 then
x = 5
else
x = "hello"
end
local y = x
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("number | string" == toString(requireType("y")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "local_assigned_in_only_one_branch_that_falls_through")
{
CheckResult result = check(R"(
local x = nil
if math.random() > 0.5 then
x = 5
end
local y = x
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("number?" == toString(requireType("y")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_and_else_branch_also_assigns_but_is_met_with_return")
{
CheckResult result = check(R"(
local x = nil
if math.random() > 0.5 then
x = 5
else
x = "hello"
return
end
local y = x
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("number?" == toString(requireType("y")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_but_is_met_with_return_and_else_branch_assigns")
{
CheckResult result = check(R"(
local x = nil
if math.random() > 0.5 then
x = 5
return
else
x = "hello"
end
local y = x
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("string?" == toString(requireType("y")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "invalidate_type_refinements_upon_assignments")
{
CheckResult result = check(R"(
type Ok<T> = { tag: "ok", val: T }
type Err<E> = { tag: "err", err: E }
type Result<T, E> = Ok<T> | Err<E>
local function f<T, E>(res: Result<T, E>)
assert(res.tag == "ok")
local tag: "ok", val: T = res.tag, res.val
res = { tag = "err" :: "err", err = (5 :: any) :: E }
local tag: "err", err: E = res.tag, res.err
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
Sync to upstream/release/607 (#1131) # What's changed? * Fix up the `std::iterator_traits` definitions for some Luau data structures. * Replace some of the usages of `std::unordered_set` and `std::unordered_map` with Luau-provided data structures to increase performance and reduce overall number of heap allocations. * Update some of the documentation links in comments throughout the codebase to correctly point to the moved repository. * Expanded JSON encoder for AST to support singleton types. * Fixed a bug in `luau-analyze` where exceptions in the last module being checked during multithreaded analysis would not be rethrown. ### New type solver * Introduce a `refine` type family to handle deferred refinements during type inference, replacing the old `RefineConstraint`. * Continued work on the implementation of type states, fixing some known bugs/blockers. * Added support for variadic functions in new non-strict mode, enabling broader support for builtins and the Roblox API. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-12-15 21:29:06 +00:00
#if 0
TEST_CASE_FIXTURE(TypeStateFixture, "local_t_is_assigned_a_fresh_table_with_x_assigned_a_union_and_then_assert_restricts_actual_outflow_of_types")
{
CheckResult result = check(R"(
local t = nil
if math.random() > 0.5 then
t = {}
t.x = if math.random() > 0.5 then 5 else "hello"
assert(typeof(t.x) == "string")
else
t = {}
t.x = if math.random() > 0.5 then 7 else true
assert(typeof(t.x) == "boolean")
end
local x = t.x
)");
LUAU_REQUIRE_NO_ERRORS(result);
// CHECK("boolean | string" == toString(requireType("x")));
CHECK("boolean | number | number | string" == toString(requireType("x")));
}
Sync to upstream/release/607 (#1131) # What's changed? * Fix up the `std::iterator_traits` definitions for some Luau data structures. * Replace some of the usages of `std::unordered_set` and `std::unordered_map` with Luau-provided data structures to increase performance and reduce overall number of heap allocations. * Update some of the documentation links in comments throughout the codebase to correctly point to the moved repository. * Expanded JSON encoder for AST to support singleton types. * Fixed a bug in `luau-analyze` where exceptions in the last module being checked during multithreaded analysis would not be rethrown. ### New type solver * Introduce a `refine` type family to handle deferred refinements during type inference, replacing the old `RefineConstraint`. * Continued work on the implementation of type states, fixing some known bugs/blockers. * Added support for variadic functions in new non-strict mode, enabling broader support for builtins and the Roblox API. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-12-15 21:29:06 +00:00
#endif
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_are_unions_of_all_assignments")
{
CheckResult result = check(R"(
local x = nil
function f()
print(x)
x = "five"
end
x = 5
f()
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("(number | string)?" == toString(requireTypeAtPosition({4, 18})));
}
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_are_unions_of_all_assignments_2")
{
CheckResult result = check(R"(
local t = {x = nil}
function f()
print(t.x)
t = {x = "five"}
end
t = {x = 5}
f()
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("{ x: nil } | { x: number } | { x: string }" == toString(requireTypeAtPosition({4, 18}), {true}));
CHECK("(number | string)?" == toString(requireTypeAtPosition({4, 20})));
}
TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions")
{
CheckResult result = check(R"(
local f
function f()
if math.random() > 0.5 then
f()
end
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("(() -> ())?" == toString(requireType("f")));
}
Sync to upstream/release/610 (#1154) # What's changed? * Check interrupt handler inside the pattern match engine to eliminate potential for programs to hang during string library function execution. * Allow iteration over table properties to pass the old type solver. ### Native Code Generation * Use in-place memory operands for math library operations on x64. * Replace opaque bools with separate enum classes in IrDump to improve code maintainability. * Translate operations on inferred vectors to IR. * Enable support for debugging native-compiled functions in Roblox Studio. ### New Type Solver * Rework type inference for boolean and string literals to introduce bounded free types (bounded below by the singleton type, and above by the primitive type) and reworked primitive type constraint to decide which is the appropriate type for the literal. * Introduce `FunctionCheckConstraint` to handle bidirectional typechecking for function calls, pushing the expected parameter types from the function onto the arguments. * Introduce `union` and `intersect` type families to compute deferred simplified unions and intersections to be employed by the constraint generation logic in the new solver. * Implement support for expanding the domain of local types in `Unifier2`. * Rework type inference for iteration variables bound by for in loops to use local types. * Change constraint blocking logic to use a set to prevent accidental re-blocking. * Add logic to detect missing return statements in functions. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-01-27 03:20:56 +00:00
TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_future_assignments")
{
Sync to upstream/release/610 (#1154) # What's changed? * Check interrupt handler inside the pattern match engine to eliminate potential for programs to hang during string library function execution. * Allow iteration over table properties to pass the old type solver. ### Native Code Generation * Use in-place memory operands for math library operations on x64. * Replace opaque bools with separate enum classes in IrDump to improve code maintainability. * Translate operations on inferred vectors to IR. * Enable support for debugging native-compiled functions in Roblox Studio. ### New Type Solver * Rework type inference for boolean and string literals to introduce bounded free types (bounded below by the singleton type, and above by the primitive type) and reworked primitive type constraint to decide which is the appropriate type for the literal. * Introduce `FunctionCheckConstraint` to handle bidirectional typechecking for function calls, pushing the expected parameter types from the function onto the arguments. * Introduce `union` and `intersect` type families to compute deferred simplified unions and intersections to be employed by the constraint generation logic in the new solver. * Implement support for expanding the domain of local types in `Unifier2`. * Rework type inference for iteration variables bound by for in loops to use local types. * Change constraint blocking logic to use a set to prevent accidental re-blocking. * Add logic to detect missing return statements in functions. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-01-27 03:20:56 +00:00
// early return if the flag isn't set since this is blocking gated commits
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
CheckResult result = check(R"(
local f
function f()
if math.random() > 0.5 then
f()
end
end
f = 5
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("((() -> ()) | number)?" == toString(requireType("f")));
}
TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions_but_has_previous_assignments")
{
CheckResult result = check(R"(
local f
f = 5
function f()
if math.random() > 0.5 then
f()
end
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("((() -> ()) | number)?" == toString(requireType("f")));
}
Sync to upstream/release/607 (#1131) # What's changed? * Fix up the `std::iterator_traits` definitions for some Luau data structures. * Replace some of the usages of `std::unordered_set` and `std::unordered_map` with Luau-provided data structures to increase performance and reduce overall number of heap allocations. * Update some of the documentation links in comments throughout the codebase to correctly point to the moved repository. * Expanded JSON encoder for AST to support singleton types. * Fixed a bug in `luau-analyze` where exceptions in the last module being checked during multithreaded analysis would not be rethrown. ### New type solver * Introduce a `refine` type family to handle deferred refinements during type inference, replacing the old `RefineConstraint`. * Continued work on the implementation of type states, fixing some known bugs/blockers. * Added support for variadic functions in new non-strict mode, enabling broader support for builtins and the Roblox API. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-12-15 21:29:06 +00:00
TEST_CASE_FIXTURE(TypeStateFixture, "multiple_assignments_in_loops")
{
CheckResult result = check(R"(
local x = nil
for i = 1, 10 do
x = 5
x = "hello"
end
print(x)
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("(number | string)?" == toString(requireType("x")));
}
Sync to upstream/release/611 (#1160) # What's changed? ### Native Code Generation * Fixed an UAF relating to reusing a hash key after a weak table has undergone some GC. * Fixed a bounds check on arm64 to allow access to the last byte of a buffer. ### New Type Solver * Type states now preserves error-suppression, i.e. `local x: any = 5` and `x.foo` does not error. * Made error-suppression logic in subtyping more accurate. * Subtyping now knows how to reduce type families. * Fixed function call overload resolution so that the return type resolves to the correct overload. * Fixed a case where we attempted to reduce irreducible type families a few too many times, leading to duplicate errors. * Type checker needs to type check annotations in function signatures to be able to report errors relating to those annotations. * Fixed an UAF from a pointer to stack-allocated data in Subtyping's `explainReasonings`. ### Nonstrict Type Checker * Fixed a crash when calling a checked function of the form `math.abs` with an incorrect argument type. * Fixed a crash when calling a checked function with a number of arguments that did not exactly match the number of parameters required. --- ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2024-02-02 21:32:42 +00:00
TEST_CASE_FIXTURE(TypeStateFixture, "typestates_preserve_error_suppression")
{
CheckResult result = check(R"(
local a: any = 51
a = "pickles" -- We'll have a new DefId for this iteration of `a`. Its type must also be error-suppressing
print(a)
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("*error-type* | string" == toString(requireTypeAtPosition({3, 14}), {true}));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_preserve_error_suppression_properties")
{
// early return if the flag isn't set since this is blocking gated commits
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
CheckResult result = check(R"(
local a: {x: any} = {x = 51}
a.x = "pickles" -- We'll have a new DefId for this iteration of `a.x`. Its type must also be error-suppressing
print(a.x)
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("*error-type* | string" == toString(requireTypeAtPosition({3, 16}), {true}));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_do_not_apply_to_the_initial_local_definition")
{
// early return if the flag isn't set since this is blocking gated commits
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
CheckResult result = check(R"(
type MyType = number | string
local foo: MyType = 5
print(foo)
foo = 7
print(foo)
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("number | string" == toString(requireTypeAtPosition({3, 14}), {true}));
CHECK("number" == toString(requireTypeAtPosition({5, 14}), {true}));
}
TEST_CASE_FIXTURE(Fixture, "typestate_globals")
{
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true};
loadDefinition(R"(
declare foo: string | number
declare function f(x: string): ()
)");
CheckResult result = check(R"(
foo = "a"
f(foo)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "typestate_unknown_global")
{
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true};
CheckResult result = check(R"(
x = 5
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<UnknownSymbol>(result.errors[0]));
}
TEST_SUITE_END();