luau/Analysis/include/Luau/VisitType.h

449 lines
12 KiB
C
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
2022-05-05 20:03:43 -04:00
#include <unordered_set>
#include "Luau/DenseHash.h"
2022-05-05 20:03:43 -04:00
#include "Luau/RecursionCounter.h"
#include "Luau/TypePack.h"
#include "Luau/Type.h"
2022-05-05 20:03:43 -04:00
LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauBoundLazyTypes2)
namespace Luau
{
namespace visit_detail
{
/**
* Apply f(tid, t, seen) if doing so would pass type checking, else apply f(tid, t)
*
* We do this to permit (but not require) Type visitors to accept the seen set as an argument.
*/
template<typename F, typename A, typename B, typename C>
auto apply(A tid, const B& t, C& c, F& f) -> decltype(f(tid, t, c))
{
return f(tid, t, c);
}
template<typename A, typename B, typename C, typename F>
auto apply(A tid, const B& t, C&, F& f) -> decltype(f(tid, t))
{
return f(tid, t);
}
inline bool hasSeen(std::unordered_set<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
return !seen.insert(ttv).second;
}
inline bool hasSeen(DenseHashSet<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
if (seen.contains(ttv))
return true;
seen.insert(ttv);
return false;
}
inline void unsee(std::unordered_set<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
seen.erase(ttv);
}
inline void unsee(DenseHashSet<void*>& seen, const void* tv)
{
// When DenseHashSet is used for 'visitTypeOnce', where don't forget visited elements
}
} // namespace visit_detail
2022-05-05 20:03:43 -04:00
template<typename S>
struct GenericTypeVisitor
2022-05-05 20:03:43 -04:00
{
using Set = S;
Set seen;
bool skipBoundTypes = false;
2022-05-05 20:03:43 -04:00
int recursionCounter = 0;
GenericTypeVisitor() = default;
2022-05-05 20:03:43 -04:00
explicit GenericTypeVisitor(Set seen, bool skipBoundTypes = false)
2022-05-05 20:03:43 -04:00
: seen(std::move(seen))
, skipBoundTypes(skipBoundTypes)
2022-05-05 20:03:43 -04:00
{
}
virtual void cycle(TypeId) {}
virtual void cycle(TypePackId) {}
virtual bool visit(TypeId ty)
{
return true;
}
virtual bool visit(TypeId ty, const BoundType& btv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const FreeType& ftv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const GenericType& gtv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const ErrorType& etv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const PrimitiveType& ptv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const FunctionType& ftv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const TableType& ttv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const MetatableType& mtv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const ClassType& ctv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const AnyType& atv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const UnknownType& utv)
2022-07-07 21:22:39 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const NeverType& ntv)
2022-07-07 21:22:39 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const UnionType& utv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const IntersectionType& itv)
2022-05-05 20:03:43 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const BlockedType& btv)
2022-07-21 17:16:54 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const PendingExpansionType& petv)
2022-08-04 18:35:33 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const SingletonType& stv)
2022-07-21 17:16:54 -04:00
{
return visit(ty);
}
virtual bool visit(TypeId ty, const NegationType& ntv)
{
return visit(ty);
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 13:50:47 -04:00
virtual bool visit(TypeId ty, const TypeFamilyInstanceType& tfit)
{
return visit(ty);
}
2022-05-05 20:03:43 -04:00
virtual bool visit(TypePackId tp)
{
return true;
}
virtual bool visit(TypePackId tp, const BoundTypePack& btp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const FreeTypePack& ftp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const GenericTypePack& gtp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const Unifiable::Error& etp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const TypePack& pack)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const VariadicTypePack& vtp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const BlockedTypePack& btp)
{
return visit(tp);
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 13:50:47 -04:00
virtual bool visit(TypePackId tp, const TypeFamilyInstanceTypePack& tfitp)
{
return visit(tp);
}
2022-05-05 20:03:43 -04:00
void traverse(TypeId ty)
{
2022-06-23 21:56:00 -04:00
RecursionLimiter limiter{&recursionCounter, FInt::LuauVisitRecursionLimit};
2022-05-05 20:03:43 -04:00
if (visit_detail::hasSeen(seen, ty))
{
cycle(ty);
return;
}
if (auto btv = get<BoundType>(ty))
2022-05-05 20:03:43 -04:00
{
if (skipBoundTypes)
traverse(btv->boundTo);
else if (visit(ty, *btv))
2022-05-05 20:03:43 -04:00
traverse(btv->boundTo);
}
else if (auto ftv = get<FreeType>(ty))
2022-05-05 20:03:43 -04:00
visit(ty, *ftv);
else if (auto gtv = get<GenericType>(ty))
2022-05-05 20:03:43 -04:00
visit(ty, *gtv);
else if (auto etv = get<ErrorType>(ty))
2022-05-05 20:03:43 -04:00
visit(ty, *etv);
else if (auto ptv = get<PrimitiveType>(ty))
2022-05-05 20:03:43 -04:00
visit(ty, *ptv);
else if (auto ftv = get<FunctionType>(ty))
2022-05-05 20:03:43 -04:00
{
if (visit(ty, *ftv))
{
traverse(ftv->argTypes);
2022-06-16 21:05:14 -04:00
traverse(ftv->retTypes);
2022-05-05 20:03:43 -04:00
}
}
else if (auto ttv = get<TableType>(ty))
2022-05-05 20:03:43 -04:00
{
// Some visitors want to see bound tables, that's why we traverse the original type
if (skipBoundTypes && ttv->boundTo)
{
traverse(*ttv->boundTo);
}
else if (visit(ty, *ttv))
2022-05-05 20:03:43 -04:00
{
if (ttv->boundTo)
{
traverse(*ttv->boundTo);
}
else
{
for (auto& [_name, prop] : ttv->props)
traverse(prop.type());
2022-05-05 20:03:43 -04:00
if (ttv->indexer)
{
traverse(ttv->indexer->indexType);
traverse(ttv->indexer->indexResultType);
}
}
}
}
else if (auto mtv = get<MetatableType>(ty))
2022-05-05 20:03:43 -04:00
{
if (visit(ty, *mtv))
{
traverse(mtv->table);
traverse(mtv->metatable);
}
}
else if (auto ctv = get<ClassType>(ty))
2022-05-05 20:03:43 -04:00
{
if (visit(ty, *ctv))
{
for (const auto& [name, prop] : ctv->props)
traverse(prop.type());
2022-05-05 20:03:43 -04:00
if (ctv->parent)
traverse(*ctv->parent);
if (ctv->metatable)
traverse(*ctv->metatable);
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 13:50:47 -04:00
if (FFlag::LuauTypecheckClassTypeIndexers)
{
if (ctv->indexer)
{
traverse(ctv->indexer->indexType);
traverse(ctv->indexer->indexResultType);
}
}
2022-05-05 20:03:43 -04:00
}
}
else if (auto atv = get<AnyType>(ty))
2022-05-05 20:03:43 -04:00
visit(ty, *atv);
else if (auto utv = get<UnionType>(ty))
2022-05-05 20:03:43 -04:00
{
if (visit(ty, *utv))
{
for (TypeId optTy : utv->options)
traverse(optTy);
}
}
else if (auto itv = get<IntersectionType>(ty))
2022-05-05 20:03:43 -04:00
{
if (visit(ty, *itv))
{
for (TypeId partTy : itv->parts)
traverse(partTy);
}
}
else if (auto ltv = get<LazyType>(ty))
2022-07-21 17:16:54 -04:00
{
if (FFlag::LuauBoundLazyTypes2)
{
if (TypeId unwrapped = ltv->unwrapped)
traverse(unwrapped);
}
// Visiting into LazyType that hasn't been unwrapped may necessarily cause infinite expansion, so we don't do that on purpose.
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ClassType
2022-07-21 17:16:54 -04:00
// that doesn't need to be expanded.
}
else if (auto stv = get<SingletonType>(ty))
2022-07-21 17:16:54 -04:00
visit(ty, *stv);
else if (auto btv = get<BlockedType>(ty))
2022-07-21 17:16:54 -04:00
visit(ty, *btv);
else if (auto utv = get<UnknownType>(ty))
2022-07-21 17:16:54 -04:00
visit(ty, *utv);
else if (auto ntv = get<NeverType>(ty))
2022-07-21 17:16:54 -04:00
visit(ty, *ntv);
else if (auto petv = get<PendingExpansionType>(ty))
2022-08-04 18:35:33 -04:00
{
if (visit(ty, *petv))
{
for (TypeId a : petv->typeArguments)
traverse(a);
for (TypePackId a : petv->packArguments)
traverse(a);
}
}
else if (auto ntv = get<NegationType>(ty))
{
if (visit(ty, *ntv))
traverse(ntv->ty);
}
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 13:50:47 -04:00
else if (auto tfit = get<TypeFamilyInstanceType>(ty))
{
if (visit(ty, *tfit))
{
for (TypeId p : tfit->typeArguments)
traverse(p);
for (TypePackId p : tfit->packArguments)
traverse(p);
}
}
2022-07-21 17:16:54 -04:00
else
LUAU_ASSERT(!"GenericTypeVisitor::traverse(TypeId) is not exhaustive!");
2022-05-05 20:03:43 -04:00
visit_detail::unsee(seen, ty);
}
void traverse(TypePackId tp)
{
if (visit_detail::hasSeen(seen, tp))
{
cycle(tp);
return;
}
if (auto btv = get<BoundTypePack>(tp))
{
if (visit(tp, *btv))
traverse(btv->boundTo);
}
else if (auto ftv = get<FreeTypePack>(tp))
2022-05-05 20:03:43 -04:00
visit(tp, *ftv);
else if (auto gtv = get<GenericTypePack>(tp))
2022-05-05 20:03:43 -04:00
visit(tp, *gtv);
else if (auto etv = get<Unifiable::Error>(tp))
visit(tp, *etv);
else if (auto pack = get<TypePack>(tp))
{
2022-05-19 20:02:24 -04:00
bool res = visit(tp, *pack);
2022-08-04 18:35:33 -04:00
if (res)
2022-05-19 20:02:24 -04:00
{
for (TypeId ty : pack->head)
traverse(ty);
2022-05-05 20:03:43 -04:00
2022-05-19 20:02:24 -04:00
if (pack->tail)
traverse(*pack->tail);
}
2022-05-05 20:03:43 -04:00
}
else if (auto pack = get<VariadicTypePack>(tp))
{
2022-05-19 20:02:24 -04:00
bool res = visit(tp, *pack);
2022-08-04 18:35:33 -04:00
if (res)
2022-05-19 20:02:24 -04:00
traverse(pack->ty);
2022-05-05 20:03:43 -04:00
}
else if (auto btp = get<BlockedTypePack>(tp))
visit(tp, *btp);
Sync to upstream/release/576 (#928) * `ClassType` can now have an indexer defined on it. This allows custom types to be used in `t[x]` expressions. * Fixed search for closest executable breakpoint line. Previously, breakpoints might have been skipped in `else` blocks at the end of a function * Fixed how unification is performed for two optional types `a? <: b?`, previously it might have unified either 'a' or 'b' with 'nil'. Note that this fix is not enabled by default yet (see the list in `ExperimentalFlags.h`) In the new type solver, a concept of 'Type Families' has been introduced. Type families can be thought of as type aliases with custom type inference/reduction logic included with them. For example, we can have an `Add<T, U>` type family that will resolve the type that is the result of adding two values together. This will help type inference to figure out what 'T' and 'U' might be when explicit type annotations are not provided. In this update we don't define any type families, but they will be added in the near future. It is also possible for Luau embedders to define their own type families in the global/environment scope. Other changes include: * Fixed scope used to find out which generic types should be included in the function generic type list * Fixed a crash after cyclic bound types were created during unification And in native code generation (jit): * Use of arm64 target on M1 now requires macOS 13 * Entry into native code has been optimized. This is especially important for coroutine call/pcall performance as they involve going through a C call frame * LOP_LOADK(X) translation into IR has been improved to enable type tag/constant propagation * arm64 can use integer immediate values to synthesize floating-point values * x64 assembler removes duplicate 64bit numbers from the data section to save space * Linux `perf` can now be used to profile native Luau code (when running with --codegen-perf CLI argument)
2023-05-12 13:50:47 -04:00
else if (auto tfitp = get<TypeFamilyInstanceTypePack>(tp))
{
if (visit(tp, *tfitp))
{
for (TypeId t : tfitp->typeArguments)
traverse(t);
for (TypePackId t : tfitp->packArguments)
traverse(t);
}
}
2022-05-05 20:03:43 -04:00
else
LUAU_ASSERT(!"GenericTypeVisitor::traverse(TypePackId) is not exhaustive!");
2022-05-05 20:03:43 -04:00
visit_detail::unsee(seen, tp);
}
};
/** Visit each type under a given type. Skips over cycles and keeps recursion depth under control.
*
* The same type may be visited multiple times if there are multiple distinct paths to it. If this is undesirable, use
* TypeOnceVisitor.
2022-05-05 20:03:43 -04:00
*/
struct TypeVisitor : GenericTypeVisitor<std::unordered_set<void*>>
2022-05-05 20:03:43 -04:00
{
explicit TypeVisitor(bool skipBoundTypes = false)
: GenericTypeVisitor{{}, skipBoundTypes}
{
}
2022-05-05 20:03:43 -04:00
};
/// Visit each type under a given type. Each type will only be checked once even if there are multiple paths to it.
struct TypeOnceVisitor : GenericTypeVisitor<DenseHashSet<void*>>
2022-05-05 20:03:43 -04:00
{
explicit TypeOnceVisitor(bool skipBoundTypes = false)
: GenericTypeVisitor{DenseHashSet<void*>{nullptr}, skipBoundTypes}
2022-05-05 20:03:43 -04:00
{
}
};
} // namespace Luau