diff --git a/Analysis/src/TypeFunctionRuntime.cpp b/Analysis/src/TypeFunctionRuntime.cpp index 2dfd21d54..efa3522ab 100644 --- a/Analysis/src/TypeFunctionRuntime.cpp +++ b/Analysis/src/TypeFunctionRuntime.cpp @@ -21,6 +21,7 @@ #include LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit) +LUAU_FASTFLAGVARIABLE(DebugLuauTypeFunExternNameMethod) namespace Luau { @@ -1312,6 +1313,24 @@ static int getWriteParent(lua_State* L) return 1; } +// Luau: `self:externname() -> string?` +// Returns the name of a class or 'nil' if there's no name. +static int getExternTypeName(lua_State* L) { + TypeFunctionTypeId self = getTypeUserData(L, 1); + auto tfEx = get(self); + if (!tfEx) + luaL_error(L, "type.externname: expected self to be an extern type, but got %s instead", getTag(L, self).c_str()); + + if (auto exTy = get(tfEx->externTy)) + { + lua_pushstring(L, exTy->name.c_str()); + } + else + lua_pushnil(L); + + return 1; +} + // Luau: `self:name() -> string?` // Returns the name of the generic or 'nil' if the generic is unnamed static int getGenericName(lua_State* L) @@ -1747,6 +1766,7 @@ void registerTypeUserData(lua_State* L) {"name", getGenericName}, {"ispack", getGenericIsPack}, + {(FFlag::DebugLuauTypeFunExternNameMethod) ? "externname" : nullptr, (FFlag::DebugLuauTypeFunExternNameMethod) ? getExternTypeName : nullptr}, {nullptr, nullptr} }; diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index 62f4b24b2..12fb1adb7 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -13,6 +13,7 @@ LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) LUAU_FASTFLAG(LuauResetConditionalContextProperly) LUAU_FASTFLAG(LuauTypeFunNoScopeMapRef) +LUAU_FASTFLAG(DebugLuauTypeFunExternNameMethod) TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests"); @@ -2452,6 +2453,35 @@ end CHECK(toString(result.errors[0]) == R"(Redefinition of type 't0', previously defined at line 2)"); } +TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_externtype_externname_api") +{ + ScopedFastFlag sff[]{ + {FFlag::LuauSolverV2, true}, + {FFlag::DebugLuauTypeFunExternNameMethod, true} + }; + + loadDefinition(R"( + declare class CustomClass + function testFunc(self): number + end + )"); + + CheckResult result = check(R"( + type function pass(arg, compare) + if (arg:is("class")) then + assert(arg:externname() == compare:value()) + end + + return types.unknown + end + + type a = pass + type b = pass + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_fuzz_environment_scope_crash") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};