mirror of
https://github.com/luau-lang/luau.git
synced 2025-08-26 11:27:08 +01:00
# What's Changed? We've been hard at work fixing bugs and introducing new features! ## VM * Include constant-folding information in Luau cost model for inlining and loop unrolling * ~1% improvement in compile times ## New Type Solver * `Luau::shallowClone`'s last argument, whether to clone persistent (builtin) types, is now non-optional. * Refinements on properties of tables are now computed with a `read` table property. This resolves some issues around refining table properies and then trying to set them. Fixes #1344. Fixes #1651. ``` if foo.bar then -- Prior to this release, this would be `typeof(foo) & { bar: ~(false?) } -- Now, this is `typeof(foo) & { read bar: ~(false?) } end ``` * The type function `keyof` should respect the empty string as a property, as in: ``` -- equivalent to type Foo ="" type Foo = keyof<{ [""]: number }> ``` * Descend into literals to report subtyping errors for function calls: this both improves bidirectional inference and makes errors more specific. Before, the error reporting for a table with incorrect members passed to a function would cite the entire table, but now it only cites the members that are incorrectly typed. * Fixes a case where intersecting two tables without any common properties would create `never`, instead of a table with both of their properties. # Internal Contributors Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: James McNellis <jmcnellis@roblox.com> Co-authored-by: Sora Kanosue <skanosue@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@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: Alexander Youngblood <ayoungblood@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>
739 lines
18 KiB
C++
739 lines
18 KiB
C++
// 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/Common.h"
|
|
#include "Luau/NotNull.h"
|
|
#include "Luau/Type.h"
|
|
#include "Luau/TypePack.h"
|
|
#include "Luau/Unifiable.h"
|
|
#include "Luau/VisitType.h"
|
|
|
|
LUAU_FASTFLAG(LuauSolverV2)
|
|
|
|
// For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit.
|
|
LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
|
|
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticClone)
|
|
|
|
namespace Luau
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
using Kind = Variant<TypeId, TypePackId>;
|
|
|
|
template<typename T>
|
|
const T* get(const Kind& kind)
|
|
{
|
|
return get_if<T>(&kind);
|
|
}
|
|
|
|
class TypeCloner
|
|
{
|
|
|
|
protected:
|
|
NotNull<TypeArena> arena;
|
|
NotNull<BuiltinTypes> builtinTypes;
|
|
|
|
// A queue of kinds where we cloned it, but whose interior types hasn't
|
|
// been updated to point to new clones. Once all of its interior types
|
|
// has been updated, it gets removed from the queue.
|
|
std::vector<Kind> queue;
|
|
|
|
NotNull<SeenTypes> types;
|
|
NotNull<SeenTypePacks> packs;
|
|
|
|
TypeId forceTy = nullptr;
|
|
TypePackId forceTp = nullptr;
|
|
|
|
int steps = 0;
|
|
|
|
public:
|
|
TypeCloner(
|
|
NotNull<TypeArena> arena,
|
|
NotNull<BuiltinTypes> builtinTypes,
|
|
NotNull<SeenTypes> types,
|
|
NotNull<SeenTypePacks> packs,
|
|
TypeId forceTy,
|
|
TypePackId forceTp
|
|
)
|
|
: arena(arena)
|
|
, builtinTypes(builtinTypes)
|
|
, types(types)
|
|
, packs(packs)
|
|
, forceTy(forceTy)
|
|
, forceTp(forceTp)
|
|
{
|
|
}
|
|
|
|
virtual ~TypeCloner() = default;
|
|
|
|
TypeId clone(TypeId ty)
|
|
{
|
|
shallowClone(ty);
|
|
run();
|
|
|
|
if (hasExceededIterationLimit())
|
|
{
|
|
TypeId error = builtinTypes->errorType;
|
|
(*types)[ty] = error;
|
|
return error;
|
|
}
|
|
|
|
return find(ty).value_or(builtinTypes->errorType);
|
|
}
|
|
|
|
TypePackId clone(TypePackId tp)
|
|
{
|
|
shallowClone(tp);
|
|
run();
|
|
|
|
if (hasExceededIterationLimit())
|
|
{
|
|
TypePackId error = builtinTypes->errorTypePack;
|
|
(*packs)[tp] = error;
|
|
return error;
|
|
}
|
|
|
|
return find(tp).value_or(builtinTypes->errorTypePack);
|
|
}
|
|
|
|
private:
|
|
bool hasExceededIterationLimit() const
|
|
{
|
|
if (FInt::LuauTypeCloneIterationLimit == 0)
|
|
return false;
|
|
|
|
return steps + queue.size() >= size_t(FInt::LuauTypeCloneIterationLimit);
|
|
}
|
|
|
|
void run()
|
|
{
|
|
while (!queue.empty())
|
|
{
|
|
++steps;
|
|
|
|
if (hasExceededIterationLimit())
|
|
break;
|
|
|
|
Kind kind = queue.back();
|
|
queue.pop_back();
|
|
|
|
if (find(kind))
|
|
continue;
|
|
|
|
cloneChildren(kind);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
std::optional<TypeId> find(TypeId ty) const
|
|
{
|
|
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
|
|
if (auto it = types->find(ty); it != types->end())
|
|
return it->second;
|
|
else if (ty->persistent && ty != forceTy)
|
|
return ty;
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<TypePackId> find(TypePackId tp) const
|
|
{
|
|
tp = follow(tp);
|
|
if (auto it = packs->find(tp); it != packs->end())
|
|
return it->second;
|
|
else if (tp->persistent && tp != forceTp)
|
|
return tp;
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<Kind> find(Kind kind) const
|
|
{
|
|
if (auto ty = get<TypeId>(kind))
|
|
return find(*ty);
|
|
else if (auto tp = get<TypePackId>(kind))
|
|
return find(*tp);
|
|
else
|
|
{
|
|
LUAU_ASSERT(!"Unknown kind?");
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
public:
|
|
virtual TypeId shallowClone(TypeId ty)
|
|
{
|
|
// We want to [`Luau::follow`] but without forcing the expansion of [`LazyType`]s.
|
|
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
|
|
|
|
if (auto clone = find(ty))
|
|
return *clone;
|
|
else if (ty->persistent && ty != forceTy)
|
|
return ty;
|
|
|
|
TypeId target = arena->addType(ty->ty);
|
|
asMutable(target)->documentationSymbol = ty->documentationSymbol;
|
|
|
|
if (auto generic = getMutable<GenericType>(target))
|
|
generic->scope = nullptr;
|
|
else if (auto free = getMutable<FreeType>(target))
|
|
free->scope = nullptr;
|
|
else if (auto table = getMutable<TableType>(target))
|
|
table->scope = nullptr;
|
|
|
|
(*types)[ty] = target;
|
|
queue.push_back(target);
|
|
return target;
|
|
}
|
|
|
|
virtual TypePackId shallowClone(TypePackId tp)
|
|
{
|
|
tp = follow(tp);
|
|
|
|
if (auto clone = find(tp))
|
|
return *clone;
|
|
else if (tp->persistent && tp != forceTp)
|
|
return tp;
|
|
|
|
TypePackId target = arena->addTypePack(tp->ty);
|
|
|
|
if (auto generic = getMutable<GenericTypePack>(target))
|
|
generic->scope = nullptr;
|
|
else if (auto free = getMutable<FreeTypePack>(target))
|
|
free->scope = nullptr;
|
|
|
|
(*packs)[tp] = target;
|
|
queue.push_back(target);
|
|
return target;
|
|
}
|
|
|
|
private:
|
|
Property shallowClone(const Property& p)
|
|
{
|
|
if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone)
|
|
{
|
|
std::optional<TypeId> cloneReadTy;
|
|
if (auto ty = p.readTy)
|
|
cloneReadTy = shallowClone(*ty);
|
|
|
|
std::optional<TypeId> cloneWriteTy;
|
|
if (auto ty = p.writeTy)
|
|
cloneWriteTy = shallowClone(*ty);
|
|
|
|
Property cloned = Property::create(cloneReadTy, cloneWriteTy);
|
|
cloned.deprecated = p.deprecated;
|
|
cloned.deprecatedSuggestion = p.deprecatedSuggestion;
|
|
cloned.location = p.location;
|
|
cloned.tags = p.tags;
|
|
cloned.documentationSymbol = p.documentationSymbol;
|
|
cloned.typeLocation = p.typeLocation;
|
|
return cloned;
|
|
}
|
|
else
|
|
{
|
|
return Property{
|
|
shallowClone(p.type_DEPRECATED()),
|
|
p.deprecated,
|
|
p.deprecatedSuggestion,
|
|
p.location,
|
|
p.tags,
|
|
p.documentationSymbol,
|
|
p.typeLocation,
|
|
};
|
|
}
|
|
}
|
|
|
|
void cloneChildren(TypeId ty)
|
|
{
|
|
return visit(
|
|
[&](auto&& t)
|
|
{
|
|
return cloneChildren(&t);
|
|
},
|
|
asMutable(ty)->ty
|
|
);
|
|
}
|
|
|
|
void cloneChildren(TypePackId tp)
|
|
{
|
|
return visit(
|
|
[&](auto&& t)
|
|
{
|
|
return cloneChildren(&t);
|
|
},
|
|
asMutable(tp)->ty
|
|
);
|
|
}
|
|
|
|
void cloneChildren(Kind kind)
|
|
{
|
|
if (auto ty = get<TypeId>(kind))
|
|
return cloneChildren(*ty);
|
|
else if (auto tp = get<TypePackId>(kind))
|
|
return cloneChildren(*tp);
|
|
else
|
|
LUAU_ASSERT(!"Item holds neither TypeId nor TypePackId when enqueuing its children?");
|
|
}
|
|
|
|
void cloneChildren(ErrorType* t)
|
|
{
|
|
// noop.
|
|
}
|
|
|
|
void cloneChildren(BoundType* t)
|
|
{
|
|
t->boundTo = shallowClone(t->boundTo);
|
|
}
|
|
|
|
void cloneChildren(FreeType* t)
|
|
{
|
|
if (t->lowerBound)
|
|
t->lowerBound = shallowClone(t->lowerBound);
|
|
if (t->upperBound)
|
|
t->upperBound = shallowClone(t->upperBound);
|
|
}
|
|
|
|
void cloneChildren(GenericType* t)
|
|
{
|
|
// TOOD: clone upper bounds.
|
|
}
|
|
|
|
void cloneChildren(PrimitiveType* t)
|
|
{
|
|
// noop.
|
|
}
|
|
|
|
void cloneChildren(BlockedType* t)
|
|
{
|
|
// TODO: In the new solver, we should ice.
|
|
}
|
|
|
|
void cloneChildren(PendingExpansionType* t)
|
|
{
|
|
// TODO: In the new solver, we should ice.
|
|
}
|
|
|
|
void cloneChildren(SingletonType* t)
|
|
{
|
|
// noop.
|
|
}
|
|
|
|
void cloneChildren(FunctionType* t)
|
|
{
|
|
for (TypeId& g : t->generics)
|
|
g = shallowClone(g);
|
|
|
|
for (TypePackId& gp : t->genericPacks)
|
|
gp = shallowClone(gp);
|
|
|
|
t->argTypes = shallowClone(t->argTypes);
|
|
t->retTypes = shallowClone(t->retTypes);
|
|
}
|
|
|
|
void cloneChildren(TableType* t)
|
|
{
|
|
if (t->indexer)
|
|
{
|
|
t->indexer->indexType = shallowClone(t->indexer->indexType);
|
|
t->indexer->indexResultType = shallowClone(t->indexer->indexResultType);
|
|
}
|
|
|
|
for (auto& [_, p] : t->props)
|
|
p = shallowClone(p);
|
|
|
|
for (TypeId& ty : t->instantiatedTypeParams)
|
|
ty = shallowClone(ty);
|
|
|
|
for (TypePackId& tp : t->instantiatedTypePackParams)
|
|
tp = shallowClone(tp);
|
|
}
|
|
|
|
void cloneChildren(MetatableType* t)
|
|
{
|
|
t->table = shallowClone(t->table);
|
|
t->metatable = shallowClone(t->metatable);
|
|
}
|
|
|
|
void cloneChildren(ExternType* t)
|
|
{
|
|
for (auto& [_, p] : t->props)
|
|
p = shallowClone(p);
|
|
|
|
if (t->parent)
|
|
t->parent = shallowClone(*t->parent);
|
|
|
|
if (t->metatable)
|
|
t->metatable = shallowClone(*t->metatable);
|
|
|
|
if (t->indexer)
|
|
{
|
|
t->indexer->indexType = shallowClone(t->indexer->indexType);
|
|
t->indexer->indexResultType = shallowClone(t->indexer->indexResultType);
|
|
}
|
|
}
|
|
|
|
void cloneChildren(AnyType* t)
|
|
{
|
|
// noop.
|
|
}
|
|
|
|
void cloneChildren(NoRefineType* t)
|
|
{
|
|
// noop.
|
|
}
|
|
|
|
void cloneChildren(UnionType* t)
|
|
{
|
|
for (TypeId& ty : t->options)
|
|
ty = shallowClone(ty);
|
|
}
|
|
|
|
void cloneChildren(IntersectionType* t)
|
|
{
|
|
for (TypeId& ty : t->parts)
|
|
ty = shallowClone(ty);
|
|
}
|
|
|
|
virtual void cloneChildren(LazyType* t)
|
|
{
|
|
if (auto unwrapped = t->unwrapped.load())
|
|
t->unwrapped.store(shallowClone(unwrapped));
|
|
}
|
|
|
|
void cloneChildren(UnknownType* t)
|
|
{
|
|
// noop.
|
|
}
|
|
|
|
void cloneChildren(NeverType* t)
|
|
{
|
|
// noop.
|
|
}
|
|
|
|
void cloneChildren(NegationType* t)
|
|
{
|
|
t->ty = shallowClone(t->ty);
|
|
}
|
|
|
|
void cloneChildren(TypeFunctionInstanceType* t)
|
|
{
|
|
for (TypeId& ty : t->typeArguments)
|
|
ty = shallowClone(ty);
|
|
|
|
for (TypePackId& tp : t->packArguments)
|
|
tp = shallowClone(tp);
|
|
}
|
|
|
|
void cloneChildren(FreeTypePack* t)
|
|
{
|
|
// TODO: clone lower and upper bounds.
|
|
// TODO: In the new solver, we should ice.
|
|
}
|
|
|
|
void cloneChildren(GenericTypePack* t)
|
|
{
|
|
// TOOD: clone upper bounds.
|
|
}
|
|
|
|
void cloneChildren(BlockedTypePack* t)
|
|
{
|
|
// TODO: In the new solver, we should ice.
|
|
}
|
|
|
|
void cloneChildren(BoundTypePack* t)
|
|
{
|
|
t->boundTo = shallowClone(t->boundTo);
|
|
}
|
|
|
|
void cloneChildren(ErrorTypePack* t)
|
|
{
|
|
// noop.
|
|
}
|
|
|
|
void cloneChildren(VariadicTypePack* t)
|
|
{
|
|
t->ty = shallowClone(t->ty);
|
|
}
|
|
|
|
void cloneChildren(TypePack* t)
|
|
{
|
|
for (TypeId& ty : t->head)
|
|
ty = shallowClone(ty);
|
|
|
|
if (t->tail)
|
|
t->tail = shallowClone(*t->tail);
|
|
}
|
|
|
|
void cloneChildren(TypeFunctionInstanceTypePack* t)
|
|
{
|
|
for (TypeId& ty : t->typeArguments)
|
|
ty = shallowClone(ty);
|
|
|
|
for (TypePackId& tp : t->packArguments)
|
|
tp = shallowClone(tp);
|
|
}
|
|
};
|
|
|
|
class FragmentAutocompleteTypeCloner final : public TypeCloner
|
|
{
|
|
Scope* replacementForNullScope = nullptr;
|
|
|
|
public:
|
|
FragmentAutocompleteTypeCloner(
|
|
NotNull<TypeArena> arena,
|
|
NotNull<BuiltinTypes> builtinTypes,
|
|
NotNull<SeenTypes> types,
|
|
NotNull<SeenTypePacks> packs,
|
|
TypeId forceTy,
|
|
TypePackId forceTp,
|
|
Scope* replacementForNullScope
|
|
)
|
|
: TypeCloner(arena, builtinTypes, types, packs, forceTy, forceTp)
|
|
, replacementForNullScope(replacementForNullScope)
|
|
{
|
|
LUAU_ASSERT(replacementForNullScope);
|
|
}
|
|
|
|
TypeId shallowClone(TypeId ty) override
|
|
{
|
|
// We want to [`Luau::follow`] but without forcing the expansion of [`LazyType`]s.
|
|
ty = follow(ty, FollowOption::DisableLazyTypeThunks);
|
|
|
|
if (auto clone = find(ty))
|
|
return *clone;
|
|
else if (ty->persistent && ty != forceTy)
|
|
return ty;
|
|
|
|
TypeId target = arena->addType(ty->ty);
|
|
asMutable(target)->documentationSymbol = ty->documentationSymbol;
|
|
|
|
if (auto generic = getMutable<GenericType>(target))
|
|
generic->scope = nullptr;
|
|
else if (auto free = getMutable<FreeType>(target))
|
|
{
|
|
free->scope = replacementForNullScope;
|
|
}
|
|
else if (auto tt = getMutable<TableType>(target))
|
|
tt->scope = replacementForNullScope;
|
|
|
|
(*types)[ty] = target;
|
|
queue.emplace_back(target);
|
|
return target;
|
|
}
|
|
|
|
TypePackId shallowClone(TypePackId tp) override
|
|
{
|
|
tp = follow(tp);
|
|
|
|
if (auto clone = find(tp))
|
|
return *clone;
|
|
else if (tp->persistent && tp != forceTp)
|
|
return tp;
|
|
|
|
TypePackId target = arena->addTypePack(tp->ty);
|
|
|
|
if (auto generic = getMutable<GenericTypePack>(target))
|
|
generic->scope = nullptr;
|
|
else if (auto free = getMutable<FreeTypePack>(target))
|
|
free->scope = replacementForNullScope;
|
|
|
|
(*packs)[tp] = target;
|
|
queue.emplace_back(target);
|
|
return target;
|
|
}
|
|
|
|
void cloneChildren(LazyType* t) override
|
|
{
|
|
// Do not clone lazy types
|
|
}
|
|
};
|
|
|
|
|
|
} // namespace
|
|
|
|
TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes)
|
|
{
|
|
if (tp->persistent && !clonePersistentTypes)
|
|
return tp;
|
|
|
|
TypeCloner cloner{
|
|
NotNull{&dest},
|
|
cloneState.builtinTypes,
|
|
NotNull{&cloneState.seenTypes},
|
|
NotNull{&cloneState.seenTypePacks},
|
|
nullptr,
|
|
clonePersistentTypes ? tp : nullptr
|
|
};
|
|
|
|
return cloner.shallowClone(tp);
|
|
}
|
|
|
|
TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes)
|
|
{
|
|
if (typeId->persistent && !clonePersistentTypes)
|
|
return typeId;
|
|
|
|
TypeCloner cloner{
|
|
NotNull{&dest},
|
|
cloneState.builtinTypes,
|
|
NotNull{&cloneState.seenTypes},
|
|
NotNull{&cloneState.seenTypePacks},
|
|
clonePersistentTypes ? typeId : nullptr,
|
|
nullptr
|
|
};
|
|
|
|
return cloner.shallowClone(typeId);
|
|
}
|
|
|
|
TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState)
|
|
{
|
|
if (tp->persistent)
|
|
return tp;
|
|
|
|
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
|
|
return cloner.clone(tp);
|
|
}
|
|
|
|
TypeId clone(TypeId typeId, TypeArena& dest, CloneState& cloneState)
|
|
{
|
|
if (typeId->persistent)
|
|
return typeId;
|
|
|
|
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
|
|
return cloner.clone(typeId);
|
|
}
|
|
|
|
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState)
|
|
{
|
|
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
|
|
|
|
TypeFun copy = typeFun;
|
|
|
|
for (auto& param : copy.typeParams)
|
|
{
|
|
param.ty = cloner.clone(param.ty);
|
|
|
|
if (param.defaultValue)
|
|
param.defaultValue = cloner.clone(*param.defaultValue);
|
|
}
|
|
|
|
for (auto& param : copy.typePackParams)
|
|
{
|
|
param.tp = cloner.clone(param.tp);
|
|
|
|
if (param.defaultValue)
|
|
param.defaultValue = cloner.clone(*param.defaultValue);
|
|
}
|
|
|
|
copy.type = cloner.clone(copy.type);
|
|
|
|
return copy;
|
|
}
|
|
|
|
Binding clone(const Binding& binding, TypeArena& dest, CloneState& cloneState)
|
|
{
|
|
TypeCloner cloner{NotNull{&dest}, cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, nullptr};
|
|
|
|
Binding b;
|
|
b.deprecated = binding.deprecated;
|
|
b.deprecatedSuggestion = binding.deprecatedSuggestion;
|
|
b.documentationSymbol = binding.documentationSymbol;
|
|
b.location = binding.location;
|
|
b.typeId = cloner.clone(binding.typeId);
|
|
|
|
return b;
|
|
}
|
|
|
|
TypePackId cloneIncremental(TypePackId tp, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
|
|
{
|
|
if (tp->persistent)
|
|
return tp;
|
|
|
|
FragmentAutocompleteTypeCloner cloner{
|
|
NotNull{&dest},
|
|
cloneState.builtinTypes,
|
|
NotNull{&cloneState.seenTypes},
|
|
NotNull{&cloneState.seenTypePacks},
|
|
nullptr,
|
|
nullptr,
|
|
freshScopeForFreeTypes
|
|
};
|
|
return cloner.clone(tp);
|
|
}
|
|
|
|
TypeId cloneIncremental(TypeId typeId, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
|
|
{
|
|
if (typeId->persistent)
|
|
return typeId;
|
|
|
|
FragmentAutocompleteTypeCloner cloner{
|
|
NotNull{&dest},
|
|
cloneState.builtinTypes,
|
|
NotNull{&cloneState.seenTypes},
|
|
NotNull{&cloneState.seenTypePacks},
|
|
nullptr,
|
|
nullptr,
|
|
freshScopeForFreeTypes
|
|
};
|
|
return cloner.clone(typeId);
|
|
}
|
|
|
|
TypeFun cloneIncremental(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
|
|
{
|
|
FragmentAutocompleteTypeCloner cloner{
|
|
NotNull{&dest},
|
|
cloneState.builtinTypes,
|
|
NotNull{&cloneState.seenTypes},
|
|
NotNull{&cloneState.seenTypePacks},
|
|
nullptr,
|
|
nullptr,
|
|
freshScopeForFreeTypes
|
|
};
|
|
|
|
TypeFun copy = typeFun;
|
|
|
|
for (auto& param : copy.typeParams)
|
|
{
|
|
param.ty = cloner.clone(param.ty);
|
|
|
|
if (param.defaultValue)
|
|
param.defaultValue = cloner.clone(*param.defaultValue);
|
|
}
|
|
|
|
for (auto& param : copy.typePackParams)
|
|
{
|
|
param.tp = cloner.clone(param.tp);
|
|
|
|
if (param.defaultValue)
|
|
param.defaultValue = cloner.clone(*param.defaultValue);
|
|
}
|
|
|
|
copy.type = cloner.clone(copy.type);
|
|
|
|
return copy;
|
|
}
|
|
|
|
Binding cloneIncremental(const Binding& binding, TypeArena& dest, CloneState& cloneState, Scope* freshScopeForFreeTypes)
|
|
{
|
|
FragmentAutocompleteTypeCloner cloner{
|
|
NotNull{&dest},
|
|
cloneState.builtinTypes,
|
|
NotNull{&cloneState.seenTypes},
|
|
NotNull{&cloneState.seenTypePacks},
|
|
nullptr,
|
|
nullptr,
|
|
freshScopeForFreeTypes
|
|
};
|
|
|
|
Binding b;
|
|
b.deprecated = binding.deprecated;
|
|
b.deprecatedSuggestion = binding.deprecatedSuggestion;
|
|
b.documentationSymbol = binding.documentationSymbol;
|
|
b.location = binding.location;
|
|
b.typeId = binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
} // namespace Luau
|