luau/Analysis/src/InferPolarity.cpp
vegorov-rbx 68cdcc4a3a
Sync to upstream/release/677 (#1872)
# What's Changed?

This week comes with many improvements to the new type solver and an important fix to the garbage collection to make it more robust in memory constrained scenarios.

# Runtime
- Garbage collection will no longer run out of memory itself, which could have happened when resizing arrays to a smaller size

# New Type Solver
- Type refinements on external types should now work and should no longer normalize the type into `never`
- Improved error reporting when `string.format` is used with a dynamic format string
- Updated type signature of `getmetatable` library function to use the corresponding type function and produce better type inference
- Restored a type mismatch error when converting function types with different number of generic parameters, like `() -> ()` into `<T>() -> ()`
- Types resulting from compound assignments have been simplified, reducing cyclic type introduction and inference failures
- Fixed function generic types leaking into tables during bidirectional type inference (Fixes #1808 and #1821 )
- Stability and performance improvements (Fixes #1860 )

# Internal Contributors

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Sora Kanosue <skanosue@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2025-06-06 11:52:47 -07:00

169 lines
4.1 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/DenseHash.h"
#include "Luau/Polarity.h"
#include "Luau/Scope.h"
#include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauEagerGeneralization3)
namespace Luau
{
struct InferPolarity : TypeVisitor
{
NotNull<TypeArena> arena;
NotNull<Scope> scope;
DenseHashMap<TypeId, Polarity> types{nullptr};
DenseHashMap<TypePackId, Polarity> packs{nullptr};
Polarity polarity = Polarity::Positive;
explicit InferPolarity(NotNull<TypeArena> arena, NotNull<Scope> scope)
: arena(arena)
, scope(scope)
{
}
void flip()
{
polarity = invert(polarity);
}
bool visit(TypeId ty, const GenericType& gt) override
{
if (ty->owningArena != arena)
return false;
if (subsumes(scope, gt.scope))
types[ty] |= polarity;
return false;
}
bool visit(TypeId ty, const TableType& tt) override
{
if (ty->owningArena != arena)
return false;
const Polarity p = polarity;
for (const auto& [name, prop] : tt.props)
{
if (prop.isShared())
{
polarity = Polarity::Mixed;
traverse(prop.type());
}
else if (prop.isReadOnly())
{
polarity = p;
traverse(*prop.readTy);
}
else if (prop.isWriteOnly())
{
polarity = invert(p);
traverse(*prop.writeTy);
}
else
LUAU_ASSERT(!"Unreachable");
}
if (tt.indexer)
{
polarity = Polarity::Mixed;
traverse(tt.indexer->indexType);
traverse(tt.indexer->indexResultType);
}
polarity = p;
return false;
}
bool visit(TypeId ty, const FunctionType& ft) override
{
if (ty->owningArena != arena)
return false;
const Polarity p = polarity;
polarity = Polarity::Positive;
// If these types actually occur within the function signature, their
// polarity will be overwritten. If not, we infer that they are phantom
// types.
for (TypeId generic : ft.generics)
{
generic = follow(generic);
const auto gen = get<GenericType>(generic);
if (gen && subsumes(scope, gen->scope))
types[generic] = Polarity::None;
}
for (TypePackId genericPack : ft.genericPacks)
{
genericPack = follow(genericPack);
const auto gen = get<GenericTypePack>(genericPack);
if (gen && subsumes(scope, gen->scope))
packs[genericPack] = Polarity::None;
}
flip();
traverse(ft.argTypes);
flip();
traverse(ft.retTypes);
polarity = p;
return false;
}
bool visit(TypeId, const ExternType&) override
{
return false;
}
bool visit(TypePackId tp, const GenericTypePack& gtp) override
{
packs[tp] |= polarity;
return false;
}
};
template<typename TID>
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
{
if (!FFlag::LuauEagerGeneralization3)
return;
InferPolarity infer{arena, scope};
infer.traverse(ty);
for (const auto& [ty, polarity] : infer.types)
{
auto gt = getMutable<GenericType>(ty);
LUAU_ASSERT(gt);
gt->polarity = polarity;
}
for (const auto& [tp, polarity] : infer.packs)
{
if (tp->owningArena != arena)
continue;
auto gp = getMutable<GenericTypePack>(tp);
LUAU_ASSERT(gp);
gp->polarity = polarity;
}
}
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypeId ty)
{
inferGenericPolarities_(arena, scope, ty);
}
void inferGenericPolarities(NotNull<TypeArena> arena, NotNull<Scope> scope, TypePackId tp)
{
inferGenericPolarities_(arena, scope, tp);
}
} // namespace Luau