Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Analysis/include/Luau/TypeFunctionRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ struct TypeFunctionFunctionType

TypeFunctionTypePackId argTypes;
TypeFunctionTypePackId retTypes;

std::vector<std::optional<FunctionArgument>> argNames;
};

template<typename T>
Expand Down
88 changes: 88 additions & 0 deletions Analysis/src/EmbeddedBuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

LUAU_FASTFLAGVARIABLE(LuauTypeCheckerVectorLerp)
LUAU_FASTFLAGVARIABLE(LuauRawGetHandlesNil)
LUAU_FASTFLAG(LuauTypeFunctionFunctionParameterNames)

namespace Luau
{
Expand Down Expand Up @@ -479,9 +480,96 @@ declare types: {
}
)BUILTIN_SRC";

static constexpr const char* kBuiltinDefinitionTypeMethodSrc_PARAMETER_NAMES = R"BUILTIN_SRC(

type Parameter = { name: string?, type: type }

export type type = {
tag: "nil" | "unknown" | "never" | "any" | "boolean" | "number" | "string" | "buffer" | "thread" |
"singleton" | "negation" | "union" | "intersection" | "table" | "function" | "class" | "generic",

is: (self: type, arg: string) -> boolean,

-- for singleton type
value: (self: type) -> (string | boolean | nil),

-- for negation type
inner: (self: type) -> type,

-- for union and intersection types
components: (self: type) -> {type},

-- for table type
setproperty: (self: type, key: type, value: type?) -> (),
setreadproperty: (self: type, key: type, value: type?) -> (),
setwriteproperty: (self: type, key: type, value: type?) -> (),
readproperty: (self: type, key: type) -> type?,
writeproperty: (self: type, key: type) -> type?,
properties: (self: type) -> { [type]: { read: type?, write: type? } },
setindexer: (self: type, index: type, result: type) -> (),
setreadindexer: (self: type, index: type, result: type) -> (),
setwriteindexer: (self: type, index: type, result: type) -> (),
indexer: (self: type) -> { index: type, readresult: type, writeresult: type }?,
readindexer: (self: type) -> { index: type, result: type }?,
writeindexer: (self: type) -> { index: type, result: type }?,
setmetatable: (self: type, arg: type) -> (),
metatable: (self: type) -> type?,

-- for function type
setparameters: (self: type, head: {type | { type: type } | Parameter}?, tail: type?) -> (),
parameters: (self: type) -> { head: {Parameter}?, tail: type? },
setreturns: (self: type, head: {type}?, tail: type? ) -> (),
returns: (self: type) -> { head: {type}?, tail: type? },
setgenerics: (self: type, {type}?) -> (),
generics: (self: type) -> {type},

-- for class type
-- 'properties', 'metatable', 'indexer', 'readindexer' and 'writeindexer' are shared with table type
readparent: (self: type) -> type?,
writeparent: (self: type) -> type?,

-- for generic type
name: (self: type) -> string?,
ispack: (self: type) -> boolean,
}

)BUILTIN_SRC";

static constexpr const char* kBuiltinDefinitionTypesLibSrc_PARAMETER_NAMES = R"BUILTIN_SRC(

