#include "Luau/Instantiation.h"
#include "Luau/Clone.h"
#include "Luau/Common.h"
#include "Luau/Instantiation2.h"
#include "Luau/ToString.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeArena.h"
#include "Luau/TypeCheckLimits.h"
#include <algorithm>
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauReplacerRespectsReboundGenerics)
LUAU_FASTFLAGVARIABLE(LuauReplacerIsSolverAgnostic)
namespace Luau
{
void Instantiation::resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
{
Substitution::resetState(log, arena);
this->builtinTypes = builtinTypes;
this->level = level;
this->scope = scope;
}
bool Instantiation::isDirty(TypeId ty)
{
if (const FunctionType* ftv = log->getMutable<FunctionType>(ty))
{
if (ftv->hasNoFreeOrGenericTypes)
return false;
return true;
}
else
{
return false;
}
}
bool Instantiation::isDirty(TypePackId tp)
{
return false;
}
bool Instantiation::ignoreChildren(TypeId ty)
{
if (log->getMutable<FunctionType>(ty))
return true;
else if (get<ExternType>(ty))
return true;
else
return false;
}
TypeId Instantiation::clean(TypeId ty)
{
const FunctionType* ftv = log->getMutable<FunctionType>(ty);
LUAU_ASSERT(ftv);
FunctionType clone = FunctionType{level, ftv->argTypes, ftv->retTypes, ftv->definition, ftv->hasSelf};
clone.magic = ftv->magic;
clone.tags = ftv->tags;
clone.argNames = ftv->argNames;
clone.isDeprecatedFunction = ftv->isDeprecatedFunction;
clone.deprecatedInfo = ftv->deprecatedInfo;
TypeId result = addType(std::move(clone));
reusableReplaceGenerics.resetState(log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks);
result = reusableReplaceGenerics.substitute(result).value_or(result);
asMutable(result)->documentationSymbol = ty->documentationSymbol;
return result;
}
TypePackId Instantiation::clean(TypePackId tp)
{
LUAU_ASSERT(false);
return tp;
}
void ReplaceGenerics::resetState(
const TxnLog* log,
TypeArena* arena,
NotNull<BuiltinTypes> builtinTypes,
TypeLevel level,
Scope* scope,
const std::vector<TypeId>& generics,
const std::vector<TypePackId>& genericPacks
)
{
Substitution::resetState(log, arena);
this->builtinTypes = builtinTypes;
this->level = level;
this->scope = scope;
this->generics = generics;
this->genericPacks = genericPacks;
}
bool ReplaceGenerics::ignoreChildren(TypeId ty)
{
if (const FunctionType* ftv = log->getMutable<FunctionType>(ty))
{
if (ftv->hasNoFreeOrGenericTypes)
return true;
return (!generics.empty() || !genericPacks.empty()) && (ftv->generics == generics) && (ftv->genericPacks == genericPacks);
}
else if (get<ExternType>(ty))
return true;
else
{
return false;
}
}
bool ReplaceGenerics::isDirty(TypeId ty)
{
if (const TableType* ttv = log->getMutable<TableType>(ty))
return ttv->state == TableState::Generic;
else if (log->getMutable<GenericType>(ty))
return std::find(generics.begin(), generics.end(), ty) != generics.end();
else
return false;
}
bool ReplaceGenerics::isDirty(TypePackId tp)
{
if (log->getMutable<GenericTypePack>(tp))
return std::find(genericPacks.begin(), genericPacks.end(), tp) != genericPacks.end();
else
return false;
}
TypeId ReplaceGenerics::clean(TypeId ty)
{
LUAU_ASSERT(isDirty(ty));
if (FFlag::LuauReplacerIsSolverAgnostic)
{
if (const TableType* ttv = log->getMutable<TableType>(ty))
{
TableType clone = TableType{ttv->props, ttv->indexer, level, scope, TableState::Free};
clone.definitionModuleName = ttv->definitionModuleName;
clone.definitionLocation = ttv->definitionLocation;
return addType(std::move(clone));
}
else
return arena->freshType(builtinTypes, scope, level);
}
else
{
if (const TableType* ttv = log->getMutable<TableType>(ty))
{
TableType clone = TableType{ttv->props, ttv->indexer, level, scope, TableState::Free};
clone.definitionModuleName = ttv->definitionModuleName;
clone.definitionLocation = ttv->definitionLocation;
return addType(std::move(clone));
}
else if (FFlag::LuauSolverV2)
{
TypeId res = freshType(NotNull{arena}, builtinTypes, scope);
getMutable<FreeType>(res)->level = level;
return res;
}
else
{
return arena->freshType(builtinTypes, scope, level);
}
}
}
TypePackId ReplaceGenerics::clean(TypePackId tp)
{
LUAU_ASSERT(isDirty(tp));
return addTypePack(TypePackVar(FreeTypePack{scope, level}));
}
std::optional<TypeId> instantiate(
NotNull<BuiltinTypes> builtinTypes,
NotNull<TypeArena> arena,
NotNull<TypeCheckLimits> limits,
NotNull<Scope> scope,
TypeId ty
)
{
ty = follow(ty);
const FunctionType* ft = get<FunctionType>(ty);
if (!ft)
return ty;
if (ft->generics.empty() && ft->genericPacks.empty())
return ty;
DenseHashMap<TypeId, TypeId> replacements{nullptr};
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
for (TypeId g : ft->generics)
replacements[g] = freshType(arena, builtinTypes, scope);
for (TypePackId g : ft->genericPacks)
replacementPacks[g] = arena->freshTypePack(scope);
if (FFlag::LuauReplacerRespectsReboundGenerics)
{
Replacer r{arena, NotNull{&replacements}, NotNull{&replacementPacks}};
if (limits->instantiationChildLimit)
r.childLimit = *limits->instantiationChildLimit;
CloneState cs{builtinTypes};
auto clonedFunctionTypeId = shallowClone(ty, *arena, cs, true);
FunctionType* ft2 = getMutable<FunctionType>(clonedFunctionTypeId);
LUAU_ASSERT(ft != ft2);
ft2->generics.clear();
ft2->genericPacks.clear();
return r.substitute(clonedFunctionTypeId);
}
else
{
Replacer_DEPRECATED r{arena, std::move(replacements), std::move(replacementPacks)};
if (limits->instantiationChildLimit)
r.childLimit = *limits->instantiationChildLimit;
std::optional<TypeId> res = r.substitute(ty);
if (!res)
return res;
FunctionType* ft2 = getMutable<FunctionType>(*res);
LUAU_ASSERT(ft != ft2);
ft2->generics.clear();
ft2->genericPacks.clear();
return res;
}
}
}