luau/Analysis/src/Clone.cpp

422 lines
12 KiB
C++
Raw Normal View History

2022-04-07 22:29:01 +01:00
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Clone.h"
#include "Luau/Module.h"
#include "Luau/RecursionCounter.h"
#include "Luau/TypePack.h"
#include "Luau/Unifiable.h"
2022-04-15 00:57:43 +01:00
LUAU_FASTFLAG(DebugLuauCopyBeforeNormalizing)
2022-04-07 22:29:01 +01:00
LUAU_FASTINTVARIABLE(LuauTypeCloneRecursionLimit, 300)
2022-04-15 00:57:43 +01:00
LUAU_FASTFLAG(LuauTypecheckOptPass)
2022-04-21 22:44:27 +01:00
LUAU_FASTFLAGVARIABLE(LuauLosslessClone, false)
2022-04-07 22:29:01 +01:00
namespace Luau
{
namespace
{
struct TypePackCloner;
/*
* Both TypeCloner and TypePackCloner work by depositing the requested type variable into the appropriate 'seen' set.
* They do not return anything because their sole consumer (the deepClone function) already has a pointer into this storage.
*/
struct TypeCloner
{
2022-04-15 00:57:43 +01:00
TypeCloner(TypeArena& dest, TypeId typeId, CloneState& cloneState)
2022-04-07 22:29:01 +01:00
: dest(dest)
, typeId(typeId)
2022-04-15 00:57:43 +01:00
, seenTypes(cloneState.seenTypes)
, seenTypePacks(cloneState.seenTypePacks)
2022-04-07 22:29:01 +01:00
, cloneState(cloneState)
{
}
TypeArena& dest;
TypeId typeId;
SeenTypes& seenTypes;
SeenTypePacks& seenTypePacks;
CloneState& cloneState;
template<typename T>
void defaultClone(const T& t);
void operator()(const Unifiable::Free& t);
void operator()(const Unifiable::Generic& t);
void operator()(const Unifiable::Bound<TypeId>& t);
void operator()(const Unifiable::Error& t);
void operator()(const PrimitiveTypeVar& t);
2022-04-15 00:57:43 +01:00
void operator()(const ConstrainedTypeVar& t);
2022-04-07 22:29:01 +01:00
void operator()(const SingletonTypeVar& t);
void operator()(const FunctionTypeVar& t);
void operator()(const TableTypeVar& t);
void operator()(const MetatableTypeVar& t);
void operator()(const ClassTypeVar& t);
void operator()(const AnyTypeVar& t);
void operator()(const UnionTypeVar& t);
void operator()(const IntersectionTypeVar& t);
void operator()(const LazyTypeVar& t);
};
struct TypePackCloner
{
TypeArena& dest;
TypePackId typePackId;
SeenTypes& seenTypes;
SeenTypePacks& seenTypePacks;
CloneState& cloneState;
2022-04-15 00:57:43 +01:00
TypePackCloner(TypeArena& dest, TypePackId typePackId, CloneState& cloneState)
2022-04-07 22:29:01 +01:00
: dest(dest)
, typePackId(typePackId)
2022-04-15 00:57:43 +01:00
, seenTypes(cloneState.seenTypes)
, seenTypePacks(cloneState.seenTypePacks)
2022-04-07 22:29:01 +01:00
, cloneState(cloneState)
{
}
template<typename T>
void defaultClone(const T& t)
{
TypePackId cloned = dest.addTypePack(TypePackVar{t});
seenTypePacks[typePackId] = cloned;
}
void operator()(const Unifiable::Free& t)
{
2022-04-21 22:44:27 +01:00
if (FFlag::LuauLosslessClone)
{
defaultClone(t);
}
else
{
cloneState.encounteredFreeType = true;
TypePackId err = getSingletonTypes().errorRecoveryTypePack(getSingletonTypes().anyTypePack);
TypePackId cloned = dest.addTypePack(*err);
seenTypePacks[typePackId] = cloned;
}
2022-04-07 22:29:01 +01:00
}
void operator()(const Unifiable::Generic& t)
{
defaultClone(t);
}
void operator()(const Unifiable::Error& t)
{
defaultClone(t);
}
// While we are a-cloning, we can flatten out bound TypeVars and make things a bit tighter.
// We just need to be sure that we rewrite pointers both to the binder and the bindee to the same pointer.
void operator()(const Unifiable::Bound<TypePackId>& t)
{
2022-04-15 00:57:43 +01:00
TypePackId cloned = clone(t.boundTo, dest, cloneState);
if (FFlag::DebugLuauCopyBeforeNormalizing)
cloned = dest.addTypePack(TypePackVar{BoundTypePack{cloned}});
2022-04-07 22:29:01 +01:00
seenTypePacks[typePackId] = cloned;
}
void operator()(const VariadicTypePack& t)
{
2022-04-15 00:57:43 +01:00
TypePackId cloned = dest.addTypePack(TypePackVar{VariadicTypePack{clone(t.ty, dest, cloneState), /*hidden*/ t.hidden}});
2022-04-07 22:29:01 +01:00
seenTypePacks[typePackId] = cloned;
}
void operator()(const TypePack& t)
{
TypePackId cloned = dest.addTypePack(TypePack{});
TypePack* destTp = getMutable<TypePack>(cloned);
LUAU_ASSERT(destTp != nullptr);
seenTypePacks[typePackId] = cloned;
for (TypeId ty : t.head)
2022-04-15 00:57:43 +01:00
destTp->head.push_back(clone(ty, dest, cloneState));
2022-04-07 22:29:01 +01:00
if (t.tail)
2022-04-15 00:57:43 +01:00
destTp->tail = clone(*t.tail, dest, cloneState);
2022-04-07 22:29:01 +01:00
}
};
template<typename T>
void TypeCloner::defaultClone(const T& t)
{
TypeId cloned = dest.addType(t);
seenTypes[typeId] = cloned;
}
void TypeCloner::operator()(const Unifiable::Free& t)
{
2022-04-21 22:44:27 +01:00
if (FFlag::LuauLosslessClone)
{
defaultClone(t);
}
else
{
cloneState.encounteredFreeType = true;
TypeId err = getSingletonTypes().errorRecoveryType(getSingletonTypes().anyType);
TypeId cloned = dest.addType(*err);
seenTypes[typeId] = cloned;
}
2022-04-07 22:29:01 +01:00
}
void TypeCloner::operator()(const Unifiable::Generic& t)
{
defaultClone(t);
}
void TypeCloner::operator()(const Unifiable::Bound<TypeId>& t)
{
2022-04-15 00:57:43 +01:00
TypeId boundTo = clone(t.boundTo, dest, cloneState);
if (FFlag::DebugLuauCopyBeforeNormalizing)
boundTo = dest.addType(BoundTypeVar{boundTo});
2022-04-07 22:29:01 +01:00
seenTypes[typeId] = boundTo;
}
void TypeCloner::operator()(const Unifiable::Error& t)
{
defaultClone(t);
}
void TypeCloner::operator()(const PrimitiveTypeVar& t)
{
defaultClone(t);
}
2022-04-15 00:57:43 +01:00
void TypeCloner::operator()(const ConstrainedTypeVar& t)
{
2022-04-21 22:44:27 +01:00
if (!FFlag::LuauLosslessClone)
cloneState.encounteredFreeType = true;
2022-04-15 00:57:43 +01:00
TypeId res = dest.addType(ConstrainedTypeVar{t.level});
ConstrainedTypeVar* ctv = getMutable<ConstrainedTypeVar>(res);
LUAU_ASSERT(ctv);
seenTypes[typeId] = res;
std::vector<TypeId> parts;
for (TypeId part : t.parts)
parts.push_back(clone(part, dest, cloneState));
ctv->parts = std::move(parts);
}
2022-04-07 22:29:01 +01:00
void TypeCloner::operator()(const SingletonTypeVar& t)
{
defaultClone(t);
}
void TypeCloner::operator()(const FunctionTypeVar& t)
{
TypeId result = dest.addType(FunctionTypeVar{TypeLevel{0, 0}, {}, {}, nullptr, nullptr, t.definition, t.hasSelf});
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(result);
LUAU_ASSERT(ftv != nullptr);
seenTypes[typeId] = result;
for (TypeId generic : t.generics)
2022-04-15 00:57:43 +01:00
ftv->generics.push_back(clone(generic, dest, cloneState));
2022-04-07 22:29:01 +01:00
for (TypePackId genericPack : t.genericPacks)
2022-04-15 00:57:43 +01:00
ftv->genericPacks.push_back(clone(genericPack, dest, cloneState));
2022-04-07 22:29:01 +01:00
ftv->tags = t.tags;
2022-04-15 00:57:43 +01:00
ftv->argTypes = clone(t.argTypes, dest, cloneState);
2022-04-07 22:29:01 +01:00
ftv->argNames = t.argNames;
2022-04-15 00:57:43 +01:00
ftv->retType = clone(t.retType, dest, cloneState);
if (FFlag::LuauTypecheckOptPass)
ftv->hasNoGenerics = t.hasNoGenerics;
2022-04-07 22:29:01 +01:00
}
void TypeCloner::operator()(const TableTypeVar& t)
{
// If table is now bound to another one, we ignore the content of the original
2022-04-15 00:57:43 +01:00
if (!FFlag::DebugLuauCopyBeforeNormalizing && t.boundTo)
2022-04-07 22:29:01 +01:00
{
2022-04-15 00:57:43 +01:00
TypeId boundTo = clone(*t.boundTo, dest, cloneState);
2022-04-07 22:29:01 +01:00
seenTypes[typeId] = boundTo;
return;
}
TypeId result = dest.addType(TableTypeVar{});
TableTypeVar* ttv = getMutable<TableTypeVar>(result);
LUAU_ASSERT(ttv != nullptr);
*ttv = t;
seenTypes[typeId] = result;
ttv->level = TypeLevel{0, 0};
2022-04-15 00:57:43 +01:00
if (FFlag::DebugLuauCopyBeforeNormalizing && t.boundTo)
ttv->boundTo = clone(*t.boundTo, dest, cloneState);
2022-04-07 22:29:01 +01:00
for (const auto& [name, prop] : t.props)
2022-04-15 00:57:43 +01:00
ttv->props[name] = {clone(prop.type, dest, cloneState), prop.deprecated, {}, prop.location, prop.tags};
2022-04-07 22:29:01 +01:00
if (t.indexer)
2022-04-15 00:57:43 +01:00
ttv->indexer = TableIndexer{clone(t.indexer->indexType, dest, cloneState), clone(t.indexer->indexResultType, dest, cloneState)};
2022-04-07 22:29:01 +01:00
for (TypeId& arg : ttv->instantiatedTypeParams)
2022-04-15 00:57:43 +01:00
arg = clone(arg, dest, cloneState);
2022-04-07 22:29:01 +01:00
for (TypePackId& arg : ttv->instantiatedTypePackParams)
2022-04-15 00:57:43 +01:00
arg = clone(arg, dest, cloneState);
2022-04-07 22:29:01 +01:00
2022-04-21 22:44:27 +01:00
if (!FFlag::LuauLosslessClone && ttv->state == TableState::Free)
2022-04-07 22:29:01 +01:00
{
cloneState.encounteredFreeType = true;
ttv->state = TableState::Sealed;
}
ttv->definitionModuleName = t.definitionModuleName;
ttv->methodDefinitionLocations = t.methodDefinitionLocations;
ttv->tags = t.tags;
}
void TypeCloner::operator()(const MetatableTypeVar& t)
{
TypeId result = dest.addType(MetatableTypeVar{});
MetatableTypeVar* mtv = getMutable<MetatableTypeVar>(result);
seenTypes[typeId] = result;
2022-04-15 00:57:43 +01:00
mtv->table = clone(t.table, dest, cloneState);
mtv->metatable = clone(t.metatable, dest, cloneState);
2022-04-07 22:29:01 +01:00
}
void TypeCloner::operator()(const ClassTypeVar& t)
{
2022-04-21 22:44:27 +01:00
TypeId result = dest.addType(ClassTypeVar{t.name, {}, std::nullopt, std::nullopt, t.tags, t.userData, t.definitionModuleName});
2022-04-07 22:29:01 +01:00
ClassTypeVar* ctv = getMutable<ClassTypeVar>(result);
seenTypes[typeId] = result;
for (const auto& [name, prop] : t.props)
2022-04-15 00:57:43 +01:00
ctv->props[name] = {clone(prop.type, dest, cloneState), prop.deprecated, {}, prop.location, prop.tags};
2022-04-07 22:29:01 +01:00
if (t.parent)
2022-04-15 00:57:43 +01:00
ctv->parent = clone(*t.parent, dest, cloneState);
2022-04-07 22:29:01 +01:00
if (t.metatable)
2022-04-15 00:57:43 +01:00
ctv->metatable = clone(*t.metatable, dest, cloneState);
2022-04-07 22:29:01 +01:00
}
void TypeCloner::operator()(const AnyTypeVar& t)
{
defaultClone(t);
}
void TypeCloner::operator()(const UnionTypeVar& t)
{
std::vector<TypeId> options;
options.reserve(t.options.size());
for (TypeId ty : t.options)
2022-04-15 00:57:43 +01:00
options.push_back(clone(ty, dest, cloneState));
2022-04-07 22:29:01 +01:00
TypeId result = dest.addType(UnionTypeVar{std::move(options)});
seenTypes[typeId] = result;
}
void TypeCloner::operator()(const IntersectionTypeVar& t)
{
TypeId result = dest.addType(IntersectionTypeVar{});
seenTypes[typeId] = result;
IntersectionTypeVar* option = getMutable<IntersectionTypeVar>(result);
LUAU_ASSERT(option != nullptr);
for (TypeId ty : t.parts)
2022-04-15 00:57:43 +01:00
option->parts.push_back(clone(ty, dest, cloneState));
2022-04-07 22:29:01 +01:00
}
void TypeCloner::operator()(const LazyTypeVar& t)
{
defaultClone(t);
}
} // anonymous namespace
2022-04-15 00:57:43 +01:00
TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState)
2022-04-07 22:29:01 +01:00
{
if (tp->persistent)
return tp;
2022-04-15 00:57:43 +01:00
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit, "cloning TypePackId");
2022-04-07 22:29:01 +01:00
2022-04-15 00:57:43 +01:00
TypePackId& res = cloneState.seenTypePacks[tp];
2022-04-07 22:29:01 +01:00
if (res == nullptr)
{
2022-04-15 00:57:43 +01:00
TypePackCloner cloner{dest, tp, cloneState};
2022-04-07 22:29:01 +01:00
Luau::visit(cloner, tp->ty); // Mutates the storage that 'res' points into.
}
return res;
}
2022-04-15 00:57:43 +01:00
TypeId clone(TypeId typeId, TypeArena& dest, CloneState& cloneState)
2022-04-07 22:29:01 +01:00
{
if (typeId->persistent)
return typeId;
2022-04-15 00:57:43 +01:00
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit, "cloning TypeId");
2022-04-07 22:29:01 +01:00
2022-04-15 00:57:43 +01:00
TypeId& res = cloneState.seenTypes[typeId];
2022-04-07 22:29:01 +01:00
if (res == nullptr)
{
2022-04-15 00:57:43 +01:00
TypeCloner cloner{dest, typeId, cloneState};
2022-04-07 22:29:01 +01:00
Luau::visit(cloner, typeId->ty); // Mutates the storage that 'res' points into.
// Persistent types are not being cloned and we get the original type back which might be read-only
if (!res->persistent)
2022-04-21 22:44:27 +01:00
{
2022-04-07 22:29:01 +01:00
asMutable(res)->documentationSymbol = typeId->documentationSymbol;
2022-04-21 22:44:27 +01:00
asMutable(res)->normal = typeId->normal;
}
2022-04-07 22:29:01 +01:00
}
return res;
}
2022-04-15 00:57:43 +01:00
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState)
2022-04-07 22:29:01 +01:00
{
TypeFun result;
for (auto param : typeFun.typeParams)
{
2022-04-15 00:57:43 +01:00
TypeId ty = clone(param.ty, dest, cloneState);
2022-04-07 22:29:01 +01:00
std::optional<TypeId> defaultValue;
if (param.defaultValue)
2022-04-15 00:57:43 +01:00
defaultValue = clone(*param.defaultValue, dest, cloneState);
2022-04-07 22:29:01 +01:00
result.typeParams.push_back({ty, defaultValue});
}
for (auto param : typeFun.typePackParams)
{
2022-04-15 00:57:43 +01:00
TypePackId tp = clone(param.tp, dest, cloneState);
2022-04-07 22:29:01 +01:00
std::optional<TypePackId> defaultValue;
if (param.defaultValue)
2022-04-15 00:57:43 +01:00
defaultValue = clone(*param.defaultValue, dest, cloneState);
2022-04-07 22:29:01 +01:00
result.typePackParams.push_back({tp, defaultValue});
}
2022-04-15 00:57:43 +01:00
result.type = clone(typeFun.type, dest, cloneState);
2022-04-07 22:29:01 +01:00
return result;
}
} // namespace Luau