mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 06:15:44 +08:00
Sync to upstream/release/642 (#1385)
## New Solver * The type functions `keyof` and `index` now also walk the inheritance chain when they are used on class types like Roblox instances. --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
This commit is contained in:
parent
b23d43496b
commit
d9536cecd8
@ -15,7 +15,6 @@
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAGVARIABLE(LuauSkipEmptyInstantiations, false);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -122,7 +121,7 @@ struct ClonePublicInterface : Substitution
|
||||
|
||||
if (FunctionType* ftv = getMutable<FunctionType>(result))
|
||||
{
|
||||
if (FFlag::LuauSkipEmptyInstantiations && ftv->generics.empty() && ftv->genericPacks.empty())
|
||||
if (ftv->generics.empty() && ftv->genericPacks.empty())
|
||||
{
|
||||
GenericTypeFinder marker;
|
||||
marker.traverse(result);
|
||||
|
@ -1897,7 +1897,30 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
||||
return res;
|
||||
}
|
||||
|
||||
// this should not be reachable since the type should be a valid tables part from normalization.
|
||||
if (auto classTy = get<ClassType>(ty))
|
||||
{
|
||||
for (auto [key, _] : classTy->props)
|
||||
result.insert(key);
|
||||
|
||||
bool res = true;
|
||||
if (classTy->metatable && !isRaw)
|
||||
{
|
||||
// findMetatableEntry demands the ability to emit errors, so we must give it
|
||||
// the necessary state to do that, even if we intend to just eat the errors.
|
||||
ErrorVec dummy;
|
||||
|
||||
std::optional<TypeId> mmType = findMetatableEntry(ctx->builtins, dummy, ty, "__index", Location{});
|
||||
if (mmType)
|
||||
res = res && computeKeysOf(*mmType, result, seen, isRaw, ctx);
|
||||
}
|
||||
|
||||
if (classTy->parent)
|
||||
res = res && computeKeysOf(follow(*classTy->parent), result, seen, isRaw, ctx);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// this should not be reachable since the type should be a valid tables or classes part from normalization.
|
||||
LUAU_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
@ -1941,34 +1964,32 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
||||
{
|
||||
LUAU_ASSERT(!normTy->hasTables());
|
||||
|
||||
// seen set for key computation for classes
|
||||
DenseHashSet<TypeId> seen{{}};
|
||||
|
||||
auto classesIter = normTy->classes.ordering.begin();
|
||||
auto classesIterEnd = normTy->classes.ordering.end();
|
||||
LUAU_ASSERT(classesIter != classesIterEnd); // should be guaranteed by the `hasClasses` check
|
||||
LUAU_ASSERT(classesIter != classesIterEnd); // should be guaranteed by the `hasClasses` check earlier
|
||||
|
||||
auto classTy = get<ClassType>(*classesIter);
|
||||
if (!classTy)
|
||||
{
|
||||
LUAU_ASSERT(false); // this should not be possible according to normalization's spec
|
||||
return {std::nullopt, true, {}, {}};
|
||||
}
|
||||
|
||||
for (auto [key, _] : classTy->props)
|
||||
keys.insert(key);
|
||||
// collect all the properties from the first class type
|
||||
if (!computeKeysOf(*classesIter, keys, seen, isRaw, ctx))
|
||||
return {ctx->builtins->stringType, false, {}, {}}; // if it failed, we have a top type!
|
||||
|
||||
// we need to look at each class to remove any keys that are not common amongst them all
|
||||
while (++classesIter != classesIterEnd)
|
||||
{
|
||||
auto classTy = get<ClassType>(*classesIter);
|
||||
if (!classTy)
|
||||
{
|
||||
LUAU_ASSERT(false); // this should not be possible according to normalization's spec
|
||||
return {std::nullopt, true, {}, {}};
|
||||
}
|
||||
seen.clear(); // we'll reuse the same seen set
|
||||
|
||||
Set<std::string> localKeys{{}};
|
||||
|
||||
// we can skip to the next class if this one is a top type
|
||||
if (!computeKeysOf(*classesIter, localKeys, seen, isRaw, ctx))
|
||||
continue;
|
||||
|
||||
for (auto key : keys)
|
||||
{
|
||||
// remove any keys that are not present in each class
|
||||
if (classTy->props.find(key) == classTy->props.end())
|
||||
if (!localKeys.contains(key))
|
||||
keys.erase(key);
|
||||
}
|
||||
}
|
||||
@ -2222,6 +2243,19 @@ TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||
if (searchPropsAndIndexer(ty, classTy->props, classTy->indexer, properties, ctx))
|
||||
continue; // Indexer was found in this class, so we can move on to the next
|
||||
|
||||
auto parent = classTy->parent;
|
||||
bool foundInParent = false;
|
||||
while (parent && !foundInParent)
|
||||
{
|
||||
auto parentClass = get<ClassType>(follow(*parent));
|
||||
foundInParent = searchPropsAndIndexer(ty, parentClass->props, parentClass->indexer, properties, ctx);
|
||||
parent = parentClass->parent;
|
||||
}
|
||||
|
||||
// we move on to the next type if any of the parents we went through had the property.
|
||||
if (foundInParent)
|
||||
continue;
|
||||
|
||||
// If code reaches here,that means the property not found -> check in the metatable's __index
|
||||
|
||||
// findMetatableEntry demands the ability to emit errors, so we must give it
|
||||
|
@ -619,6 +619,20 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_common_subset_if_union_of_d
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_works_with_parent_classes_too")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type KeysOfMyObject = keyof<ChildClass>
|
||||
|
||||
local function ok(idx: KeysOfMyObject): "BaseField" | "BaseMethod" | "Method" | "Touched" return idx end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "binary_type_function_works_with_default_argument")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
@ -1033,6 +1047,20 @@ TEST_CASE_FIXTURE(ClassFixture, "index_type_function_works_on_classes")
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "index_type_function_works_on_classes_with_parents")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type KeysOfMyObject = index<ChildClass, "BaseField">
|
||||
|
||||
local function ok(idx: KeysOfMyObject): number return idx end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_index_metatables")
|
||||
{
|
||||
if (!FFlag::LuauSolverV2)
|
||||
|
@ -793,9 +793,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail_and_strin
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "string_format_as_method")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::LuauSolverV2)
|
||||
return;
|
||||
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
|
||||
|
||||
CheckResult result = check("local _ = ('%s'):format(5)");
|
||||
|
||||
@ -809,6 +807,7 @@ TEST_CASE_FIXTURE(Fixture, "string_format_as_method")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
|
||||
CheckResult result = check(R"(
|
||||
local _ = ("%s"):format("%d", "hello")
|
||||
)");
|
||||
@ -820,10 +819,7 @@ TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument2")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
|
||||
CheckResult result = check(R"(
|
||||
local _ = ("%s %d").format("%d %s", "A type error", 2)
|
||||
)");
|
||||
@ -882,10 +878,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "debug_info_is_crazy")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "aliased_string_format")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
|
||||
CheckResult result = check(R"(
|
||||
local fmt = string.format
|
||||
local s = fmt("%d", "oops")
|
||||
@ -945,10 +938,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_on_variadic")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_report_all_type_errors_at_correct_positions")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::LuauSolverV2)
|
||||
return;
|
||||
|
||||
ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true};
|
||||
CheckResult result = check(R"(
|
||||
("%s%d%s"):format(1, "hello", true)
|
||||
|
Loading…
Reference in New Issue
Block a user