declare types: {
unknown: type,
never: type,
any: type,
boolean: type,
number: type,
string: type,
thread: type,
buffer: type,

singleton: @checked (arg: string | boolean | nil) -> type,
optional: @checked (arg: type) -> type,
generic: @checked (name: string, ispack: boolean?) -> type,
negationof: @checked (arg: type) -> type,
unionof: @checked (...type) -> type,
intersectionof: @checked (...type) -> type,
newtable: @checked (props: {[type]: type} | {[type]: { read: type, write: type } } | nil, indexer: { index: type, readresult: type, writeresult: type }?, metatable: type?) -> type,
newfunction: @checked (parameters: { head: {type | Parameter}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
copy: @checked (arg: type) -> type,
}

)BUILTIN_SRC";

std::string getTypeFunctionDefinitionSource()
{
if (FFlag::LuauTypeFunctionFunctionParameterNames)
{
std::string result = kBuiltinDefinitionTypeMethodSrc_PARAMETER_NAMES;

result += kBuiltinDefinitionTypesLibSrc_PARAMETER_NAMES;

return result;
}

std::string result = kBuiltinDefinitionTypeMethodSrc;

Expand Down
108 changes: 97 additions & 11 deletions Analysis/src/TypeFunctionRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <vector>

LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
LUAU_FASTFLAGVARIABLE(LuauTypeFunctionFunctionParameterNames)

namespace Luau
{
Expand Down Expand Up @@ -994,7 +995,7 @@ static std::tuple<std::vector<TypeFunctionTypeId>, std::vector<TypeFunctionTypeP
return {types, packs};
}

static TypeFunctionTypePackId getTypePack(lua_State* L, int headIdx, int tailIdx)
static TypeFunctionTypePackId getTypePack(lua_State* L, int headIdx, int tailIdx, bool namedTypePack = false)
{
TypeFunctionTypePackId result = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{});

Expand All @@ -1014,9 +1015,17 @@ static TypeFunctionTypePackId getTypePack(lua_State* L, int headIdx, int tailIdx
lua_pop(L, 1);
break;
}

head.push_back(getTypeUserData(L, -1));
lua_pop(L, 1);
else if (FFlag::LuauTypeFunctionFunctionParameterNames && namedTypePack && lua_istable(L, -1))
{
lua_getfield(L, -1, "type");
head.push_back(getTypeUserData(L, -1));
lua_pop(L, 2);
}
else
{
head.push_back(getTypeUserData(L, -1));
lua_pop(L, 1);
}
}

lua_pop(L, 1);
Expand Down Expand Up @@ -1092,7 +1101,40 @@ static void pushTypePack(lua_State* L, TypeFunctionTypePackId tp)
}
}

// Luau: `types.newfunction(parameters: {head: {type}?, tail: type?}, returns: {head: {type}?, tail: type?}, generics: {type}?) -> type`
static void getArgNames(lua_State* L, int headIdx, std::vector<std::optional<FunctionArgument>>& argNames)
{
if (!lua_istable(L, headIdx))
return;

int headLen = lua_objlen(L, headIdx);
argNames.resize(headLen);
headIdx = lua_absindex(L, headIdx);

for (int i = 1; i <= headLen; i++)
{
lua_pushinteger(L, i);
lua_gettable(L, headIdx);

std::optional<FunctionArgument> argName;

if (lua_istable(L, -1))
{
lua_getfield(L, -1, "name");
if (lua_isstring(L, -1))
{
const char* str = lua_tostring(L, -1);
if (Luau::isIdentifier(str))
argName = FunctionArgument{std::string(str), {}};
}
lua_pop(L, 1);
}

argNames[i - 1] = argName;
lua_pop(L, 1);
}
}

// Luau: `types.newfunction(parameters: {head: {type | {name: string?, type: type}}?, tail: type?}, returns: {head: {type | {type: type}}?, tail: type?}, generics: {type}?) -> type`
// Returns the type instance representing a function
static int createFunction(lua_State* L)
{
Expand All @@ -1102,12 +1144,19 @@ static int createFunction(lua_State* L)

TypeFunctionTypePackId argTypes = nullptr;

std::vector<std::optional<FunctionArgument>> argNames{};

if (lua_istable(L, 1))
{
lua_getfield(L, 1, "head");
lua_getfield(L, 1, "tail");

argTypes = getTypePack(L, -2, -1);
argTypes = getTypePack(L, -2, -1, /* namedTypePack */ true);

if (FFlag::LuauTypeFunctionFunctionParameterNames)
{
getArgNames(L, -2, argNames);
}

lua_pop(L, 2);
}
Expand Down Expand Up @@ -1142,12 +1191,12 @@ static int createFunction(lua_State* L)

auto [genericTypes, genericPacks] = getGenerics(L, 3, "types.newfunction");

allocTypeUserData(L, TypeFunctionFunctionType{std::move(genericTypes), std::move(genericPacks), argTypes, retTypes});
allocTypeUserData(L, TypeFunctionFunctionType{std::move(genericTypes), std::move(genericPacks), argTypes, retTypes, argNames});

return 1;
}

