luau/tests/TypeInfer.anyerror.test.cpp
vegorov-rbx 4b267aa5c5
Sync to upstream/release/574 (#910)
* Added a limit on how many instructions the Compiler can safely produce
(reported by @TheGreatSageEqualToHeaven)

C++ API Changes:
* With work started on read-only and write-only properties,
`Property::type` member variable has been replaced with `TypeId type()`
and `setType(TypeId)` functions.
* New `LazyType` unwrap callback now has a `void` return type, all
that's required from the callback is to write into `unwrapped` field.

In our work on the new type solver, the following issues were fixed:

* Work has started to support https://github.com/Roblox/luau/pull/77 and
https://github.com/Roblox/luau/pull/79
* Refinements are no longer applied on l-values, removing some
false-positive errors
* Improved overload resolution against expected result type
* `Frontend::prepareModuleScope` now works in the new solver
* Cofinite strings are now comparable

And these are the changes in native code generation (JIT):

* Fixed MIN_NUM and MAX_NUM constant fold when one of the arguments is
NaN
* Added constant folding for number conversions and bit operations
* Value spilling and rematerialization is now supported on arm64
* Improved FASTCALL2K IR generation to support second argument constant
* Added value numbering and load/store propagation optimizations
* Added STORE_VECTOR on arm64, completing the IR lowering on this target
2023-04-28 12:55:13 -07:00

367 lines
7.7 KiB
C++

// 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"
#include "Luau/VisitType.h"
#include "Fixture.h"
#include "doctest.h"
using namespace Luau;
TEST_SUITE_BEGIN("TypeInferAnyError");
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any")
{
CheckResult result = check(R"(
function bar(): any
return true
end
local a
for b in bar do
a = b
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(builtinTypes->anyType, requireType("a"));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any2")
{
CheckResult result = check(R"(
function bar(): any
return true
end
local a
for b in bar() do
a = b
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any")
{
CheckResult result = check(R"(
local bar: any
local a
for b in bar do
a = b
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any2")
{
CheckResult result = check(R"(
local bar: any
local a
for b in bar() do
a = b
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error")
{
CheckResult result = check(R"(
local a
for b in bar do
a = b
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("*error-type*", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error2")
{
CheckResult result = check(R"(
function bar(c) return c end
local a
for b in bar() do
a = b
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("*error-type*", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "length_of_error_type_does_not_produce_an_error")
{
CheckResult result = check(R"(
local l = #this_is_not_defined
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
}
TEST_CASE_FIXTURE(Fixture, "indexing_error_type_does_not_produce_an_error")
{
CheckResult result = check(R"(
local originalReward = unknown.Parent.Reward:GetChildren()[1]
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
}
TEST_CASE_FIXTURE(Fixture, "dot_on_error_type_does_not_produce_an_error")
{
CheckResult result = check(R"(
local foo = (true).x
foo.x = foo.y
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
}
TEST_CASE_FIXTURE(Fixture, "any_type_propagates")
{
CheckResult result = check(R"(
local foo: any
local bar = foo:method("argument")
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("bar")));
}
TEST_CASE_FIXTURE(Fixture, "can_subscript_any")
{
CheckResult result = check(R"(
local foo: any
local bar = foo[5]
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("bar")));
}
// Not strictly correct: metatables permit overriding this
TEST_CASE_FIXTURE(Fixture, "can_get_length_of_any")
{
CheckResult result = check(R"(
local foo: any = {}
local bar = #foo
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(PrimitiveType::Number, getPrimitiveType(requireType("bar")));
}
TEST_CASE_FIXTURE(Fixture, "assign_prop_to_table_by_calling_any_yields_any")
{
CheckResult result = check(R"(
local f: any
local T = {}
T.prop = f()
return T
)");
LUAU_REQUIRE_NO_ERRORS(result);
TableType* ttv = getMutable<TableType>(requireType("T"));
REQUIRE(ttv);
REQUIRE(ttv->props.count("prop"));
REQUIRE_EQ("any", toString(ttv->props["prop"].type()));
}
TEST_CASE_FIXTURE(Fixture, "quantify_any_does_not_bind_to_itself")
{
CheckResult result = check(R"(
local A : any
function A.B() end
A:C()
)");
LUAU_REQUIRE_NO_ERRORS(result);
TypeId aType = requireType("A");
CHECK_EQ(aType, builtinTypes->anyType);
}
TEST_CASE_FIXTURE(Fixture, "calling_error_type_yields_error")
{
CheckResult result = check(R"(
local a = unknown.Parent.Reward.GetChildren()
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
UnknownSymbol* err = get<UnknownSymbol>(result.errors[0]);
REQUIRE(err != nullptr);
CHECK_EQ("unknown", err->name);
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK_EQ("any", toString(requireType("a")));
else
CHECK_EQ("*error-type*", toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error")
{
CheckResult result = check(R"(
local a = Utility.Create "Foo" {}
)");
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK_EQ("any", toString(requireType("a")));
else
CHECK_EQ("*error-type*", toString(requireType("a")));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_complex_function_with_any")
{
CheckResult result = check(R"(
local a: any
local b
for _, i in pairs(a) do
b = i
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("b")));
}
TEST_CASE_FIXTURE(Fixture, "call_to_any_yields_any")
{
CheckResult result = check(R"(
local a: any
local b = a()
)");
REQUIRE_EQ("any", toString(requireType("b")));
}
TEST_CASE_FIXTURE(Fixture, "CheckMethodsOfAny")
{
CheckResult result = check(R"(
local x: any = {}
function x:y(z: number)
local s: string = z
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
}
TEST_CASE_FIXTURE(Fixture, "CheckMethodsOfError")
{
CheckResult result = check(R"(
local x = (true).foo
function x:y(z: number)
local s: string = z
end
)");
LUAU_REQUIRE_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_of_any_can_be_a_table")
{
CheckResult result = check(R"(
--!strict
local T: any
T = {}
T.__index = T
function T.new(...)
local self = {}
setmetatable(self, T)
self:construct(...)
return self
end
function T:construct(index)
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "type_error_addition")
{
CheckResult result = check(R"(
--!strict
local foo = makesandwich()
local bar = foo.nutrition + 100
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
// We should definitely get this error
CHECK_EQ("Unknown global 'makesandwich'", toString(result.errors[0]));
// We get this error if makesandwich() returns a free type
// CHECK_EQ("Unknown type used in + operation; consider adding a type annotation to 'foo'", toString(result.errors[1]));
}
TEST_CASE_FIXTURE(Fixture, "prop_access_on_any_with_other_options")
{
CheckResult result = check(R"(
local function f(thing: any | string)
local foo = thing.SomeRandomKey
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "union_of_types_regression_test")
{
CheckResult result = check(R"(
--!strict
local stat
stat = stat and tonumber(stat) or stat
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "intersection_of_any_can_have_props")
{
// *blocked-130* ~ hasProp any & ~(false?), "_status"
CheckResult result = check(R"(
function foo(x: any, y)
if x then
return x._status
end
return y
end
)");
CHECK("(any, any) -> any" == toString(requireType("foo")));
}
TEST_SUITE_END();