// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once #include "Luau/Unifiable.h" #include "Luau/Variant.h" #include "Luau/TypeFwd.h" #include "Luau/NotNull.h" #include "Luau/Common.h" #include #include #include namespace Luau { struct TypeArena; struct TypePackFunction; struct TxnLog; struct TypePack; struct VariadicTypePack; struct BlockedTypePack; struct TypeFunctionInstanceTypePack; struct FreeTypePack { explicit FreeTypePack(TypeLevel level); explicit FreeTypePack(Scope* scope); FreeTypePack(Scope* scope, TypeLevel level); int index; TypeLevel level; Scope* scope = nullptr; }; struct GenericTypePack { // By default, generics are global, with a synthetic name GenericTypePack(); explicit GenericTypePack(TypeLevel level); explicit GenericTypePack(const Name& name); explicit GenericTypePack(Scope* scope); GenericTypePack(TypeLevel level, const Name& name); GenericTypePack(Scope* scope, const Name& name); int index; TypeLevel level; Scope* scope = nullptr; Name name; bool explicitName = false; }; using BoundTypePack = Unifiable::Bound; using ErrorTypePack = Unifiable::Error; using TypePackVariant = Unifiable::Variant; /* A TypePack is a rope-like string of TypeIds. We use this structure to encode * notions like packs of unknown length and packs of any length, as well as more * nuanced compositions like "a pack which is a number prepended to this other pack," * or "a pack that is 2 numbers followed by any number of any other types." */ struct TypePack { std::vector head; std::optional tail; }; struct VariadicTypePack { TypeId ty; bool hidden = false; // if true, we don't display this when toString()ing a pack with this variadic as its tail. }; /** * Analogous to a BlockedType. */ struct BlockedTypePack { BlockedTypePack(); size_t index; struct Constraint* owner = nullptr; static size_t nextIndex; }; /** * Analogous to a TypeFunctionInstanceType. */ struct TypeFunctionInstanceTypePack { NotNull function; std::vector typeArguments; std::vector packArguments; }; struct TypePackVar { explicit TypePackVar(const TypePackVariant& ty); explicit TypePackVar(TypePackVariant&& ty); TypePackVar(TypePackVariant&& ty, bool persistent); bool operator==(const TypePackVar& rhs) const; TypePackVar& operator=(TypePackVariant&& tp); TypePackVar& operator=(const TypePackVar& rhs); // Re-assignes the content of the pack, but doesn't change the owning arena and can't make pack persistent. void reassign(const TypePackVar& rhs) { ty = rhs.ty; } TypePackVariant ty; bool persistent = false; // Pointer to the type arena that allocated this pack. TypeArena* owningArena = nullptr; }; /* Walk the set of TypeIds in a TypePack. * * Like Types, individual TypePacks can be free, generic, or any. * * We afford the ability to work with these kinds of packs by giving the * iterator a .tail() property that yields the tail-most TypePack in the * rope. * * It is very commonplace to want to walk each type in a pack, then handle * the tail specially. eg when checking parameters, it might be the case * that the parameter pack ends with a VariadicTypePack. In this case, we * want to allow any number of extra arguments. * * The iterator obtained by calling end(tp) does not have a .tail(), but is * equivalent with end(tp2) for any two type packs. */ struct TypePackIterator { using value_type = Luau::TypeId; using pointer = value_type*; using reference = value_type&; using difference_type = size_t; using iterator_category = std::input_iterator_tag; TypePackIterator() = default; explicit TypePackIterator(TypePackId tp); TypePackIterator(TypePackId tp, const TxnLog* log); TypePackIterator& operator++(); TypePackIterator operator++(int); bool operator!=(const TypePackIterator& rhs); bool operator==(const TypePackIterator& rhs); const TypeId& operator*(); /** Return the tail of a TypePack. * This may *only* be called on an iterator that has been incremented to the end. * Returns nullopt if the pack has fixed length. */ std::optional tail(); friend TypePackIterator end(TypePackId tp); private: TypePackId currentTypePack = nullptr; const TypePack* tp = nullptr; size_t currentIndex = 0; const TxnLog* log; }; TypePackIterator begin(TypePackId tp); TypePackIterator begin(TypePackId tp, const TxnLog* log); TypePackIterator end(TypePackId tp); using SeenSet = std::set>; bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs); TypePackId follow(TypePackId tp); TypePackId follow(TypePackId t, const void* context, TypePackId (*mapper)(const void*, TypePackId)); size_t size(TypePackId tp, TxnLog* log = nullptr); bool finite(TypePackId tp, TxnLog* log = nullptr); size_t size(const TypePack& tp, TxnLog* log = nullptr); std::optional first(TypePackId tp, bool ignoreHiddenVariadics = true); TypePackVar* asMutable(TypePackId tp); TypePack* asMutable(const TypePack* tp); template const T* get(TypePackId tp) { LUAU_ASSERT(tp); if constexpr (!std::is_same_v) LUAU_ASSERT(get_if(&tp->ty) == nullptr); return get_if(&(tp->ty)); } template T* getMutable(TypePackId tp) { LUAU_ASSERT(tp); if constexpr (!std::is_same_v) LUAU_ASSERT(get_if(&tp->ty) == nullptr); return get_if(&(asMutable(tp)->ty)); } /// Returns true if the type pack is known to be empty (no types in the head and no/an empty tail). bool isEmpty(TypePackId tp); /// Flattens out a type pack. Also returns a valid TypePackId tail if the type pack's full size is not known std::pair, std::optional> flatten(TypePackId tp); std::pair, std::optional> flatten(TypePackId tp, const TxnLog& log); /// Returs true if the type pack arose from a function that is declared to be variadic. /// Returns *false* for function argument packs that are inferred to be safe to oversaturate! bool isVariadic(TypePackId tp); bool isVariadic(TypePackId tp, const TxnLog& log); // Returns true if the TypePack is Generic or Variadic. Does not walk TypePacks!! bool isVariadicTail(TypePackId tp, const TxnLog& log, bool includeHiddenVariadics = false); bool containsNever(TypePackId tp); /* * Use this to change the kind of a particular type pack. * * LUAU_NOINLINE so that the calling frame doesn't have to pay the stack storage for the new variant. */ template LUAU_NOINLINE T* emplaceTypePack(TypePackVar* ty, Args&&... args) { return &ty->ty.emplace(std::forward(args)...); } template<> LUAU_NOINLINE Unifiable::Bound* emplaceTypePack(TypePackVar* ty, TypePackId& tyArg); } // namespace Luau