mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Support __call
on class type vars (#762)
Currently, the metatable of a class type var is not correctly checked to see if it is callable (i.e. has a `__call` metatable). We resolve this issue by checking the metatable in `checkCallOverload` Fixes #756 Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
This commit is contained in:
parent
7dbe47f4dd
commit
9095fc4b83
@ -50,6 +50,7 @@ LUAU_FASTFLAGVARIABLE(LuauBetterMessagingOnCountMismatch, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauArgMismatchReportFunctionLocation, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauImplicitElseRefinement, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDeclareClassPrototype, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCallableClasses, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -4257,26 +4258,33 @@ std::optional<WithPredicate<TypePackId>> TypeChecker::checkCallOverload(const Sc
|
||||
|
||||
std::vector<Location> metaArgLocations;
|
||||
|
||||
// Might be a callable table
|
||||
// Might be a callable table or class
|
||||
std::optional<TypeId> callTy = std::nullopt;
|
||||
if (const MetatableTypeVar* mttv = get<MetatableTypeVar>(fn))
|
||||
{
|
||||
if (std::optional<TypeId> ty = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, /* addErrors= */ false))
|
||||
{
|
||||
// Construct arguments with 'self' added in front
|
||||
TypePackId metaCallArgPack = addTypePack(TypePackVar(TypePack{args->head, args->tail}));
|
||||
callTy = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
||||
}
|
||||
else if (const ClassTypeVar* ctv = get<ClassTypeVar>(fn); FFlag::LuauCallableClasses && ctv && ctv->metatable)
|
||||
{
|
||||
callTy = getIndexTypeFromType(scope, *ctv->metatable, "__call", expr.func->location, /* addErrors= */ false);
|
||||
}
|
||||
|
||||
TypePack* metaCallArgs = getMutable<TypePack>(metaCallArgPack);
|
||||
metaCallArgs->head.insert(metaCallArgs->head.begin(), fn);
|
||||
if (callTy)
|
||||
{
|
||||
// Construct arguments with 'self' added in front
|
||||
TypePackId metaCallArgPack = addTypePack(TypePackVar(TypePack{args->head, args->tail}));
|
||||
|
||||
metaArgLocations = *argLocations;
|
||||
metaArgLocations.insert(metaArgLocations.begin(), expr.func->location);
|
||||
TypePack* metaCallArgs = getMutable<TypePack>(metaCallArgPack);
|
||||
metaCallArgs->head.insert(metaCallArgs->head.begin(), fn);
|
||||
|
||||
fn = instantiate(scope, *ty, expr.func->location);
|
||||
metaArgLocations = *argLocations;
|
||||
metaArgLocations.insert(metaArgLocations.begin(), expr.func->location);
|
||||
|
||||
argPack = metaCallArgPack;
|
||||
args = metaCallArgs;
|
||||
argLocations = &metaArgLocations;
|
||||
}
|
||||
fn = instantiate(scope, *callTy, expr.func->location);
|
||||
|
||||
argPack = metaCallArgPack;
|
||||
args = metaCallArgs;
|
||||
argLocations = &metaArgLocations;
|
||||
}
|
||||
|
||||
const FunctionTypeVar* ftv = get<FunctionTypeVar>(fn);
|
||||
|
@ -91,6 +91,13 @@ struct ClassFixture : BuiltinsFixture
|
||||
typeChecker.globalScope->exportedTypeBindings["Vector2"] = TypeFun{{}, vector2InstanceType};
|
||||
addGlobalBinding(frontend, "Vector2", vector2Type, "@test");
|
||||
|
||||
TypeId callableClassMetaType = arena.addType(TableTypeVar{});
|
||||
TypeId callableClassType = arena.addType(ClassTypeVar{"CallableClass", {}, nullopt, callableClassMetaType, {}, {}, "Test"});
|
||||
getMutable<TableTypeVar>(callableClassMetaType)->props = {
|
||||
{"__call", {makeFunction(arena, nullopt, {callableClassType, typeChecker.stringType}, {typeChecker.numberType})}},
|
||||
};
|
||||
typeChecker.globalScope->exportedTypeBindings["CallableClass"] = TypeFun{{}, callableClassType};
|
||||
|
||||
for (const auto& [name, tf] : typeChecker.globalScope->exportedTypeBindings)
|
||||
persist(tf.type);
|
||||
|
||||
@ -514,4 +521,17 @@ TEST_CASE_FIXTURE(ClassFixture, "unions_of_intersections_of_classes")
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "callable_classes")
|
||||
{
|
||||
ScopedFastFlag luauCallableClasses{"LuauCallableClasses", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local x : CallableClass
|
||||
local y = x("testing")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("number", toString(requireType("y")));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
Loading…
Reference in New Issue
Block a user