From a8047b2e46e357737abb6283fccebc1d1b4f3f8f Mon Sep 17 00:00:00 2001 From: karl-police Date: Mon, 9 Sep 2024 22:51:33 +0200 Subject: [PATCH] keyof - fix LUAU_ASSERT when there's only one key entry (#1388) Fixes https://github.com/luau-lang/luau/issues/1387 Was suggested by @alexmccord I changed ``singletons[0]`` to ``singletons.front()``, unsure if that makes a huge difference, and then I added the rest of the things needed for the return type. Maybe it's also the ideal location since doing it before looping through ``keys`` won't add the string into the type arena. I put comments next to it based on how I thought it would make sense.   ``LUAU_ASSERT`` seems to trigger when there's only one entry being put inside a UnionType. It's as if it was put there for quality. Allow edits by maintainers is enabled. I tested this with a quick Unit Test something like ```lua local test: keyof ``` --- Analysis/src/TypeFunction.cpp | 6 ++++++ tests/TypeFunction.test.cpp | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index 9d4de8bc..205d0fc4 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -2041,6 +2041,12 @@ TypeFunctionReductionResult keyofFunctionImpl( for (std::string key : keys) singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{key}})); + // If there's only one entry, we don't need a UnionType. + // We can take straight take it from the first entry + // because it was added into the type arena already. + if (singletons.size() == 1) + return {singletons.front(), false, {}, {}}; + return {ctx->arena->addType(UnionType{singletons}), false, {}, {}}; } diff --git a/tests/TypeFunction.test.cpp b/tests/TypeFunction.test.cpp index 4d500d18..1f8d7d66 100644 --- a/tests/TypeFunction.test.cpp +++ b/tests/TypeFunction.test.cpp @@ -388,6 +388,25 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables") CHECK_EQ("\"w\" | \"x\" | \"y\" | \"z\"", toString(tpm->givenTp)); } +TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_single_entry_no_uniontype") +{ + if (!FFlag::LuauSolverV2) + return; + + CheckResult result = check(R"( + local tbl_A = { abc = "value" } + local tbl_B = { a1 = nil, ["a2"] = nil } + + type keyof_A = keyof + type keyof_B = keyof + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + + CHECK(toString(requireTypeAlias("keyof_A")) == "\"abc\""); + CHECK(toString(requireTypeAlias("keyof_B")) == "\"a1\" | \"a2\""); +} + TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_errors_if_it_has_nontable_part") { if (!FFlag::LuauSolverV2)