// Luau: `self:setparameters(head: {type}?, tail: type?)`
// Luau: `self:setparameters(head: {type | {name: string?, type: type}}?, tail: type?)`
// Sets the parameters of the function
static int setFunctionParameters(lua_State* L)
{
Expand All @@ -1160,12 +1209,17 @@ static int setFunctionParameters(lua_State* L)
if (!tfft)
luaL_error(L, "type.setparameters: expected self to be a function, but got %s instead", getTag(L, self).c_str());

tfft->argTypes = getTypePack(L, 2, 3);
tfft->argTypes = getTypePack(L, 2, 3, /* namedTypePack */ true);

if (FFlag::LuauTypeFunctionFunctionParameterNames)
{
getArgNames(L, -2, tfft->argNames);
}

return 0;
}

// Luau: `self:parameters() -> {head: {type}?, tail: type?}`
// Luau: `self:parameters() -> {head: {{name: string?, type: type}}?, tail: type?}`
// Returns the parameters of the function
static int getFunctionParameters(lua_State* L)
{
Expand All @@ -1180,6 +1234,31 @@ static int getFunctionParameters(lua_State* L)

pushTypePack(L, tfft->argTypes);

if (FFlag::LuauTypeFunctionFunctionParameterNames)
{
lua_getfield(L, -1, "head");

int pos = 1;

for (const auto& arg : tfft->argNames)
{
lua_createtable(L, 0, 2);

if (arg)
{
lua_pushstring(L, arg->name.c_str());
lua_setfield(L, -2, "name");
}

lua_rawgeti(L, -2, pos);
lua_setfield(L, -2, "type");

lua_rawseti(L, -2, pos++);
}

lua_pop(L, 1);
}

return 1;
}

Expand Down Expand Up @@ -2397,7 +2476,7 @@ class TypeFunctionCloner
else if (auto f = get<TypeFunctionFunctionType>(ty))
{
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack, {}});
}
else if (auto c = get<TypeFunctionExternType>(ty))
target = ty; // Don't copy a class since they are immutable
Expand Down Expand Up @@ -2562,6 +2641,13 @@ class TypeFunctionCloner

f2->argTypes = shallowClone(f1->argTypes);
f2->retTypes = shallowClone(f1->retTypes);

if (FFlag::LuauTypeFunctionFunctionParameterNames)
{
f2->argNames.reserve(f1->argNames.size());
for (const auto& argName : f1->argNames)
f2->argNames.push_back(argName);
}
}

void cloneChildren(TypeFunctionExternType* c1, TypeFunctionExternType* c2)
Expand Down
17 changes: 16 additions & 1 deletion Analysis/src/TypeFunctionRuntimeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// currently, controls serialization, deserialization, and `type.copy`
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
LUAU_FASTFLAG(LuauEmplaceNotPushBack)
LUAU_FASTFLAG(LuauTypeFunctionFunctionParameterNames)

namespace Luau
{
Expand Down Expand Up @@ -204,7 +205,7 @@ class TypeFunctionSerializer
else if (auto f = get<FunctionType>(ty))
{
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack, {}});
}
else if (auto c = get<ExternType>(ty))
{
Expand Down Expand Up @@ -407,6 +408,13 @@ class TypeFunctionSerializer

f2->argTypes = shallowSerialize(f1->argTypes);
f2->retTypes = shallowSerialize(f1->retTypes);

if (FFlag::LuauTypeFunctionFunctionParameterNames)
{
f2->argNames.reserve(f1->argNames.size());
for (const auto& argName : f1->argNames)
f2->argNames.push_back(argName);
}
}

void serializeChildren(const ExternType* c1, TypeFunctionExternType* c2)
Expand Down Expand Up @@ -993,6 +1001,13 @@ class TypeFunctionDeserializer

if (f2->retTypes)
f1->retTypes = shallowDeserialize(f2->retTypes);

if (FFlag::LuauTypeFunctionFunctionParameterNames)
{
f1->argNames.reserve(f2->argNames.size());
for (const auto& argName : f2->argNames)
f1->argNames.push_back(argName);
}
}

void deserializeChildren(TypeFunctionExternType* c2, ExternType* c1)
Expand Down
Loading