luau/tests/TypeInfer.modules.test.cpp

525 lines
13 KiB
C++
Raw Permalink Normal View History

2022-03-18 08:46:04 +08:00
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/AstQuery.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/Type.h"
2022-03-18 08:46:04 +08:00
#include "Fixture.h"
#include "doctest.h"
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
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 15:46:57 +08:00
LUAU_FASTFLAG(LuauTinyControlFlowAnalysis);
2022-03-18 08:46:04 +08:00
using namespace Luau;
TEST_SUITE_BEGIN("TypeInferModules");
TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_require_basic")
{
fileResolver.source["game/A"] = R"(
--!strict
return {
a = 1,
}
)";
fileResolver.source["game/B"] = R"(
--!strict
local A = require(game.A)
local b = A.a
)";
CheckResult aResult = frontend.check("game/A");
LUAU_REQUIRE_NO_ERRORS(aResult);
CheckResult bResult = frontend.check("game/B");
LUAU_REQUIRE_NO_ERRORS(bResult);
ModulePtr b = frontend.moduleResolver.getModule("game/B");
REQUIRE(b != nullptr);
std::optional<TypeId> bType = requireType(b, "b");
REQUIRE(bType);
CHECK(toString(*bType) == "number");
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "require")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["game/A"] = R"(
local function hooty(x: number): string
return "Hi there!"
end
return {hooty=hooty}
)";
fileResolver.source["game/B"] = R"(
local Hooty = require(game.A)
local h -- free!
local i = Hooty.hooty(h)
)";
CheckResult aResult = frontend.check("game/A");
dumpErrors(aResult);
LUAU_REQUIRE_NO_ERRORS(aResult);
CheckResult bResult = frontend.check("game/B");
dumpErrors(bResult);
LUAU_REQUIRE_NO_ERRORS(bResult);
ModulePtr b = frontend.moduleResolver.getModule("game/B");
2022-03-18 08:46:04 +08:00
REQUIRE(b != nullptr);
dumpErrors(bResult);
std::optional<TypeId> iType = requireType(b, "i");
REQUIRE_EQ("string", toString(*iType));
std::optional<TypeId> hType = requireType(b, "h");
REQUIRE_EQ("number", toString(*hType));
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "require_types")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["workspace/A"] = R"(
export type Point = {x: number, y: number}
return {}
)";
fileResolver.source["workspace/B"] = R"(
local Hooty = require(workspace.A)
local h: Hooty.Point
)";
CheckResult bResult = frontend.check("workspace/B");
2022-05-14 03:36:37 +08:00
LUAU_REQUIRE_NO_ERRORS(bResult);
2022-03-18 08:46:04 +08:00
ModulePtr b = frontend.moduleResolver.getModule("workspace/B");
2022-03-18 08:46:04 +08:00
REQUIRE(b != nullptr);
TypeId hType = requireType(b, "h");
REQUIRE_MESSAGE(bool(get<TableType>(hType)), "Expected table but got " << toString(hType));
2022-03-18 08:46:04 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "require_a_variadic_function")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["game/A"] = R"(
local T = {}
function T.f(...) end
return T
)";
fileResolver.source["game/B"] = R"(
local A = require(game.A)
local f = A.f
)";
CheckResult result = frontend.check("game/B");
ModulePtr bModule = frontend.moduleResolver.getModule("game/B");
REQUIRE(bModule != nullptr);
TypeId f = follow(requireType(bModule, "f"));
const FunctionType* ftv = get<FunctionType>(f);
2022-03-18 08:46:04 +08:00
REQUIRE(ftv);
auto iter = begin(ftv->argTypes);
auto endIter = end(ftv->argTypes);
REQUIRE(iter == endIter);
REQUIRE(iter.tail());
CHECK(get<VariadicTypePack>(*iter.tail()));
}
TEST_CASE_FIXTURE(Fixture, "type_error_of_unknown_qualified_type")
{
CheckResult result = check(R"(
local p: SomeModule.DoesNotExist
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
REQUIRE_EQ(result.errors[0], (TypeError{Location{{1, 17}, {1, 40}}, UnknownSymbol{"SomeModule.DoesNotExist"}}));
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "require_module_that_does_not_export")
2022-03-18 08:46:04 +08:00
{
const std::string sourceA = R"(
)";
const std::string sourceB = R"(
local Hooty = require(script.Parent.A)
)";
fileResolver.source["game/Workspace/A"] = sourceA;
fileResolver.source["game/Workspace/B"] = sourceB;
frontend.check("game/Workspace/A");
frontend.check("game/Workspace/B");
ModulePtr aModule = frontend.moduleResolver.getModule("game/Workspace/A");
ModulePtr bModule = frontend.moduleResolver.getModule("game/Workspace/B");
2022-03-18 08:46:04 +08:00
CHECK(aModule->errors.empty());
REQUIRE_EQ(1, bModule->errors.size());
CHECK_MESSAGE(get<IllegalRequire>(bModule->errors[0]), "Should be IllegalRequire: " << toString(bModule->errors[0]));
auto hootyType = requireType(bModule, "Hooty");
CHECK_EQ("*error-type*", toString(hootyType));
2022-03-18 08:46:04 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "warn_if_you_try_to_require_a_non_modulescript")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["Modules/A"] = "";
fileResolver.sourceTypes["Modules/A"] = SourceCode::Local;
fileResolver.source["Modules/B"] = R"(
local M = require(script.Parent.A)
)";
CheckResult result = frontend.check("Modules/B");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<IllegalRequire>(result.errors[0]));
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "general_require_call_expression")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["game/A"] = R"(
--!strict
return { def = 4 }
)";
fileResolver.source["game/B"] = R"(
--!strict
local tbl = { abc = require(game.A) }
local a : string = ""
a = tbl.abc.def
)";
CheckResult result = frontend.check("game/B");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("Type 'number' could not be converted into 'string'", toString(result.errors[0]));
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "general_require_type_mismatch")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["game/A"] = R"(
return { def = 4 }
)";
fileResolver.source["game/B"] = R"(
local tbl: string = require(game.A)
)";
CheckResult result = frontend.check("game/B");
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK_EQ("Type '{ def: number }' could not be converted into 'string'", toString(result.errors[0]));
else
CHECK_EQ("Type '{| def: number |}' could not be converted into 'string'", toString(result.errors[0]));
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE(Fixture, "bound_free_table_export_is_ok")
{
CheckResult result = check(R"(
local n = {}
function n:Clone() end
local m = {}
function m.a(x)
x:Clone()
end
function m.b()
m.a(n)
end
return m
)");
LUAU_REQUIRE_NO_ERRORS(result);
2022-03-18 08:46:04 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "custom_require_global")
2022-03-18 08:46:04 +08:00
{
CheckResult result = check(R"(
--!nonstrict
require = function(a) end
local crash = require(game.A)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "require_failed_module")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["game/A"] = R"(
return unfortunately()
)";
CheckResult aResult = frontend.check("game/A");
LUAU_REQUIRE_ERRORS(aResult);
CheckResult result = check(R"(
local ModuleA = require(game.A)
)");
LUAU_REQUIRE_NO_ERRORS(result);
std::optional<TypeId> oty = requireType("ModuleA");
2022-07-29 12:24:07 +08:00
CHECK_EQ("*error-type*", toString(*oty));
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_modify_imported_types")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["game/A"] = R"(
export type Type = { unrelated: boolean }
return {}
)";
fileResolver.source["game/B"] = R"(
local types = require(game.A)
type Type = types.Type
local x: Type = {}
function x:Destroy(): () end
)";
CheckResult result = frontend.check("game/B");
LUAU_REQUIRE_ERROR_COUNT(2, result);
2022-03-18 08:46:04 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_modify_imported_types_2")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["game/A"] = R"(
export type Type = { x: { a: number } }
return {}
)";
fileResolver.source["game/B"] = R"(
local types = require(game.A)
type Type = types.Type
local x: Type = { x = { a = 2 } }
type Rename = typeof(x.x)
)";
CheckResult result = frontend.check("game/B");
LUAU_REQUIRE_NO_ERRORS(result);
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_modify_imported_types_3")
2022-03-18 08:46:04 +08:00
{
fileResolver.source["game/A"] = R"(
local y = setmetatable({}, {})
export type Type = { x: typeof(y) }
return { x = y }
)";
fileResolver.source["game/B"] = R"(
local types = require(game.A)
type Type = types.Type
local x: Type = types
type Rename = typeof(x.x)
)";
CheckResult result = frontend.check("game/B");
LUAU_REQUIRE_NO_ERRORS(result);
}
2022-07-08 09:22:39 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_modify_imported_types_4")
{
fileResolver.source["game/A"] = R"(
export type Array<T> = {T}
local arrayops = {}
function arrayops.foo(x: Array<any>) end
return arrayops
)";
CheckResult result = check(R"(
local arrayops = require(game.A)
local tbl = {}
tbl.a = 2
function tbl:foo(b: number, c: number)
-- introduce BoundType to imported type
2022-07-08 09:22:39 +08:00
arrayops.foo(self._regions)
end
-- this alias decreases function type level and causes a demotion of its type
type Table = typeof(tbl)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_modify_imported_types_5")
{
fileResolver.source["game/A"] = R"(
export type Type = {x: number, y: number}
local arrayops = {}
function arrayops.foo(x: Type) end
return arrayops
)";
CheckResult result = check(R"(
local arrayops = require(game.A)
local tbl = {}
tbl.a = 2
function tbl:foo(b: number, c: number)
-- introduce boundTo TableType to imported type
self.x.a = 2
arrayops.foo(self.x)
end
-- this alias decreases function type level and causes a demotion of its type
2022-07-08 09:22:39 +08:00
type Table = typeof(tbl)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "module_type_conflict")
2022-03-25 06:04:14 +08:00
{
fileResolver.source["game/A"] = R"(
export type T = { x: number }
return {}
)";
fileResolver.source["game/B"] = R"(
export type T = { x: string }
return {}
)";
fileResolver.source["game/C"] = R"(
local A = require(game.A)
local B = require(game.B)
local a: A.T = { x = 2 }
local b: B.T = a
)";
CheckResult result = frontend.check("game/C");
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::DebugLuauDeferredConstraintResolution)
Sync to upstream/release/614 (#1173) # What's changed? Add program argument passing to scripts run using the Luau REPL! You can now pass `--program-args` (or shorthand `-a`) to the REPL which will treat all remaining arguments as arguments to pass to executed scripts. These values can be accessed through variadic argument expansion. You can read these values like so: ``` local args = {...} -- gets you an array of all the arguments ``` For example if we run the following script like `luau test.lua -a test1 test2 test3`: ``` -- test.lua print(...) ``` you should get the output: ``` test1 test2 test3 ``` ### Native Code Generation * Improve A64 lowering for vector operations by using vector instructions * Fix lowering issue in IR value location tracking! - A developer reported a divergence between code run in the VM and Native Code Generation which we have now fixed ### New Type Solver * Apply substitution to type families, and emit new constraints to reduce those further * More progress on reducing comparison (`lt/le`)type families * Resolve two major sources of cyclic types in the new solver ### Miscellaneous * Turned internal compiler errors (ICE's) into warnings and errors ------- 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: 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>
2024-02-24 04:08:34 +08:00
CHECK(toString(result.errors.at(0)) == "Type 'a' could not be converted into 'T'; at [read \"x\"], number is not exactly string");
else
{
const std::string expected = R"(Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'
caused by:
Property 'x' is not compatible.
Type 'number' could not be converted into 'string' in an invariant context)";
CHECK_EQ(expected, toString(result.errors[0]));
}
2022-03-25 06:04:14 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "module_type_conflict_instantiated")
2022-03-25 06:04:14 +08:00
{
fileResolver.source["game/A"] = R"(
export type Wrap<T> = { x: T }
return {}
)";
fileResolver.source["game/B"] = R"(
local A = require(game.A)
export type T = A.Wrap<number>
return {}
)";
fileResolver.source["game/C"] = R"(
local A = require(game.A)
export type T = A.Wrap<string>
return {}
)";
fileResolver.source["game/D"] = R"(
local A = require(game.B)
local B = require(game.C)
local a: A.T = { x = 2 }
local b: B.T = a
)";
CheckResult result = frontend.check("game/D");
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::DebugLuauDeferredConstraintResolution)
Sync to upstream/release/614 (#1173) # What's changed? Add program argument passing to scripts run using the Luau REPL! You can now pass `--program-args` (or shorthand `-a`) to the REPL which will treat all remaining arguments as arguments to pass to executed scripts. These values can be accessed through variadic argument expansion. You can read these values like so: ``` local args = {...} -- gets you an array of all the arguments ``` For example if we run the following script like `luau test.lua -a test1 test2 test3`: ``` -- test.lua print(...) ``` you should get the output: ``` test1 test2 test3 ``` ### Native Code Generation * Improve A64 lowering for vector operations by using vector instructions * Fix lowering issue in IR value location tracking! - A developer reported a divergence between code run in the VM and Native Code Generation which we have now fixed ### New Type Solver * Apply substitution to type families, and emit new constraints to reduce those further * More progress on reducing comparison (`lt/le`)type families * Resolve two major sources of cyclic types in the new solver ### Miscellaneous * Turned internal compiler errors (ICE's) into warnings and errors ------- 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: 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>
2024-02-24 04:08:34 +08:00
CHECK(toString(result.errors.at(0)) == "Type 'a' could not be converted into 'T'; at [read \"x\"], number is not exactly string");
else
{
const std::string expected = R"(Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'
caused by:
Property 'x' is not compatible.
Type 'number' could not be converted into 'string' in an invariant context)";
CHECK_EQ(expected, toString(result.errors[0]));
}
2022-03-25 06:04:14 +08:00
}
2022-07-08 09:22:39 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "constrained_anyification_clone_immutable_types")
{
fileResolver.source["game/A"] = R"(
return function(...) end
)";
fileResolver.source["game/B"] = R"(
local l0 = require(game.A)
return l0
)";
CheckResult result = frontend.check("game/B");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_anyify_variadic_return_must_follow")
{
CheckResult result = check(R"(
return unpack(l0[_])
)");
LUAU_REQUIRE_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "check_imported_module_names")
{
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 15:46:57 +08:00
ScopedFastFlag sff{FFlag::LuauTinyControlFlowAnalysis, true};
Sync to upstream/release/568 (#865) * A small subset of control-flow refinements have been added to recognize type options that are unreachable after a conditional/unconditional code block. (Fixes https://github.com/Roblox/luau/issues/356). Some examples: ```lua local function f(x: string?) if not x then return end -- x is 'string' here end ``` Throwing calls like `error` or `assert(false)` instead of 'return' are also recognized. Existing complex refinements like type/typeof and tagged union checks are expected to work, among others. To enable this feature, `LuauTinyControlFlowAnalysis` exclusion has to be removed from `ExperimentalFlags.h`. If will become enabled unconditionally in the near future. * Linter has been integrated into the typechecker analysis so that type-aware lint warnings can work in any mode `Frontend::lint` methods were deprecated, `Frontend::check` has to be used instead with `runLintChecks` option set. Resulting lint warning are located inside `CheckResult`. * Fixed large performance drop and increased memory consumption when array is filled at an offset (Fixes https://github.com/Roblox/luau/issues/590) * Part of [Type error suppression RFC](https://github.com/Roblox/luau/blob/master/rfcs/type-error-suppression.md) was implemented making subtyping checks with `any` type transitive. --- In our work on the new type-solver: * `--!nocheck` mode no longer reports type errors * New solver will not be used for `--!nonstrict` modules until all issues with strict mode typechecking are fixed * Added control-flow aware type refinements mentioned earlier In native code generation: * `LOP_NAMECALL` has been translated to IR * `type` and `typeof` builtin fastcalls have been translated to IR/assembly * Additional steps were taken towards arm64 support
2023-03-18 03:20:37 +08:00
fileResolver.source["game/A"] = R"(
return function(...) end
)";
fileResolver.source["game/B"] = R"(
local l0 = require(game.A)
return l0
)";
CheckResult result = check(R"(
local l0 = require(game.B)
if true then
local l1 = require(game.A)
end
return l0
)");
LUAU_REQUIRE_NO_ERRORS(result);
ModulePtr mod = getMainModule();
REQUIRE(mod);
Sync to upstream/release/568 (#865) * A small subset of control-flow refinements have been added to recognize type options that are unreachable after a conditional/unconditional code block. (Fixes https://github.com/Roblox/luau/issues/356). Some examples: ```lua local function f(x: string?) if not x then return end -- x is 'string' here end ``` Throwing calls like `error` or `assert(false)` instead of 'return' are also recognized. Existing complex refinements like type/typeof and tagged union checks are expected to work, among others. To enable this feature, `LuauTinyControlFlowAnalysis` exclusion has to be removed from `ExperimentalFlags.h`. If will become enabled unconditionally in the near future. * Linter has been integrated into the typechecker analysis so that type-aware lint warnings can work in any mode `Frontend::lint` methods were deprecated, `Frontend::check` has to be used instead with `runLintChecks` option set. Resulting lint warning are located inside `CheckResult`. * Fixed large performance drop and increased memory consumption when array is filled at an offset (Fixes https://github.com/Roblox/luau/issues/590) * Part of [Type error suppression RFC](https://github.com/Roblox/luau/blob/master/rfcs/type-error-suppression.md) was implemented making subtyping checks with `any` type transitive. --- In our work on the new type-solver: * `--!nocheck` mode no longer reports type errors * New solver will not be used for `--!nonstrict` modules until all issues with strict mode typechecking are fixed * Added control-flow aware type refinements mentioned earlier In native code generation: * `LOP_NAMECALL` has been translated to IR * `type` and `typeof` builtin fastcalls have been translated to IR/assembly * Additional steps were taken towards arm64 support
2023-03-18 03:20:37 +08:00
REQUIRE(mod->scopes.size() == 4);
CHECK(mod->scopes[0].second->importedModules["l0"] == "game/B");
CHECK(mod->scopes[3].second->importedModules["l1"] == "game/A");
}
2022-03-18 08:46:04 +08:00
TEST_SUITE_END();