luau/Analysis/include/Luau/Instantiation.h
ayoungbloodrbx 66202dc4ac
Sync to upstream/release/684 (#1930)
## General
- Support AstStatDeclareGlobal output as a source string (via
@karl-police in #1889)
- Luau heap dump correctly reports the size of a string, now including
overhead for the string type
- Prevent yields from Luau `xpcall` error handling function.
 
## Analysis
- Avoid exponential blowup when normalizing union of normalized free
variables.
- Fix type pack-related bugs that caused infinite recursion when:
  - A generic type pack was bound to itself during subtyping.
- In type pack flattening, when that same generic type pack was now
being bound another generic type pack which contained it.
- Properly simplify `any & (*error-type* | string)` to `*error-type* |
*error-type* | string` instead of hanging due to creating a huge union
type.

---

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: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
2025-07-25 15:33:42 -07:00

170 lines
4.5 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/NotNull.h"
#include "Luau/Substitution.h"
#include "Luau/TypeFwd.h"
#include "Luau/Unifiable.h"
#include "Luau/VisitType.h"
namespace Luau
{
struct TxnLog;
struct TypeArena;
struct TypeCheckLimits;
// A substitution which replaces generic types in a given set by free types.
struct ReplaceGenerics : Substitution
{
ReplaceGenerics(
const TxnLog* log,
TypeArena* arena,
NotNull<BuiltinTypes> builtinTypes,
TypeLevel level,
Scope* scope,
const std::vector<TypeId>& generics,
const std::vector<TypePackId>& genericPacks
)
: Substitution(log, arena)
, builtinTypes(builtinTypes)
, level(level)
, scope(scope)
, generics(generics)
, genericPacks(genericPacks)
{
}
void resetState(
const TxnLog* log,
TypeArena* arena,
NotNull<BuiltinTypes> builtinTypes,
TypeLevel level,
Scope* scope,
const std::vector<TypeId>& generics,
const std::vector<TypePackId>& genericPacks
);
NotNull<BuiltinTypes> builtinTypes;
TypeLevel level;
Scope* scope;
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
// A substitution which replaces generic functions by monomorphic functions
struct Instantiation final : Substitution
{
Instantiation(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
: Substitution(log, arena)
, builtinTypes(builtinTypes)
, level(level)
, scope(scope)
, reusableReplaceGenerics(log, arena, builtinTypes, level, scope, {}, {})
{
}
void resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope);
NotNull<BuiltinTypes> builtinTypes;
TypeLevel level;
Scope* scope;
ReplaceGenerics reusableReplaceGenerics;
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
// Used to find if a FunctionType requires generic type cleanup during instantiation
struct GenericTypeFinder : TypeOnceVisitor
{
bool found = false;
GenericTypeFinder()
: TypeOnceVisitor("GenericTypeFinder")
{
}
bool visit(TypeId ty) override
{
return !found;
}
bool visit(TypePackId ty) override
{
return !found;
}
bool visit(TypeId ty, const Luau::FunctionType& ftv) override
{
if (ftv.hasNoFreeOrGenericTypes)
return false;
if (!ftv.generics.empty() || !ftv.genericPacks.empty())
found = true;
return !found;
}
bool visit(TypeId ty, const Luau::TableType& ttv) override
{
if (ttv.state == Luau::TableState::Generic)
found = true;
return !found;
}
bool visit(TypeId ty, const Luau::GenericType&) override
{
found = true;
return false;
}
bool visit(TypePackId ty, const Luau::GenericTypePack&) override
{
found = true;
return false;
}
bool visit(TypeId ty, const Luau::ExternType&) override
{
// During function instantiation, extern types are not traversed even if they have generics
return false;
}
};
/** Attempt to instantiate a type. Only used under local type inference.
*
* When given a generic function type, instantiate() will return a copy with the
* generics replaced by fresh types. Instantiation will return the same TypeId
* back if the function does not have any generics.
*
* All higher order generics are left as-is. For example, instantiation of
* <X>(<Y>(Y) -> (X, Y)) -> (X, Y) is (<Y>(Y) -> ('x, Y)) -> ('x, Y)
*
* We substitute the generic X for the free 'x, but leave the generic Y alone.
*
* Instantiation fails only when processing the type causes internal recursion
* limits to be exceeded.
*/
std::optional<TypeId> instantiate(
NotNull<BuiltinTypes> builtinTypes,
NotNull<TypeArena> arena,
NotNull<TypeCheckLimits> limits,
NotNull<Scope> scope,
TypeId ty
);
} // namespace Luau