2021-10-29 21:25:12 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
#include "Luau/TypeVar.h"
|
|
|
|
|
|
|
|
#include "Luau/BuiltinDefinitions.h"
|
|
|
|
#include "Luau/Common.h"
|
|
|
|
#include "Luau/DenseHash.h"
|
|
|
|
#include "Luau/Error.h"
|
2022-01-21 17:00:19 +00:00
|
|
|
#include "Luau/RecursionCounter.h"
|
2021-10-29 21:25:12 +01:00
|
|
|
#include "Luau/StringUtils.h"
|
|
|
|
#include "Luau/ToString.h"
|
|
|
|
#include "Luau/TypeInfer.h"
|
|
|
|
#include "Luau/TypePack.h"
|
|
|
|
#include "Luau/VisitTypeVar.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <optional>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <unordered_set>
|
|
|
|
|
2022-01-27 23:46:05 +00:00
|
|
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
|
|
|
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
2022-01-21 17:00:19 +00:00
|
|
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
2022-07-08 02:22:39 +01:00
|
|
|
LUAU_FASTFLAG(LuauUnknownAndNeverType)
|
|
|
|
LUAU_FASTFLAGVARIABLE(LuauDeduceGmatchReturnTypes, false)
|
|
|
|
LUAU_FASTFLAGVARIABLE(LuauMaybeGenericIntersectionTypes, false)
|
2022-07-14 23:52:26 +01:00
|
|
|
LUAU_FASTFLAGVARIABLE(LuauDeduceFindMatchReturnTypes, false)
|
2022-08-25 22:53:50 +01:00
|
|
|
LUAU_FASTFLAGVARIABLE(LuauStringFormatArgumentErrorFix, false)
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
std::optional<WithPredicate<TypePackId>> magicFunctionFormat(
|
|
|
|
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate);
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
static std::optional<WithPredicate<TypePackId>> magicFunctionGmatch(
|
|
|
|
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate);
|
|
|
|
|
2022-07-14 23:52:26 +01:00
|
|
|
static std::optional<WithPredicate<TypePackId>> magicFunctionMatch(
|
|
|
|
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate);
|
|
|
|
|
|
|
|
static std::optional<WithPredicate<TypePackId>> magicFunctionFind(
|
|
|
|
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate);
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
TypeId follow(TypeId t)
|
|
|
|
{
|
2022-01-07 01:46:53 +00:00
|
|
|
return follow(t, [](TypeId t) {
|
|
|
|
return t;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeId follow(TypeId t, std::function<TypeId(TypeId)> mapper)
|
|
|
|
{
|
|
|
|
auto advance = [&mapper](TypeId ty) -> std::optional<TypeId> {
|
|
|
|
if (auto btv = get<Unifiable::Bound<TypeId>>(mapper(ty)))
|
2021-10-29 21:25:12 +01:00
|
|
|
return btv->boundTo;
|
2022-01-07 01:46:53 +00:00
|
|
|
else if (auto ttv = get<TableTypeVar>(mapper(ty)))
|
2021-10-29 21:25:12 +01:00
|
|
|
return ttv->boundTo;
|
|
|
|
else
|
|
|
|
return std::nullopt;
|
|
|
|
};
|
|
|
|
|
2022-01-07 01:46:53 +00:00
|
|
|
auto force = [&mapper](TypeId ty) {
|
|
|
|
if (auto ltv = get_if<LazyTypeVar>(&mapper(ty)->ty))
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
|
|
|
TypeId res = ltv->thunk();
|
|
|
|
if (get<LazyTypeVar>(res))
|
|
|
|
throw std::runtime_error("Lazy TypeVar cannot resolve to another Lazy TypeVar");
|
|
|
|
|
|
|
|
*asMutable(ty) = BoundTypeVar(res);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
force(t);
|
|
|
|
|
|
|
|
TypeId cycleTester = t; // Null once we've determined that there is no cycle
|
|
|
|
if (auto a = advance(cycleTester))
|
|
|
|
cycleTester = *a;
|
|
|
|
else
|
|
|
|
return t;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
force(t);
|
|
|
|
auto a1 = advance(t);
|
|
|
|
if (a1)
|
|
|
|
t = *a1;
|
|
|
|
else
|
|
|
|
return t;
|
|
|
|
|
|
|
|
if (nullptr != cycleTester)
|
|
|
|
{
|
|
|
|
auto a2 = advance(cycleTester);
|
|
|
|
if (a2)
|
|
|
|
{
|
|
|
|
auto a3 = advance(*a2);
|
|
|
|
if (a3)
|
|
|
|
cycleTester = *a3;
|
|
|
|
else
|
|
|
|
cycleTester = nullptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cycleTester = nullptr;
|
|
|
|
|
|
|
|
if (t == cycleTester)
|
|
|
|
throw std::runtime_error("Luau::follow detected a TypeVar cycle!!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<TypeId> flattenIntersection(TypeId ty)
|
|
|
|
{
|
|
|
|
if (!get<IntersectionTypeVar>(follow(ty)))
|
|
|
|
return {ty};
|
|
|
|
|
|
|
|
std::unordered_set<TypeId> seen;
|
|
|
|
std::deque<TypeId> queue{ty};
|
|
|
|
|
|
|
|
std::vector<TypeId> result;
|
|
|
|
|
|
|
|
while (!queue.empty())
|
|
|
|
{
|
|
|
|
TypeId current = follow(queue.front());
|
|
|
|
queue.pop_front();
|
|
|
|
|
|
|
|
if (seen.find(current) != seen.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
seen.insert(current);
|
|
|
|
|
|
|
|
if (auto itv = get<IntersectionTypeVar>(current))
|
|
|
|
{
|
|
|
|
for (TypeId ty : itv->parts)
|
|
|
|
queue.push_back(ty);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result.push_back(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isPrim(TypeId ty, PrimitiveTypeVar::Type primType)
|
|
|
|
{
|
|
|
|
auto p = get<PrimitiveTypeVar>(follow(ty));
|
|
|
|
return p && p->type == primType;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isNil(TypeId ty)
|
|
|
|
{
|
|
|
|
return isPrim(ty, PrimitiveTypeVar::NilType);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isBoolean(TypeId ty)
|
|
|
|
{
|
2022-02-24 23:53:37 +00:00
|
|
|
if (isPrim(ty, PrimitiveTypeVar::Boolean) || get<BooleanSingleton>(get<SingletonTypeVar>(follow(ty))))
|
|
|
|
return true;
|
2022-01-27 23:46:05 +00:00
|
|
|
|
2022-02-24 23:53:37 +00:00
|
|
|
if (auto utv = get<UnionTypeVar>(follow(ty)))
|
|
|
|
return std::all_of(begin(utv), end(utv), isBoolean);
|
2022-01-27 23:46:05 +00:00
|
|
|
|
2022-02-24 23:53:37 +00:00
|
|
|
return false;
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isNumber(TypeId ty)
|
|
|
|
{
|
|
|
|
return isPrim(ty, PrimitiveTypeVar::Number);
|
|
|
|
}
|
|
|
|
|
2022-03-04 16:36:33 +00:00
|
|
|
// Returns true when ty is a subtype of string
|
2021-10-29 21:25:12 +01:00
|
|
|
bool isString(TypeId ty)
|
|
|
|
{
|
2022-07-14 23:52:26 +01:00
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
if (isPrim(ty, PrimitiveTypeVar::String) || get<StringSingleton>(get<SingletonTypeVar>(ty)))
|
2022-02-24 23:53:37 +00:00
|
|
|
return true;
|
2022-01-27 23:46:05 +00:00
|
|
|
|
2022-07-14 23:52:26 +01:00
|
|
|
if (auto utv = get<UnionTypeVar>(ty))
|
2022-02-24 23:53:37 +00:00
|
|
|
return std::all_of(begin(utv), end(utv), isString);
|
2022-01-27 23:46:05 +00:00
|
|
|
|
2022-02-24 23:53:37 +00:00
|
|
|
return false;
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-03-04 16:36:33 +00:00
|
|
|
// Returns true when ty is a supertype of string
|
|
|
|
bool maybeString(TypeId ty)
|
|
|
|
{
|
2022-06-24 02:56:00 +01:00
|
|
|
ty = follow(ty);
|
2022-04-15 00:57:43 +01:00
|
|
|
|
2022-07-14 23:52:26 +01:00
|
|
|
if (isPrim(ty, PrimitiveTypeVar::String) || get<AnyTypeVar>(ty))
|
|
|
|
return true;
|
2022-03-04 16:36:33 +00:00
|
|
|
|
2022-06-24 02:56:00 +01:00
|
|
|
if (auto utv = get<UnionTypeVar>(ty))
|
|
|
|
return std::any_of(begin(utv), end(utv), maybeString);
|
2022-03-04 16:36:33 +00:00
|
|
|
|
2022-06-24 02:56:00 +01:00
|
|
|
return false;
|
2022-03-04 16:36:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
bool isThread(TypeId ty)
|
|
|
|
{
|
|
|
|
return isPrim(ty, PrimitiveTypeVar::Thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isOptional(TypeId ty)
|
|
|
|
{
|
|
|
|
if (isNil(ty))
|
|
|
|
return true;
|
|
|
|
|
2022-03-18 00:46:04 +00:00
|
|
|
ty = follow(ty);
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
if (get<AnyTypeVar>(ty) || (FFlag::LuauUnknownAndNeverType && get<UnknownTypeVar>(ty)))
|
2022-03-18 00:46:04 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
auto utv = get<UnionTypeVar>(ty);
|
2022-02-24 23:53:37 +00:00
|
|
|
if (!utv)
|
2022-01-27 23:46:05 +00:00
|
|
|
return false;
|
2022-02-24 23:53:37 +00:00
|
|
|
|
2022-05-20 01:02:24 +01:00
|
|
|
return std::any_of(begin(utv), end(utv), isOptional);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isTableIntersection(TypeId ty)
|
|
|
|
{
|
2021-11-05 02:34:35 +00:00
|
|
|
if (!get<IntersectionTypeVar>(follow(ty)))
|
2021-10-29 21:25:12 +01:00
|
|
|
return false;
|
2021-11-05 02:34:35 +00:00
|
|
|
|
|
|
|
std::vector<TypeId> parts = flattenIntersection(ty);
|
|
|
|
return std::all_of(parts.begin(), parts.end(), getTableType);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isOverloadedFunction(TypeId ty)
|
|
|
|
{
|
|
|
|
if (!get<IntersectionTypeVar>(follow(ty)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto isFunction = [](TypeId part) -> bool {
|
|
|
|
return get<FunctionTypeVar>(part);
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<TypeId> parts = flattenIntersection(ty);
|
|
|
|
return std::all_of(parts.begin(), parts.end(), isFunction);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<TypeId> getMetatable(TypeId type)
|
|
|
|
{
|
2022-07-14 23:52:26 +01:00
|
|
|
type = follow(type);
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
if (const MetatableTypeVar* mtType = get<MetatableTypeVar>(type))
|
|
|
|
return mtType->metatable;
|
|
|
|
else if (const ClassTypeVar* classType = get<ClassTypeVar>(type))
|
|
|
|
return classType->metatable;
|
2022-02-24 23:53:37 +00:00
|
|
|
else if (isString(type))
|
2022-01-27 23:46:05 +00:00
|
|
|
{
|
2022-02-24 23:53:37 +00:00
|
|
|
auto ptv = get<PrimitiveTypeVar>(getSingletonTypes().stringType);
|
|
|
|
LUAU_ASSERT(ptv && ptv->metatable);
|
|
|
|
return ptv->metatable;
|
2022-01-27 23:46:05 +00:00
|
|
|
}
|
2022-02-24 23:53:37 +00:00
|
|
|
|
|
|
|
return std::nullopt;
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const TableTypeVar* getTableType(TypeId type)
|
|
|
|
{
|
2022-02-24 23:53:37 +00:00
|
|
|
type = follow(type);
|
2022-02-04 16:45:57 +00:00
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
if (const TableTypeVar* ttv = get<TableTypeVar>(type))
|
|
|
|
return ttv;
|
|
|
|
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(type))
|
2022-02-24 23:53:37 +00:00
|
|
|
return get<TableTypeVar>(follow(mtv->table));
|
2021-10-29 21:25:12 +01:00
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
TableTypeVar* getMutableTableType(TypeId type)
|
|
|
|
{
|
|
|
|
return const_cast<TableTypeVar*>(getTableType(type));
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string* getName(TypeId type)
|
|
|
|
{
|
|
|
|
type = follow(type);
|
|
|
|
if (auto mtv = get<MetatableTypeVar>(type))
|
|
|
|
{
|
|
|
|
if (mtv->syntheticName)
|
|
|
|
return &*mtv->syntheticName;
|
2022-02-24 23:53:37 +00:00
|
|
|
type = follow(mtv->table);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (auto ttv = get<TableTypeVar>(type))
|
|
|
|
{
|
|
|
|
if (ttv->name)
|
|
|
|
return &*ttv->name;
|
|
|
|
if (ttv->syntheticName)
|
|
|
|
return &*ttv->syntheticName;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-03-24 22:04:14 +00:00
|
|
|
std::optional<ModuleName> getDefinitionModuleName(TypeId type)
|
|
|
|
{
|
|
|
|
type = follow(type);
|
|
|
|
|
|
|
|
if (auto ttv = get<TableTypeVar>(type))
|
|
|
|
{
|
|
|
|
if (!ttv->definitionModuleName.empty())
|
|
|
|
return ttv->definitionModuleName;
|
|
|
|
}
|
|
|
|
else if (auto ftv = get<FunctionTypeVar>(type))
|
|
|
|
{
|
|
|
|
if (ftv->definition)
|
|
|
|
return ftv->definition->definitionModuleName;
|
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
else if (auto ctv = get<ClassTypeVar>(type))
|
2022-04-21 22:44:27 +01:00
|
|
|
{
|
|
|
|
if (!ctv->definitionModuleName.empty())
|
|
|
|
return ctv->definitionModuleName;
|
|
|
|
}
|
2022-03-24 22:04:14 +00:00
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
bool isSubset(const UnionTypeVar& super, const UnionTypeVar& sub)
|
|
|
|
{
|
|
|
|
std::unordered_set<TypeId> superTypes;
|
|
|
|
|
|
|
|
for (TypeId id : super.options)
|
|
|
|
superTypes.insert(id);
|
|
|
|
|
|
|
|
for (TypeId id : sub.options)
|
|
|
|
{
|
|
|
|
if (superTypes.find(id) == superTypes.end())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// When typechecking an assignment `x = e`, we typecheck `x:T` and `e:U`,
|
|
|
|
// then instantiate U if `isGeneric(U)` is true, and `maybeGeneric(T)` is false.
|
|
|
|
bool isGeneric(TypeId ty)
|
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
if (auto ftv = get<FunctionTypeVar>(ty))
|
|
|
|
return ftv->generics.size() > 0 || ftv->genericPacks.size() > 0;
|
|
|
|
else
|
|
|
|
// TODO: recurse on type synonyms CLI-39914
|
|
|
|
// TODO: recurse on table types CLI-39914
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool maybeGeneric(TypeId ty)
|
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
if (FFlag::LuauMaybeGenericIntersectionTypes)
|
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
if (get<FreeTypeVar>(ty))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (auto ttv = get<TableTypeVar>(ty))
|
|
|
|
{
|
|
|
|
// TODO: recurse on table types CLI-39914
|
|
|
|
(void)ttv;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto itv = get<IntersectionTypeVar>(ty))
|
|
|
|
{
|
|
|
|
return std::any_of(begin(itv), end(itv), maybeGeneric);
|
|
|
|
}
|
|
|
|
|
|
|
|
return isGeneric(ty);
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
ty = follow(ty);
|
2021-11-12 14:56:25 +00:00
|
|
|
if (get<FreeTypeVar>(ty))
|
2021-11-12 14:27:34 +00:00
|
|
|
return true;
|
2021-10-29 21:25:12 +01:00
|
|
|
else if (auto ttv = get<TableTypeVar>(ty))
|
|
|
|
{
|
|
|
|
// TODO: recurse on table types CLI-39914
|
|
|
|
(void)ttv;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return isGeneric(ty);
|
|
|
|
}
|
|
|
|
|
2021-11-19 16:10:07 +00:00
|
|
|
bool maybeSingleton(TypeId ty)
|
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
if (get<SingletonTypeVar>(ty))
|
|
|
|
return true;
|
|
|
|
if (const UnionTypeVar* utv = get<UnionTypeVar>(ty))
|
|
|
|
for (TypeId option : utv)
|
|
|
|
if (get<SingletonTypeVar>(follow(option)))
|
|
|
|
return true;
|
|
|
|
return false;
|
2022-01-21 17:00:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount)
|
|
|
|
{
|
2022-06-24 02:56:00 +01:00
|
|
|
RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit);
|
2022-01-21 17:00:19 +00:00
|
|
|
|
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
if (seen.contains(ty))
|
|
|
|
return true;
|
|
|
|
|
2022-05-20 01:02:24 +01:00
|
|
|
if (isString(ty) || get<AnyTypeVar>(ty) || get<TableTypeVar>(ty) || get<MetatableTypeVar>(ty))
|
2022-01-21 17:00:19 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (auto uty = get<UnionTypeVar>(ty))
|
|
|
|
{
|
|
|
|
seen.insert(ty);
|
|
|
|
|
|
|
|
for (TypeId part : uty->options)
|
|
|
|
{
|
|
|
|
if (!hasLength(part, seen, recursionCount))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto ity = get<IntersectionTypeVar>(ty))
|
|
|
|
{
|
|
|
|
seen.insert(ty);
|
|
|
|
|
|
|
|
for (TypeId part : ity->parts)
|
|
|
|
{
|
|
|
|
if (hasLength(part, seen, recursionCount))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2021-11-19 16:10:07 +00:00
|
|
|
}
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
BlockedTypeVar::BlockedTypeVar()
|
|
|
|
: index(++nextIndex)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int BlockedTypeVar::nextIndex = 0;
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
PendingExpansionTypeVar::PendingExpansionTypeVar(TypeFun fn, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
|
|
|
: fn(fn)
|
|
|
|
, typeArguments(typeArguments)
|
|
|
|
, packArguments(packArguments)
|
|
|
|
, index(++nextIndex)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t PendingExpansionTypeVar::nextIndex = 0;
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
FunctionTypeVar::FunctionTypeVar(TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn, bool hasSelf)
|
2021-10-29 21:25:12 +01:00
|
|
|
: argTypes(argTypes)
|
2022-06-17 02:05:14 +01:00
|
|
|
, retTypes(retTypes)
|
2021-10-29 21:25:12 +01:00
|
|
|
, definition(std::move(defn))
|
|
|
|
, hasSelf(hasSelf)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
FunctionTypeVar::FunctionTypeVar(TypeLevel level, TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn, bool hasSelf)
|
2021-10-29 21:25:12 +01:00
|
|
|
: level(level)
|
|
|
|
, argTypes(argTypes)
|
2022-06-17 02:05:14 +01:00
|
|
|
, retTypes(retTypes)
|
2021-10-29 21:25:12 +01:00
|
|
|
, definition(std::move(defn))
|
|
|
|
, hasSelf(hasSelf)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
FunctionTypeVar::FunctionTypeVar(std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes, TypePackId retTypes,
|
2021-10-29 21:25:12 +01:00
|
|
|
std::optional<FunctionDefinition> defn, bool hasSelf)
|
|
|
|
: generics(generics)
|
|
|
|
, genericPacks(genericPacks)
|
|
|
|
, argTypes(argTypes)
|
2022-06-17 02:05:14 +01:00
|
|
|
, retTypes(retTypes)
|
2021-10-29 21:25:12 +01:00
|
|
|
, definition(std::move(defn))
|
|
|
|
, hasSelf(hasSelf)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
FunctionTypeVar::FunctionTypeVar(TypeLevel level, std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes,
|
2022-06-17 02:05:14 +01:00
|
|
|
TypePackId retTypes, std::optional<FunctionDefinition> defn, bool hasSelf)
|
2021-10-29 21:25:12 +01:00
|
|
|
: level(level)
|
|
|
|
, generics(generics)
|
|
|
|
, genericPacks(genericPacks)
|
|
|
|
, argTypes(argTypes)
|
2022-06-17 02:05:14 +01:00
|
|
|
, retTypes(retTypes)
|
2021-10-29 21:25:12 +01:00
|
|
|
, definition(std::move(defn))
|
|
|
|
, hasSelf(hasSelf)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TableTypeVar::TableTypeVar(TableState state, TypeLevel level)
|
|
|
|
: state(state)
|
|
|
|
, level(level)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TableTypeVar::TableTypeVar(const Props& props, const std::optional<TableIndexer>& indexer, TypeLevel level, TableState state)
|
|
|
|
: props(props)
|
|
|
|
, indexer(indexer)
|
|
|
|
, state(state)
|
|
|
|
, level(level)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test TypeVars for equivalence
|
|
|
|
// More complex than we'd like because TypeVars can self-reference.
|
|
|
|
|
|
|
|
bool areSeen(SeenSet& seen, const void* lhs, const void* rhs)
|
|
|
|
{
|
|
|
|
if (lhs == rhs)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
auto p = std::make_pair(const_cast<void*>(lhs), const_cast<void*>(rhs));
|
|
|
|
if (seen.find(p) != seen.end())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
seen.insert(p);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool areEqual(SeenSet& seen, const FunctionTypeVar& lhs, const FunctionTypeVar& rhs)
|
|
|
|
{
|
|
|
|
if (areSeen(seen, &lhs, &rhs))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// TODO: check generics CLI-39915
|
|
|
|
|
|
|
|
if (!areEqual(seen, *lhs.argTypes, *rhs.argTypes))
|
|
|
|
return false;
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
if (!areEqual(seen, *lhs.retTypes, *rhs.retTypes))
|
2021-10-29 21:25:12 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool areEqual(SeenSet& seen, const TableTypeVar& lhs, const TableTypeVar& rhs)
|
|
|
|
{
|
|
|
|
if (areSeen(seen, &lhs, &rhs))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (lhs.state != rhs.state)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (lhs.props.size() != rhs.props.size())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (bool(lhs.indexer) != bool(rhs.indexer))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (lhs.indexer && rhs.indexer)
|
|
|
|
{
|
|
|
|
if (!areEqual(seen, *lhs.indexer->indexType, *rhs.indexer->indexType))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!areEqual(seen, *lhs.indexer->indexResultType, *rhs.indexer->indexResultType))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto l = lhs.props.begin();
|
|
|
|
auto r = rhs.props.begin();
|
|
|
|
|
|
|
|
while (l != lhs.props.end())
|
|
|
|
{
|
|
|
|
if (l->first != r->first)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!areEqual(seen, *l->second.type, *r->second.type))
|
|
|
|
return false;
|
|
|
|
++l;
|
|
|
|
++r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool areEqual(SeenSet& seen, const MetatableTypeVar& lhs, const MetatableTypeVar& rhs)
|
|
|
|
{
|
2022-02-18 01:18:01 +00:00
|
|
|
if (areSeen(seen, &lhs, &rhs))
|
2022-01-14 16:20:09 +00:00
|
|
|
return true;
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
return areEqual(seen, *lhs.table, *rhs.table) && areEqual(seen, *lhs.metatable, *rhs.metatable);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool areEqual(SeenSet& seen, const TypeVar& lhs, const TypeVar& rhs)
|
|
|
|
{
|
|
|
|
if (auto bound = get_if<BoundTypeVar>(&lhs.ty))
|
|
|
|
return areEqual(seen, *bound->boundTo, rhs);
|
|
|
|
|
|
|
|
if (auto bound = get_if<BoundTypeVar>(&rhs.ty))
|
|
|
|
return areEqual(seen, lhs, *bound->boundTo);
|
|
|
|
|
|
|
|
if (lhs.ty.index() != rhs.ty.index())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
{
|
|
|
|
const FreeTypeVar* lf = get_if<FreeTypeVar>(&lhs.ty);
|
|
|
|
const FreeTypeVar* rf = get_if<FreeTypeVar>(&rhs.ty);
|
|
|
|
if (lf && rf)
|
|
|
|
return lf->index == rf->index;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const GenericTypeVar* lg = get_if<GenericTypeVar>(&lhs.ty);
|
|
|
|
const GenericTypeVar* rg = get_if<GenericTypeVar>(&rhs.ty);
|
|
|
|
if (lg && rg)
|
|
|
|
return lg->index == rg->index;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const PrimitiveTypeVar* lp = get_if<PrimitiveTypeVar>(&lhs.ty);
|
|
|
|
const PrimitiveTypeVar* rp = get_if<PrimitiveTypeVar>(&rhs.ty);
|
|
|
|
if (lp && rp)
|
|
|
|
return lp->type == rp->type;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const GenericTypeVar* lg = get_if<GenericTypeVar>(&lhs.ty);
|
|
|
|
const GenericTypeVar* rg = get_if<GenericTypeVar>(&rhs.ty);
|
|
|
|
if (lg && rg)
|
|
|
|
return lg->index == rg->index;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const ErrorTypeVar* le = get_if<ErrorTypeVar>(&lhs.ty);
|
|
|
|
const ErrorTypeVar* re = get_if<ErrorTypeVar>(&rhs.ty);
|
|
|
|
if (le && re)
|
|
|
|
return le->index == re->index;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const FunctionTypeVar* lf = get_if<FunctionTypeVar>(&lhs.ty);
|
|
|
|
const FunctionTypeVar* rf = get_if<FunctionTypeVar>(&rhs.ty);
|
|
|
|
if (lf && rf)
|
|
|
|
return areEqual(seen, *lf, *rf);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const TableTypeVar* lt = get_if<TableTypeVar>(&lhs.ty);
|
|
|
|
const TableTypeVar* rt = get_if<TableTypeVar>(&rhs.ty);
|
|
|
|
if (lt && rt)
|
|
|
|
return areEqual(seen, *lt, *rt);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const MetatableTypeVar* lmt = get_if<MetatableTypeVar>(&lhs.ty);
|
|
|
|
const MetatableTypeVar* rmt = get_if<MetatableTypeVar>(&rhs.ty);
|
|
|
|
|
|
|
|
if (lmt && rmt)
|
|
|
|
return areEqual(seen, *lmt, *rmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get_if<AnyTypeVar>(&lhs.ty) && get_if<AnyTypeVar>(&rhs.ty))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeVar* asMutable(TypeId ty)
|
|
|
|
{
|
|
|
|
return const_cast<TypeVar*>(ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TypeVar::operator==(const TypeVar& rhs) const
|
|
|
|
{
|
|
|
|
SeenSet seen;
|
|
|
|
return areEqual(seen, *this, rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TypeVar::operator!=(const TypeVar& rhs) const
|
|
|
|
{
|
|
|
|
SeenSet seen;
|
|
|
|
return !areEqual(seen, *this, rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeVar& TypeVar::operator=(const TypeVariant& rhs)
|
|
|
|
{
|
|
|
|
ty = rhs;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeVar& TypeVar::operator=(TypeVariant&& rhs)
|
|
|
|
{
|
|
|
|
ty = std::move(rhs);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2022-06-10 17:58:21 +01:00
|
|
|
TypeVar& TypeVar::operator=(const TypeVar& rhs)
|
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
LUAU_ASSERT(owningArena == rhs.owningArena);
|
|
|
|
LUAU_ASSERT(!rhs.persistent);
|
2022-06-10 17:58:21 +01:00
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
reassign(rhs);
|
2022-06-10 17:58:21 +01:00
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
TypeId makeFunction(TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> generics,
|
|
|
|
std::initializer_list<TypePackId> genericPacks, std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames,
|
|
|
|
std::initializer_list<TypeId> retTypes);
|
|
|
|
|
2021-11-12 14:27:34 +00:00
|
|
|
static TypeVar nilType_{PrimitiveTypeVar{PrimitiveTypeVar::NilType}, /*persistent*/ true};
|
|
|
|
static TypeVar numberType_{PrimitiveTypeVar{PrimitiveTypeVar::Number}, /*persistent*/ true};
|
|
|
|
static TypeVar stringType_{PrimitiveTypeVar{PrimitiveTypeVar::String}, /*persistent*/ true};
|
|
|
|
static TypeVar booleanType_{PrimitiveTypeVar{PrimitiveTypeVar::Boolean}, /*persistent*/ true};
|
|
|
|
static TypeVar threadType_{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persistent*/ true};
|
2022-04-07 22:29:01 +01:00
|
|
|
static TypeVar trueType_{SingletonTypeVar{BooleanSingleton{true}}, /*persistent*/ true};
|
|
|
|
static TypeVar falseType_{SingletonTypeVar{BooleanSingleton{false}}, /*persistent*/ true};
|
2022-04-15 00:57:43 +01:00
|
|
|
static TypeVar anyType_{AnyTypeVar{}, /*persistent*/ true};
|
2022-07-08 02:22:39 +01:00
|
|
|
static TypeVar unknownType_{UnknownTypeVar{}, /*persistent*/ true};
|
|
|
|
static TypeVar neverType_{NeverTypeVar{}, /*persistent*/ true};
|
2022-04-15 00:57:43 +01:00
|
|
|
static TypeVar errorType_{ErrorTypeVar{}, /*persistent*/ true};
|
2021-11-12 14:27:34 +00:00
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
static TypePackVar anyTypePack_{VariadicTypePack{&anyType_}, /*persistent*/ true};
|
|
|
|
static TypePackVar errorTypePack_{Unifiable::Error{}, /*persistent*/ true};
|
|
|
|
static TypePackVar neverTypePack_{VariadicTypePack{&neverType_}, /*persistent*/ true};
|
|
|
|
static TypePackVar uninhabitableTypePack_{TypePack{{&neverType_}, &neverTypePack_}, /*persistent*/ true};
|
2021-11-12 14:27:34 +00:00
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
SingletonTypes::SingletonTypes()
|
2021-11-12 14:27:34 +00:00
|
|
|
: nilType(&nilType_)
|
|
|
|
, numberType(&numberType_)
|
|
|
|
, stringType(&stringType_)
|
|
|
|
, booleanType(&booleanType_)
|
|
|
|
, threadType(&threadType_)
|
2022-04-07 22:29:01 +01:00
|
|
|
, trueType(&trueType_)
|
|
|
|
, falseType(&falseType_)
|
2021-11-12 14:27:34 +00:00
|
|
|
, anyType(&anyType_)
|
2022-07-08 02:22:39 +01:00
|
|
|
, unknownType(&unknownType_)
|
|
|
|
, neverType(&neverType_)
|
2021-11-12 14:27:34 +00:00
|
|
|
, anyTypePack(&anyTypePack_)
|
2022-07-08 02:22:39 +01:00
|
|
|
, neverTypePack(&neverTypePack_)
|
|
|
|
, uninhabitableTypePack(&uninhabitableTypePack_)
|
2021-11-12 14:27:34 +00:00
|
|
|
, arena(new TypeArena)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
|
|
|
TypeId stringMetatable = makeStringMetatable();
|
2021-12-10 22:05:05 +00:00
|
|
|
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, stringMetatable};
|
2021-10-29 21:25:12 +01:00
|
|
|
persist(stringMetatable);
|
2021-12-10 22:05:05 +00:00
|
|
|
|
|
|
|
debugFreezeArena = FFlag::DebugLuauFreezeArena;
|
2021-10-29 21:25:12 +01:00
|
|
|
freeze(*arena);
|
|
|
|
}
|
|
|
|
|
2021-12-10 22:05:05 +00:00
|
|
|
SingletonTypes::~SingletonTypes()
|
|
|
|
{
|
|
|
|
// Destroy the arena with the same memory management flags it was created with
|
|
|
|
bool prevFlag = FFlag::DebugLuauFreezeArena;
|
|
|
|
FFlag::DebugLuauFreezeArena.value = debugFreezeArena;
|
|
|
|
|
|
|
|
unfreeze(*arena);
|
|
|
|
arena.reset(nullptr);
|
|
|
|
|
|
|
|
FFlag::DebugLuauFreezeArena.value = prevFlag;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
TypeId SingletonTypes::makeStringMetatable()
|
|
|
|
{
|
|
|
|
const TypeId optionalNumber = arena->addType(UnionTypeVar{{nilType, numberType}});
|
|
|
|
const TypeId optionalString = arena->addType(UnionTypeVar{{nilType, stringType}});
|
2022-04-15 00:57:43 +01:00
|
|
|
const TypeId optionalBoolean = arena->addType(UnionTypeVar{{nilType, booleanType}});
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
const TypePackId oneStringPack = arena->addTypePack({stringType});
|
|
|
|
const TypePackId anyTypePack = arena->addTypePack(TypePackVar{VariadicTypePack{anyType}, true});
|
|
|
|
|
|
|
|
FunctionTypeVar formatFTV{arena->addTypePack(TypePack{{stringType}, anyTypePack}), oneStringPack};
|
|
|
|
formatFTV.magicFunction = &magicFunctionFormat;
|
|
|
|
const TypeId formatFn = arena->addType(formatFTV);
|
|
|
|
|
|
|
|
const TypePackId emptyPack = arena->addTypePack({});
|
|
|
|
const TypePackId stringVariadicList = arena->addTypePack(TypePackVar{VariadicTypePack{stringType}});
|
|
|
|
const TypePackId numberVariadicList = arena->addTypePack(TypePackVar{VariadicTypePack{numberType}});
|
|
|
|
|
|
|
|
const TypeId stringToStringType = makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType});
|
|
|
|
|
|
|
|
const TypeId replArgType = arena->addType(
|
|
|
|
UnionTypeVar{{stringType, arena->addType(TableTypeVar({}, TableIndexer(stringType, stringType), TypeLevel{}, TableState::Generic)),
|
|
|
|
makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType})}});
|
|
|
|
const TypeId gsubFunc = makeFunction(*arena, stringType, {}, {}, {stringType, replArgType, optionalNumber}, {}, {stringType, numberType});
|
|
|
|
const TypeId gmatchFunc =
|
|
|
|
makeFunction(*arena, stringType, {}, {}, {stringType}, {}, {arena->addType(FunctionTypeVar{emptyPack, stringVariadicList})});
|
2022-07-08 02:22:39 +01:00
|
|
|
attachMagicFunction(gmatchFunc, magicFunctionGmatch);
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2022-07-14 23:52:26 +01:00
|
|
|
const TypeId matchFunc = arena->addType(FunctionTypeVar{arena->addTypePack({stringType, stringType, optionalNumber}),
|
|
|
|
arena->addTypePack(TypePackVar{VariadicTypePack{FFlag::LuauDeduceFindMatchReturnTypes ? stringType : optionalString}})});
|
|
|
|
attachMagicFunction(matchFunc, magicFunctionMatch);
|
|
|
|
|
|
|
|
const TypeId findFunc = arena->addType(FunctionTypeVar{arena->addTypePack({stringType, stringType, optionalNumber, optionalBoolean}),
|
|
|
|
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})});
|
|
|
|
attachMagicFunction(findFunc, magicFunctionFind);
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
TableTypeVar::Props stringLib = {
|
|
|
|
{"byte", {arena->addType(FunctionTypeVar{arena->addTypePack({stringType, optionalNumber, optionalNumber}), numberVariadicList})}},
|
2022-06-03 23:15:45 +01:00
|
|
|
{"char", {arena->addType(FunctionTypeVar{numberVariadicList, arena->addTypePack({stringType})})}},
|
2022-07-14 23:52:26 +01:00
|
|
|
{"find", {findFunc}},
|
2021-10-29 21:25:12 +01:00
|
|
|
{"format", {formatFn}}, // FIXME
|
|
|
|
{"gmatch", {gmatchFunc}},
|
|
|
|
{"gsub", {gsubFunc}},
|
|
|
|
{"len", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType})}},
|
|
|
|
{"lower", {stringToStringType}},
|
2022-07-14 23:52:26 +01:00
|
|
|
{"match", {matchFunc}},
|
2021-10-29 21:25:12 +01:00
|
|
|
{"rep", {makeFunction(*arena, stringType, {}, {}, {numberType}, {}, {stringType})}},
|
|
|
|
{"reverse", {stringToStringType}},
|
|
|
|
{"sub", {makeFunction(*arena, stringType, {}, {}, {numberType, optionalNumber}, {}, {stringType})}},
|
|
|
|
{"upper", {stringToStringType}},
|
2022-01-27 23:46:05 +00:00
|
|
|
{"split", {makeFunction(*arena, stringType, {}, {}, {optionalString}, {},
|
2022-03-04 16:36:33 +00:00
|
|
|
{arena->addType(TableTypeVar{{}, TableIndexer{numberType, stringType}, TypeLevel{}, TableState::Sealed})})}},
|
2021-10-29 21:25:12 +01:00
|
|
|
{"pack", {arena->addType(FunctionTypeVar{
|
|
|
|
arena->addTypePack(TypePack{{stringType}, anyTypePack}),
|
|
|
|
oneStringPack,
|
|
|
|
})}},
|
|
|
|
{"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType})}},
|
|
|
|
{"unpack", {arena->addType(FunctionTypeVar{
|
|
|
|
arena->addTypePack(TypePack{{stringType, stringType, optionalNumber}}),
|
|
|
|
anyTypePack,
|
|
|
|
})}},
|
|
|
|
};
|
|
|
|
|
|
|
|
assignPropDocumentationSymbols(stringLib, "@luau/global/string");
|
|
|
|
|
|
|
|
TypeId tableType = arena->addType(TableTypeVar{std::move(stringLib), std::nullopt, TypeLevel{}, TableState::Sealed});
|
|
|
|
|
2021-12-10 22:05:05 +00:00
|
|
|
if (TableTypeVar* ttv = getMutable<TableTypeVar>(tableType))
|
|
|
|
ttv->name = "string";
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
return arena->addType(TableTypeVar{{{{"__index", {tableType}}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
|
|
|
}
|
|
|
|
|
2021-11-19 16:10:07 +00:00
|
|
|
TypeId SingletonTypes::errorRecoveryType()
|
|
|
|
{
|
|
|
|
return &errorType_;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypePackId SingletonTypes::errorRecoveryTypePack()
|
|
|
|
{
|
|
|
|
return &errorTypePack_;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeId SingletonTypes::errorRecoveryType(TypeId guess)
|
|
|
|
{
|
2022-04-29 02:24:24 +01:00
|
|
|
return guess;
|
2021-11-19 16:10:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TypePackId SingletonTypes::errorRecoveryTypePack(TypePackId guess)
|
|
|
|
{
|
2022-04-29 02:24:24 +01:00
|
|
|
return guess;
|
2021-11-19 16:10:07 +00:00
|
|
|
}
|
|
|
|
|
2021-12-10 22:05:05 +00:00
|
|
|
SingletonTypes& getSingletonTypes()
|
|
|
|
{
|
|
|
|
static SingletonTypes singletonTypes;
|
|
|
|
return singletonTypes;
|
|
|
|
}
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
void persist(TypeId ty)
|
|
|
|
{
|
|
|
|
std::deque<TypeId> queue{ty};
|
|
|
|
|
|
|
|
while (!queue.empty())
|
|
|
|
{
|
|
|
|
TypeId t = queue.front();
|
|
|
|
queue.pop_front();
|
|
|
|
|
|
|
|
if (t->persistent)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
asMutable(t)->persistent = true;
|
2022-04-15 00:57:43 +01:00
|
|
|
asMutable(t)->normal = true; // all persistent types are assumed to be normal
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
if (auto btv = get<BoundTypeVar>(t))
|
|
|
|
queue.push_back(btv->boundTo);
|
|
|
|
else if (auto ftv = get<FunctionTypeVar>(t))
|
|
|
|
{
|
|
|
|
persist(ftv->argTypes);
|
2022-06-17 02:05:14 +01:00
|
|
|
persist(ftv->retTypes);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
else if (auto ttv = get<TableTypeVar>(t))
|
|
|
|
{
|
2022-03-04 16:36:33 +00:00
|
|
|
LUAU_ASSERT(ttv->state != TableState::Free && ttv->state != TableState::Unsealed);
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
for (const auto& [_name, prop] : ttv->props)
|
|
|
|
queue.push_back(prop.type);
|
|
|
|
|
|
|
|
if (ttv->indexer)
|
|
|
|
{
|
|
|
|
queue.push_back(ttv->indexer->indexType);
|
|
|
|
queue.push_back(ttv->indexer->indexResultType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (auto ctv = get<ClassTypeVar>(t))
|
|
|
|
{
|
|
|
|
for (const auto& [_name, prop] : ctv->props)
|
|
|
|
queue.push_back(prop.type);
|
|
|
|
}
|
|
|
|
else if (auto utv = get<UnionTypeVar>(t))
|
|
|
|
{
|
|
|
|
for (TypeId opt : utv->options)
|
|
|
|
queue.push_back(opt);
|
|
|
|
}
|
|
|
|
else if (auto itv = get<IntersectionTypeVar>(t))
|
|
|
|
{
|
|
|
|
for (TypeId opt : itv->parts)
|
|
|
|
queue.push_back(opt);
|
|
|
|
}
|
2022-04-15 00:57:43 +01:00
|
|
|
else if (auto ctv = get<ConstrainedTypeVar>(t))
|
|
|
|
{
|
|
|
|
for (TypeId opt : ctv->parts)
|
|
|
|
queue.push_back(opt);
|
|
|
|
}
|
2021-12-10 22:05:05 +00:00
|
|
|
else if (auto mtv = get<MetatableTypeVar>(t))
|
|
|
|
{
|
|
|
|
queue.push_back(mtv->table);
|
|
|
|
queue.push_back(mtv->metatable);
|
|
|
|
}
|
|
|
|
else if (get<GenericTypeVar>(t) || get<AnyTypeVar>(t) || get<FreeTypeVar>(t) || get<SingletonTypeVar>(t) || get<PrimitiveTypeVar>(t))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(!"TypeId is not supported in a persist call");
|
|
|
|
}
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void persist(TypePackId tp)
|
|
|
|
{
|
|
|
|
if (tp->persistent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
asMutable(tp)->persistent = true;
|
|
|
|
|
|
|
|
if (auto p = get<TypePack>(tp))
|
|
|
|
{
|
|
|
|
for (TypeId ty : p->head)
|
|
|
|
persist(ty);
|
|
|
|
if (p->tail)
|
|
|
|
persist(*p->tail);
|
|
|
|
}
|
2021-12-10 22:05:05 +00:00
|
|
|
else if (auto vtp = get<VariadicTypePack>(tp))
|
|
|
|
{
|
|
|
|
persist(vtp->ty);
|
|
|
|
}
|
|
|
|
else if (get<GenericTypePack>(tp))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(!"TypePackId is not supported in a persist call");
|
|
|
|
}
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const TypeLevel* getLevel(TypeId ty)
|
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
if (auto ftv = get<Unifiable::Free>(ty))
|
|
|
|
return &ftv->level;
|
|
|
|
else if (auto ttv = get<TableTypeVar>(ty))
|
|
|
|
return &ttv->level;
|
|
|
|
else if (auto ftv = get<FunctionTypeVar>(ty))
|
|
|
|
return &ftv->level;
|
2022-07-08 02:22:39 +01:00
|
|
|
else if (auto ctv = get<ConstrainedTypeVar>(ty))
|
|
|
|
return &ctv->level;
|
2021-10-29 21:25:12 +01:00
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeLevel* getMutableLevel(TypeId ty)
|
|
|
|
{
|
|
|
|
return const_cast<TypeLevel*>(getLevel(ty));
|
|
|
|
}
|
|
|
|
|
2022-04-15 00:57:43 +01:00
|
|
|
std::optional<TypeLevel> getLevel(TypePackId tp)
|
|
|
|
{
|
|
|
|
tp = follow(tp);
|
|
|
|
|
|
|
|
if (auto ftv = get<Unifiable::Free>(tp))
|
|
|
|
return ftv->level;
|
|
|
|
else
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
const Property* lookupClassProp(const ClassTypeVar* cls, const Name& name)
|
|
|
|
{
|
|
|
|
while (cls)
|
|
|
|
{
|
|
|
|
auto it = cls->props.find(name);
|
|
|
|
if (it != cls->props.end())
|
|
|
|
return &it->second;
|
|
|
|
|
|
|
|
if (cls->parent)
|
|
|
|
cls = get<ClassTypeVar>(*cls->parent);
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
LUAU_ASSERT(cls);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isSubclass(const ClassTypeVar* cls, const ClassTypeVar* parent)
|
|
|
|
{
|
|
|
|
while (cls)
|
|
|
|
{
|
|
|
|
if (cls == parent)
|
|
|
|
return true;
|
|
|
|
else if (!cls->parent)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
cls = get<ClassTypeVar>(*cls->parent);
|
|
|
|
LUAU_ASSERT(cls);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
const std::vector<TypeId>& getTypes(const UnionTypeVar* utv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return utv->options;
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
const std::vector<TypeId>& getTypes(const IntersectionTypeVar* itv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return itv->parts;
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
const std::vector<TypeId>& getTypes(const ConstrainedTypeVar* ctv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return ctv->parts;
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
UnionTypeVarIterator begin(const UnionTypeVar* utv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return UnionTypeVarIterator{utv};
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
UnionTypeVarIterator end(const UnionTypeVar* utv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return UnionTypeVarIterator{};
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
IntersectionTypeVarIterator begin(const IntersectionTypeVar* itv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return IntersectionTypeVarIterator{itv};
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
IntersectionTypeVarIterator end(const IntersectionTypeVar* itv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return IntersectionTypeVarIterator{};
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
ConstrainedTypeVarIterator begin(const ConstrainedTypeVar* ctv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return ConstrainedTypeVarIterator{ctv};
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
ConstrainedTypeVarIterator end(const ConstrainedTypeVar* ctv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-07-08 02:22:39 +01:00
|
|
|
return ConstrainedTypeVarIterator{};
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const char* data, size_t size)
|
|
|
|
{
|
2022-08-04 15:22:16 +01:00
|
|
|
const char* options = "cdiouxXeEfgGqs*";
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
std::vector<TypeId> result;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
{
|
|
|
|
if (data[i] == '%')
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
|
|
|
|
if (i < size && data[i] == '%')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// we just ignore all characters (including flags/precision) up until first alphabetic character
|
2022-08-04 15:22:16 +01:00
|
|
|
while (i < size && !(data[i] > 0 && (isalpha(data[i]) || data[i] == '*')))
|
2021-10-29 21:25:12 +01:00
|
|
|
i++;
|
|
|
|
|
|
|
|
if (i == size)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (data[i] == 'q' || data[i] == 's')
|
|
|
|
result.push_back(typechecker.stringType);
|
2022-08-04 15:22:16 +01:00
|
|
|
else if (data[i] == '*')
|
|
|
|
result.push_back(typechecker.unknownType);
|
2021-10-29 21:25:12 +01:00
|
|
|
else if (strchr(options, data[i]))
|
|
|
|
result.push_back(typechecker.numberType);
|
|
|
|
else
|
2021-11-19 16:10:07 +00:00
|
|
|
result.push_back(typechecker.errorRecoveryType(typechecker.anyType));
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
std::optional<WithPredicate<TypePackId>> magicFunctionFormat(
|
|
|
|
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-06-17 02:05:14 +01:00
|
|
|
auto [paramPack, _predicates] = withPredicate;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
TypeArena& arena = typechecker.currentModule->internalTypes;
|
|
|
|
|
|
|
|
AstExprConstantString* fmt = nullptr;
|
|
|
|
if (auto index = expr.func->as<AstExprIndexName>(); index && expr.self)
|
|
|
|
{
|
|
|
|
if (auto group = index->expr->as<AstExprGroup>())
|
|
|
|
fmt = group->expr->as<AstExprConstantString>();
|
|
|
|
else
|
|
|
|
fmt = index->expr->as<AstExprConstantString>();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!expr.self && expr.args.size > 0)
|
|
|
|
fmt = expr.args.data[0]->as<AstExprConstantString>();
|
|
|
|
|
|
|
|
if (!fmt)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
std::vector<TypeId> expected = parseFormatString(typechecker, fmt->value.data, fmt->value.size);
|
|
|
|
const auto& [params, tail] = flatten(paramPack);
|
|
|
|
|
2022-01-06 23:26:14 +00:00
|
|
|
size_t paramOffset = 1;
|
|
|
|
size_t dataOffset = expr.self ? 0 : 1;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
// unify the prefix one argument at a time
|
2022-01-06 23:26:14 +00:00
|
|
|
for (size_t i = 0; i < expected.size() && i + paramOffset < params.size(); ++i)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-01-06 23:26:14 +00:00
|
|
|
Location location = expr.args.data[std::min(i + dataOffset, expr.args.size - 1)]->location;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
typechecker.unify(params[i + paramOffset], expected[i], scope, location);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// if we know the argument count or if we have too many arguments for sure, we can issue an error
|
2022-08-25 22:53:50 +01:00
|
|
|
if (FFlag::LuauStringFormatArgumentErrorFix)
|
|
|
|
{
|
|
|
|
size_t numActualParams = params.size();
|
|
|
|
size_t numExpectedParams = expected.size() + 1; // + 1 for the format string
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2022-08-25 22:53:50 +01:00
|
|
|
if (numExpectedParams != numActualParams && (!tail || numExpectedParams < numActualParams))
|
|
|
|
typechecker.reportError(TypeError{expr.location, CountMismatch{numExpectedParams, numActualParams}});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
size_t actualParamSize = params.size() - paramOffset;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2022-08-25 22:53:50 +01:00
|
|
|
if (expected.size() != actualParamSize && (!tail || expected.size() < actualParamSize))
|
|
|
|
typechecker.reportError(TypeError{expr.location, CountMismatch{expected.size(), actualParamSize}});
|
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
return WithPredicate<TypePackId>{arena.addTypePack({typechecker.stringType})};
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-07-08 02:22:39 +01:00
|
|
|
static std::vector<TypeId> parsePatternString(TypeChecker& typechecker, const char* data, size_t size)
|
|
|
|
{
|
|
|
|
std::vector<TypeId> result;
|
|
|
|
int depth = 0;
|
|
|
|
bool parsingSet = false;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
{
|
|
|
|
if (data[i] == '%')
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
if (!parsingSet && i < size && data[i] == 'b')
|
|
|
|
i += 2;
|
|
|
|
}
|
|
|
|
else if (!parsingSet && data[i] == '[')
|
|
|
|
{
|
|
|
|
parsingSet = true;
|
|
|
|
if (i + 1 < size && data[i + 1] == ']')
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
else if (parsingSet && data[i] == ']')
|
|
|
|
{
|
|
|
|
parsingSet = false;
|
|
|
|
}
|
|
|
|
else if (data[i] == '(')
|
|
|
|
{
|
|
|
|
if (parsingSet)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (i + 1 < size && data[i + 1] == ')')
|
|
|
|
{
|
|
|
|
i++;
|
|
|
|
result.push_back(typechecker.numberType);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
++depth;
|
|
|
|
result.push_back(typechecker.stringType);
|
|
|
|
}
|
|
|
|
else if (data[i] == ')')
|
|
|
|
{
|
|
|
|
if (parsingSet)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
--depth;
|
|
|
|
|
|
|
|
if (depth < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (depth != 0 || parsingSet)
|
|
|
|
return std::vector<TypeId>();
|
|
|
|
|
|
|
|
if (result.empty())
|
|
|
|
result.push_back(typechecker.stringType);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::optional<WithPredicate<TypePackId>> magicFunctionGmatch(
|
|
|
|
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
|
|
|
|
{
|
|
|
|
if (!FFlag::LuauDeduceGmatchReturnTypes)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
auto [paramPack, _predicates] = withPredicate;
|
|
|
|
const auto& [params, tail] = flatten(paramPack);
|
|
|
|
|
|
|
|
if (params.size() != 2)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
TypeArena& arena = typechecker.currentModule->internalTypes;
|
|
|
|
|
|
|
|
AstExprConstantString* pattern = nullptr;
|
|
|
|
size_t index = expr.self ? 0 : 1;
|
|
|
|
if (expr.args.size > index)
|
|
|
|
pattern = expr.args.data[index]->as<AstExprConstantString>();
|
|
|
|
|
|
|
|
if (!pattern)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
std::vector<TypeId> returnTypes = parsePatternString(typechecker, pattern->value.data, pattern->value.size);
|
|
|
|
|
|
|
|
if (returnTypes.empty())
|
|
|
|
return std::nullopt;
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
typechecker.unify(params[0], typechecker.stringType, scope, expr.args.data[0]->location);
|
2022-07-08 02:22:39 +01:00
|
|
|
|
|
|
|
const TypePackId emptyPack = arena.addTypePack({});
|
|
|
|
const TypePackId returnList = arena.addTypePack(returnTypes);
|
|
|
|
const TypeId iteratorType = arena.addType(FunctionTypeVar{emptyPack, returnList});
|
|
|
|
return WithPredicate<TypePackId>{arena.addTypePack({iteratorType})};
|
|
|
|
}
|
|
|
|
|
2022-07-14 23:52:26 +01:00
|
|
|
static std::optional<WithPredicate<TypePackId>> magicFunctionMatch(
|
|
|
|
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
|
|
|
|
{
|
|
|
|
if (!FFlag::LuauDeduceFindMatchReturnTypes)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
auto [paramPack, _predicates] = withPredicate;
|
|
|
|
const auto& [params, tail] = flatten(paramPack);
|
|
|
|
|
|
|
|
if (params.size() < 2 || params.size() > 3)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
TypeArena& arena = typechecker.currentModule->internalTypes;
|
|
|
|
|
|
|
|
AstExprConstantString* pattern = nullptr;
|
|
|
|
size_t patternIndex = expr.self ? 0 : 1;
|
|
|
|
if (expr.args.size > patternIndex)
|
|
|
|
pattern = expr.args.data[patternIndex]->as<AstExprConstantString>();
|
|
|
|
|
|
|
|
if (!pattern)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
std::vector<TypeId> returnTypes = parsePatternString(typechecker, pattern->value.data, pattern->value.size);
|
|
|
|
|
|
|
|
if (returnTypes.empty())
|
|
|
|
return std::nullopt;
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
typechecker.unify(params[0], typechecker.stringType, scope, expr.args.data[0]->location);
|
2022-07-14 23:52:26 +01:00
|
|
|
|
|
|
|
const TypeId optionalNumber = arena.addType(UnionTypeVar{{typechecker.nilType, typechecker.numberType}});
|
|
|
|
|
|
|
|
size_t initIndex = expr.self ? 1 : 2;
|
|
|
|
if (params.size() == 3 && expr.args.size > initIndex)
|
2022-08-18 22:32:08 +01:00
|
|
|
typechecker.unify(params[2], optionalNumber, scope, expr.args.data[initIndex]->location);
|
2022-07-14 23:52:26 +01:00
|
|
|
|
|
|
|
const TypePackId returnList = arena.addTypePack(returnTypes);
|
|
|
|
return WithPredicate<TypePackId>{returnList};
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::optional<WithPredicate<TypePackId>> magicFunctionFind(
|
|
|
|
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
|
|
|
|
{
|
|
|
|
if (!FFlag::LuauDeduceFindMatchReturnTypes)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
auto [paramPack, _predicates] = withPredicate;
|
|
|
|
const auto& [params, tail] = flatten(paramPack);
|
|
|
|
|
|
|
|
if (params.size() < 2 || params.size() > 4)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
TypeArena& arena = typechecker.currentModule->internalTypes;
|
|
|
|
|
|
|
|
AstExprConstantString* pattern = nullptr;
|
|
|
|
size_t patternIndex = expr.self ? 0 : 1;
|
|
|
|
if (expr.args.size > patternIndex)
|
|
|
|
pattern = expr.args.data[patternIndex]->as<AstExprConstantString>();
|
|
|
|
|
|
|
|
if (!pattern)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
bool plain = false;
|
|
|
|
size_t plainIndex = expr.self ? 2 : 3;
|
|
|
|
if (expr.args.size > plainIndex)
|
|
|
|
{
|
|
|
|
AstExprConstantBool* p = expr.args.data[plainIndex]->as<AstExprConstantBool>();
|
|
|
|
plain = p && p->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<TypeId> returnTypes;
|
|
|
|
if (!plain)
|
|
|
|
{
|
|
|
|
returnTypes = parsePatternString(typechecker, pattern->value.data, pattern->value.size);
|
|
|
|
|
|
|
|
if (returnTypes.empty())
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
typechecker.unify(params[0], typechecker.stringType, scope, expr.args.data[0]->location);
|
2022-07-14 23:52:26 +01:00
|
|
|
|
|
|
|
const TypeId optionalNumber = arena.addType(UnionTypeVar{{typechecker.nilType, typechecker.numberType}});
|
|
|
|
const TypeId optionalBoolean = arena.addType(UnionTypeVar{{typechecker.nilType, typechecker.booleanType}});
|
|
|
|
|
|
|
|
size_t initIndex = expr.self ? 1 : 2;
|
|
|
|
if (params.size() >= 3 && expr.args.size > initIndex)
|
2022-08-18 22:32:08 +01:00
|
|
|
typechecker.unify(params[2], optionalNumber, scope, expr.args.data[initIndex]->location);
|
2022-07-14 23:52:26 +01:00
|
|
|
|
|
|
|
if (params.size() == 4 && expr.args.size > plainIndex)
|
2022-08-18 22:32:08 +01:00
|
|
|
typechecker.unify(params[3], optionalBoolean, scope, expr.args.data[plainIndex]->location);
|
2022-07-14 23:52:26 +01:00
|
|
|
|
|
|
|
returnTypes.insert(returnTypes.begin(), {optionalNumber, optionalNumber});
|
|
|
|
|
|
|
|
const TypePackId returnList = arena.addTypePack(returnTypes);
|
|
|
|
return WithPredicate<TypePackId>{returnList};
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate)
|
|
|
|
{
|
|
|
|
type = follow(type);
|
|
|
|
|
|
|
|
if (auto utv = get<UnionTypeVar>(type))
|
|
|
|
{
|
|
|
|
std::set<TypeId> options;
|
|
|
|
for (TypeId option : utv)
|
|
|
|
if (auto out = predicate(follow(option)))
|
|
|
|
options.insert(*out);
|
|
|
|
|
|
|
|
return std::vector<TypeId>(options.begin(), options.end());
|
|
|
|
}
|
|
|
|
else if (auto out = predicate(type))
|
|
|
|
return {*out};
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-11-05 15:47:21 +00:00
|
|
|
static Tags* getTags(TypeId ty)
|
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
if (auto ftv = getMutable<FunctionTypeVar>(ty))
|
|
|
|
return &ftv->tags;
|
|
|
|
else if (auto ttv = getMutable<TableTypeVar>(ty))
|
|
|
|
return &ttv->tags;
|
|
|
|
else if (auto ctv = getMutable<ClassTypeVar>(ty))
|
|
|
|
return &ctv->tags;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void attachTag(TypeId ty, const std::string& tagName)
|
|
|
|
{
|
2022-01-27 23:46:05 +00:00
|
|
|
if (auto tags = getTags(ty))
|
|
|
|
tags->push_back(tagName);
|
2021-11-05 15:47:21 +00:00
|
|
|
else
|
2022-01-27 23:46:05 +00:00
|
|
|
LUAU_ASSERT(!"This TypeId does not support tags");
|
2021-11-05 15:47:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void attachTag(Property& prop, const std::string& tagName)
|
|
|
|
{
|
|
|
|
prop.tags.push_back(tagName);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We would ideally not expose this because it could cause a footgun.
|
|
|
|
// If the Base class has a tag and you ask if Derived has that tag, it would return false.
|
|
|
|
// Unfortunately, there's already use cases that's hard to disentangle. For now, we expose it.
|
|
|
|
bool hasTag(const Tags& tags, const std::string& tagName)
|
|
|
|
{
|
|
|
|
return std::find(tags.begin(), tags.end(), tagName) != tags.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasTag(TypeId ty, const std::string& tagName)
|
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
// We special case classes because getTags only returns a pointer to one vector of tags.
|
|
|
|
// But classes has multiple vector of tags, represented throughout the hierarchy.
|
|
|
|
if (auto ctv = get<ClassTypeVar>(ty))
|
|
|
|
{
|
|
|
|
while (ctv)
|
|
|
|
{
|
|
|
|
if (hasTag(ctv->tags, tagName))
|
|
|
|
return true;
|
|
|
|
else if (!ctv->parent)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ctv = get<ClassTypeVar>(*ctv->parent);
|
|
|
|
LUAU_ASSERT(ctv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (auto tags = getTags(ty))
|
|
|
|
return hasTag(*tags, tagName);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasTag(const Property& prop, const std::string& tagName)
|
|
|
|
{
|
|
|
|
return hasTag(prop.tags, tagName);
|
|
|
|
}
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
bool TypeFun::operator==(const TypeFun& rhs) const
|
|
|
|
{
|
|
|
|
return type == rhs.type && typeParams == rhs.typeParams && typePackParams == rhs.typePackParams;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GenericTypeDefinition::operator==(const GenericTypeDefinition& rhs) const
|
|
|
|
{
|
|
|
|
return ty == rhs.ty && defaultValue == rhs.defaultValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GenericTypePackDefinition::operator==(const GenericTypePackDefinition& rhs) const
|
|
|
|
{
|
|
|
|
return tp == rhs.tp && defaultValue == rhs.defaultValue;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
} // namespace Luau
|