2022-04-15 00:57:43 +01:00
|
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
|
|
|
|
|
|
#include "Luau/Normalize.h"
|
2022-10-07 01:23:29 +01:00
|
|
|
|
#include "Luau/ToString.h"
|
2022-04-15 00:57:43 +01:00
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
#include "Luau/Clone.h"
|
2022-09-23 20:17:25 +01:00
|
|
|
|
#include "Luau/Common.h"
|
2022-11-04 17:33:22 +00:00
|
|
|
|
#include "Luau/RecursionCounter.h"
|
2023-01-04 20:53:17 +00:00
|
|
|
|
#include "Luau/Type.h"
|
2022-04-15 00:57:43 +01:00
|
|
|
|
#include "Luau/Unifier.h"
|
|
|
|
|
|
|
|
|
|
LUAU_FASTFLAGVARIABLE(DebugLuauCopyBeforeNormalizing, false)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant, false)
|
2022-04-15 00:57:43 +01:00
|
|
|
|
|
|
|
|
|
// This could theoretically be 2000 on amd64, but x86 requires this.
|
|
|
|
|
LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
LUAU_FASTFLAGVARIABLE(LuauNegatedClassTypes, false);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
LUAU_FASTFLAGVARIABLE(LuauNegatedFunctionTypes, false);
|
2023-01-27 22:28:31 +00:00
|
|
|
|
LUAU_FASTFLAGVARIABLE(LuauNegatedTableTypes, false);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
LUAU_FASTFLAGVARIABLE(LuauNormalizeBlockedTypes, false);
|
2022-09-23 20:17:25 +01:00
|
|
|
|
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
2022-12-09 19:57:01 +00:00
|
|
|
|
LUAU_FASTFLAG(LuauUninhabitedSubAnything2)
|
2023-03-17 19:20:37 +00:00
|
|
|
|
LUAU_FASTFLAG(LuauTransitiveSubtyping)
|
2022-04-15 00:57:43 +01:00
|
|
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
|
{
|
2022-10-07 01:23:29 +01:00
|
|
|
|
void TypeIds::insert(TypeId ty)
|
|
|
|
|
{
|
|
|
|
|
ty = follow(ty);
|
|
|
|
|
auto [_, fresh] = types.insert(ty);
|
|
|
|
|
if (fresh)
|
|
|
|
|
{
|
|
|
|
|
order.push_back(ty);
|
|
|
|
|
hash ^= std::hash<TypeId>{}(ty);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TypeIds::clear()
|
|
|
|
|
{
|
|
|
|
|
order.clear();
|
|
|
|
|
types.clear();
|
|
|
|
|
hash = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-09 19:57:01 +00:00
|
|
|
|
TypeId TypeIds::front() const
|
|
|
|
|
{
|
|
|
|
|
return order.at(0);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
TypeIds::iterator TypeIds::begin()
|
|
|
|
|
{
|
|
|
|
|
return order.begin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeIds::iterator TypeIds::end()
|
|
|
|
|
{
|
|
|
|
|
return order.end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeIds::const_iterator TypeIds::begin() const
|
|
|
|
|
{
|
|
|
|
|
return order.begin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeIds::const_iterator TypeIds::end() const
|
|
|
|
|
{
|
|
|
|
|
return order.end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeIds::iterator TypeIds::erase(TypeIds::const_iterator it)
|
|
|
|
|
{
|
|
|
|
|
TypeId ty = *it;
|
|
|
|
|
types.erase(ty);
|
|
|
|
|
hash ^= std::hash<TypeId>{}(ty);
|
|
|
|
|
return order.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t TypeIds::size() const
|
|
|
|
|
{
|
|
|
|
|
return types.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TypeIds::empty() const
|
|
|
|
|
{
|
|
|
|
|
return types.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t TypeIds::count(TypeId ty) const
|
|
|
|
|
{
|
|
|
|
|
ty = follow(ty);
|
|
|
|
|
return types.count(ty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TypeIds::retain(const TypeIds& there)
|
|
|
|
|
{
|
|
|
|
|
for (auto it = begin(); it != end();)
|
|
|
|
|
{
|
|
|
|
|
if (there.count(*it))
|
|
|
|
|
it++;
|
|
|
|
|
else
|
|
|
|
|
it = erase(it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t TypeIds::getHash() const
|
|
|
|
|
{
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TypeIds::operator==(const TypeIds& there) const
|
|
|
|
|
{
|
|
|
|
|
return hash == there.hash && types == there.types;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
NormalizedStringType::NormalizedStringType() {}
|
2022-12-09 19:57:01 +00:00
|
|
|
|
|
|
|
|
|
NormalizedStringType::NormalizedStringType(bool isCofinite, std::map<std::string, TypeId> singletons)
|
2022-10-28 11:37:29 +01:00
|
|
|
|
: isCofinite(isCofinite)
|
|
|
|
|
, singletons(std::move(singletons))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NormalizedStringType::resetToString()
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
isCofinite = true;
|
|
|
|
|
singletons.clear();
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NormalizedStringType::resetToNever()
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
isCofinite = false;
|
|
|
|
|
singletons.clear();
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NormalizedStringType::isNever() const
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
return !isCofinite && singletons.empty();
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NormalizedStringType::isString() const
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
return isCofinite && singletons.empty();
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NormalizedStringType::isUnion() const
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
return !isCofinite;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NormalizedStringType::isIntersection() const
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
return isCofinite;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NormalizedStringType::includes(const std::string& str) const
|
|
|
|
|
{
|
|
|
|
|
if (isString())
|
|
|
|
|
return true;
|
2022-12-09 19:57:01 +00:00
|
|
|
|
else if (isUnion() && singletons.count(str))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
return true;
|
2022-12-09 19:57:01 +00:00
|
|
|
|
else if (isIntersection() && !singletons.count(str))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-09 19:57:01 +00:00
|
|
|
|
const NormalizedStringType NormalizedStringType::never;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
|
|
|
|
|
bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr)
|
|
|
|
|
{
|
|
|
|
|
if (subStr.isUnion() && superStr.isUnion())
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
for (auto [name, ty] : subStr.singletons)
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
if (!superStr.singletons.count(name))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (subStr.isString() && superStr.isUnion())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
void NormalizedClassType::pushPair(TypeId ty, TypeIds negations)
|
|
|
|
|
{
|
|
|
|
|
ordering.push_back(ty);
|
|
|
|
|
classes.insert(std::make_pair(ty, std::move(negations)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NormalizedClassType::resetToNever()
|
|
|
|
|
{
|
|
|
|
|
ordering.clear();
|
|
|
|
|
classes.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NormalizedClassType::isNever() const
|
|
|
|
|
{
|
|
|
|
|
return classes.empty();
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
|
NormalizedFunctionType::NormalizedFunctionType()
|
|
|
|
|
: parts(FFlag::LuauNegatedFunctionTypes ? std::optional<TypeIds>{TypeIds{}} : std::nullopt)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NormalizedFunctionType::resetToTop()
|
|
|
|
|
{
|
|
|
|
|
isTop = true;
|
|
|
|
|
parts.emplace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NormalizedFunctionType::resetToNever()
|
|
|
|
|
{
|
|
|
|
|
isTop = false;
|
|
|
|
|
parts.emplace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NormalizedFunctionType::isNever() const
|
|
|
|
|
{
|
|
|
|
|
return !isTop && (!parts || parts->empty());
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
NormalizedType::NormalizedType(NotNull<BuiltinTypes> builtinTypes)
|
|
|
|
|
: tops(builtinTypes->neverType)
|
|
|
|
|
, booleans(builtinTypes->neverType)
|
|
|
|
|
, errors(builtinTypes->neverType)
|
|
|
|
|
, nils(builtinTypes->neverType)
|
|
|
|
|
, numbers(builtinTypes->neverType)
|
2022-10-28 11:37:29 +01:00
|
|
|
|
, strings{NormalizedStringType::never}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
, threads(builtinTypes->neverType)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
|
static bool isShallowInhabited(const NormalizedType& norm)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
bool inhabitedClasses;
|
|
|
|
|
|
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
|
inhabitedClasses = !norm.classes.isNever();
|
|
|
|
|
else
|
|
|
|
|
inhabitedClasses = !norm.DEPRECATED_classes.empty();
|
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
|
// This test is just a shallow check, for example it returns `true` for `{ p : never }`
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return !get<NeverType>(norm.tops) || !get<NeverType>(norm.booleans) || inhabitedClasses || !get<NeverType>(norm.errors) ||
|
|
|
|
|
!get<NeverType>(norm.nils) || !get<NeverType>(norm.numbers) || !norm.strings.isNever() || !get<NeverType>(norm.threads) ||
|
2022-11-04 17:33:22 +00:00
|
|
|
|
!norm.functions.isNever() || !norm.tables.empty() || !norm.tyvars.empty();
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
|
bool isInhabited_DEPRECATED(const NormalizedType& norm)
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
LUAU_ASSERT(!FFlag::LuauUninhabitedSubAnything2);
|
2022-12-02 18:09:59 +00:00
|
|
|
|
return isShallowInhabited(norm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Normalizer::isInhabited(const NormalizedType* norm, std::unordered_set<TypeId> seen)
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
// If normalization failed, the type is complex, and so is more likely than not to be inhabited.
|
|
|
|
|
if (!norm)
|
|
|
|
|
return true;
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
bool inhabitedClasses;
|
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
|
inhabitedClasses = !norm->classes.isNever();
|
|
|
|
|
else
|
|
|
|
|
inhabitedClasses = !norm->DEPRECATED_classes.empty();
|
|
|
|
|
|
|
|
|
|
if (!get<NeverType>(norm->tops) || !get<NeverType>(norm->booleans) || !get<NeverType>(norm->errors) || !get<NeverType>(norm->nils) ||
|
|
|
|
|
!get<NeverType>(norm->numbers) || !get<NeverType>(norm->threads) || inhabitedClasses || !norm->strings.isNever() ||
|
|
|
|
|
!norm->functions.isNever())
|
2022-12-02 18:09:59 +00:00
|
|
|
|
return true;
|
2022-12-09 19:57:01 +00:00
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
|
for (const auto& [_, intersect] : norm->tyvars)
|
|
|
|
|
{
|
|
|
|
|
if (isInhabited(intersect.get(), seen))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (TypeId table : norm->tables)
|
|
|
|
|
{
|
|
|
|
|
if (isInhabited(table, seen))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Normalizer::isInhabited(TypeId ty, std::unordered_set<TypeId> seen)
|
|
|
|
|
{
|
|
|
|
|
// TODO: use log.follow(ty), CLI-64291
|
|
|
|
|
ty = follow(ty);
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(ty))
|
2022-12-02 18:09:59 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<IntersectionType>(ty) && !get<UnionType>(ty) && !get<TableType>(ty) && !get<MetatableType>(ty))
|
2022-12-02 18:09:59 +00:00
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (seen.count(ty))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
seen.insert(ty);
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const TableType* ttv = get<TableType>(ty))
|
2022-12-02 18:09:59 +00:00
|
|
|
|
{
|
|
|
|
|
for (const auto& [_, prop] : ttv->props)
|
|
|
|
|
{
|
|
|
|
|
if (!isInhabited(prop.type, seen))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const MetatableType* mtv = get<MetatableType>(ty))
|
2022-12-02 18:09:59 +00:00
|
|
|
|
return isInhabited(mtv->table, seen) && isInhabited(mtv->metatable, seen);
|
|
|
|
|
|
|
|
|
|
const NormalizedType* norm = normalize(ty);
|
|
|
|
|
return isInhabited(norm, seen);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
static int tyvarIndex(TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const GenericType* gtv = get<GenericType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return gtv->index;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const FreeType* ftv = get<FreeType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return ftv->index;
|
2023-03-17 19:20:37 +00:00
|
|
|
|
else if (const BlockedType* btv = get<BlockedType>(ty))
|
|
|
|
|
return btv->index;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
2022-10-14 20:48:41 +01:00
|
|
|
|
return 0;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
static bool isTop(NotNull<BuiltinTypes> builtinTypes, const NormalizedClassType& classes)
|
|
|
|
|
{
|
|
|
|
|
if (classes.classes.size() != 1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
auto first = classes.classes.begin();
|
|
|
|
|
if (first->first != builtinTypes->classType)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!first->second.empty())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void resetToTop(NotNull<BuiltinTypes> builtinTypes, NormalizedClassType& classes)
|
|
|
|
|
{
|
|
|
|
|
classes.ordering.clear();
|
|
|
|
|
classes.classes.clear();
|
|
|
|
|
classes.pushPair(builtinTypes->classType, TypeIds{});
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
#ifdef LUAU_ASSERTENABLED
|
|
|
|
|
|
|
|
|
|
static bool isNormalizedTop(TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return get<NeverType>(ty) || get<AnyType>(ty) || get<UnknownType>(ty);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isNormalizedBoolean(TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
|
|
|
|
|
return ptv->type == PrimitiveType::Boolean;
|
|
|
|
|
else if (const SingletonType* stv = get<SingletonType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return get<BooleanSingleton>(stv);
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isNormalizedError(TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(ty) || get<ErrorType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isNormalizedNil(TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
|
|
|
|
|
return ptv->type == PrimitiveType::NilType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isNormalizedNumber(TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
|
|
|
|
|
return ptv->type == PrimitiveType::Number;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isNormalizedString(const NormalizedStringType& ty)
|
|
|
|
|
{
|
2022-10-28 11:37:29 +01:00
|
|
|
|
if (ty.isString())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-12-09 19:57:01 +00:00
|
|
|
|
for (auto& [str, ty] : ty.singletons)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const SingletonType* stv = get<SingletonType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
if (const StringSingleton* sstv = get<StringSingleton>(stv))
|
|
|
|
|
{
|
|
|
|
|
if (sstv->value != str)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isNormalizedThread(TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(ty))
|
|
|
|
|
return ptv->type == PrimitiveType::Thread;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool areNormalizedFunctions(const NormalizedFunctionType& tys)
|
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
if (tys.parts)
|
|
|
|
|
{
|
|
|
|
|
for (TypeId ty : *tys.parts)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<FunctionType>(ty) && !get<ErrorType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return false;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool areNormalizedTables(const TypeIds& tys)
|
|
|
|
|
{
|
|
|
|
|
for (TypeId ty : tys)
|
2023-01-27 22:28:31 +00:00
|
|
|
|
{
|
|
|
|
|
if (get<TableType>(ty) || get<MetatableType>(ty))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const PrimitiveType* pt = get<PrimitiveType>(ty);
|
|
|
|
|
if (!pt)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return false;
|
2023-01-27 22:28:31 +00:00
|
|
|
|
|
|
|
|
|
if (pt->type == PrimitiveType::Table && FFlag::LuauNegatedTableTypes)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool areNormalizedClasses(const TypeIds& tys)
|
|
|
|
|
{
|
|
|
|
|
for (TypeId ty : tys)
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<ClassType>(ty))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
static bool areNormalizedClasses(const NormalizedClassType& tys)
|
|
|
|
|
{
|
|
|
|
|
for (const auto& [ty, negations] : tys.classes)
|
|
|
|
|
{
|
|
|
|
|
const ClassType* ctv = get<ClassType>(ty);
|
|
|
|
|
if (!ctv)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (TypeId negation : negations)
|
|
|
|
|
{
|
|
|
|
|
const ClassType* nctv = get<ClassType>(negation);
|
|
|
|
|
if (!nctv)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isSubclass(nctv, ctv))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& [otherTy, otherNegations] : tys.classes)
|
|
|
|
|
{
|
|
|
|
|
if (otherTy == ty)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const ClassType* octv = get<ClassType>(otherTy);
|
|
|
|
|
if (!octv)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isSubclass(ctv, octv))
|
|
|
|
|
{
|
|
|
|
|
auto iss = [ctv](TypeId t) {
|
|
|
|
|
const ClassType* c = get<ClassType>(t);
|
|
|
|
|
if (!c)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return isSubclass(ctv, c);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!std::any_of(otherNegations.begin(), otherNegations.end(), iss))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
static bool isPlainTyvar(TypeId ty)
|
|
|
|
|
{
|
2023-03-17 19:20:37 +00:00
|
|
|
|
return (get<FreeType>(ty) || get<GenericType>(ty) || (FFlag::LuauNormalizeBlockedTypes && get<BlockedType>(ty)));
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isNormalizedTyvar(const NormalizedTyvars& tyvars)
|
|
|
|
|
{
|
|
|
|
|
for (auto& [tyvar, intersect] : tyvars)
|
|
|
|
|
{
|
|
|
|
|
if (!isPlainTyvar(tyvar))
|
|
|
|
|
return false;
|
2022-12-02 18:09:59 +00:00
|
|
|
|
if (!isShallowInhabited(*intersect))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return false;
|
|
|
|
|
for (auto& [other, _] : intersect->tyvars)
|
|
|
|
|
if (tyvarIndex(other) <= tyvarIndex(tyvar))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // LUAU_ASSERTENABLED
|
|
|
|
|
|
|
|
|
|
static void assertInvariant(const NormalizedType& norm)
|
|
|
|
|
{
|
2022-10-14 20:48:41 +01:00
|
|
|
|
#ifdef LUAU_ASSERTENABLED
|
|
|
|
|
if (!FFlag::DebugLuauCheckNormalizeInvariant)
|
|
|
|
|
return;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
|
LUAU_ASSERT(isNormalizedTop(norm.tops));
|
|
|
|
|
LUAU_ASSERT(isNormalizedBoolean(norm.booleans));
|
2023-01-04 20:53:17 +00:00
|
|
|
|
LUAU_ASSERT(areNormalizedClasses(norm.DEPRECATED_classes));
|
2022-10-14 20:48:41 +01:00
|
|
|
|
LUAU_ASSERT(areNormalizedClasses(norm.classes));
|
|
|
|
|
LUAU_ASSERT(isNormalizedError(norm.errors));
|
|
|
|
|
LUAU_ASSERT(isNormalizedNil(norm.nils));
|
|
|
|
|
LUAU_ASSERT(isNormalizedNumber(norm.numbers));
|
|
|
|
|
LUAU_ASSERT(isNormalizedString(norm.strings));
|
|
|
|
|
LUAU_ASSERT(isNormalizedThread(norm.threads));
|
|
|
|
|
LUAU_ASSERT(areNormalizedFunctions(norm.functions));
|
|
|
|
|
LUAU_ASSERT(areNormalizedTables(norm.tables));
|
|
|
|
|
LUAU_ASSERT(isNormalizedTyvar(norm.tyvars));
|
|
|
|
|
for (auto& [_, child] : norm.tyvars)
|
|
|
|
|
assertInvariant(*child);
|
|
|
|
|
#endif
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
Normalizer::Normalizer(TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> sharedState)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
: arena(arena)
|
2023-01-04 20:53:17 +00:00
|
|
|
|
, builtinTypes(builtinTypes)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
, sharedState(sharedState)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const NormalizedType* Normalizer::normalize(TypeId ty)
|
|
|
|
|
{
|
|
|
|
|
if (!arena)
|
|
|
|
|
sharedState->iceHandler->ice("Normalizing types outside a module");
|
|
|
|
|
|
|
|
|
|
auto found = cachedNormals.find(ty);
|
|
|
|
|
if (found != cachedNormals.end())
|
|
|
|
|
return found->second.get();
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
NormalizedType norm{builtinTypes};
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (!unionNormalWithTy(norm, ty))
|
|
|
|
|
return nullptr;
|
|
|
|
|
std::unique_ptr<NormalizedType> uniq = std::make_unique<NormalizedType>(std::move(norm));
|
|
|
|
|
const NormalizedType* result = uniq.get();
|
|
|
|
|
cachedNormals[ty] = std::move(uniq);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::clearNormal(NormalizedType& norm)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
norm.tops = builtinTypes->neverType;
|
|
|
|
|
norm.booleans = builtinTypes->neverType;
|
|
|
|
|
norm.classes.resetToNever();
|
|
|
|
|
norm.DEPRECATED_classes.clear();
|
|
|
|
|
norm.errors = builtinTypes->neverType;
|
|
|
|
|
norm.nils = builtinTypes->neverType;
|
|
|
|
|
norm.numbers = builtinTypes->neverType;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
norm.strings.resetToNever();
|
2023-01-04 20:53:17 +00:00
|
|
|
|
norm.threads = builtinTypes->neverType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
norm.tables.clear();
|
2022-11-04 17:33:22 +00:00
|
|
|
|
norm.functions.resetToNever();
|
2022-10-07 01:23:29 +01:00
|
|
|
|
norm.tyvars.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------- Cached TypeIds
|
|
|
|
|
const TypeIds* Normalizer::cacheTypeIds(TypeIds tys)
|
|
|
|
|
{
|
|
|
|
|
auto found = cachedTypeIds.find(&tys);
|
|
|
|
|
if (found != cachedTypeIds.end())
|
|
|
|
|
return found->first;
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<TypeIds> uniq = std::make_unique<TypeIds>(std::move(tys));
|
|
|
|
|
const TypeIds* result = uniq.get();
|
|
|
|
|
cachedTypeIds[result] = std::move(uniq);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeId Normalizer::unionType(TypeId here, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
here = follow(here);
|
|
|
|
|
there = follow(there);
|
|
|
|
|
|
|
|
|
|
if (here == there)
|
|
|
|
|
return here;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(here) || get<AnyType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(there) || get<AnyType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return here;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
TypeIds tmps;
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const UnionType* utv = get<UnionType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeIds heres;
|
|
|
|
|
heres.insert(begin(utv), end(utv));
|
|
|
|
|
tmps.insert(heres.begin(), heres.end());
|
|
|
|
|
cachedUnions[cacheTypeIds(std::move(heres))] = here;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
tmps.insert(here);
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const UnionType* utv = get<UnionType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeIds theres;
|
|
|
|
|
theres.insert(begin(utv), end(utv));
|
|
|
|
|
tmps.insert(theres.begin(), theres.end());
|
|
|
|
|
cachedUnions[cacheTypeIds(std::move(theres))] = there;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
tmps.insert(there);
|
|
|
|
|
|
|
|
|
|
auto cacheHit = cachedUnions.find(&tmps);
|
|
|
|
|
if (cacheHit != cachedUnions.end())
|
|
|
|
|
return cacheHit->second;
|
|
|
|
|
|
|
|
|
|
std::vector<TypeId> parts;
|
|
|
|
|
parts.insert(parts.end(), tmps.begin(), tmps.end());
|
2023-01-04 20:53:17 +00:00
|
|
|
|
TypeId result = arena->addType(UnionType{std::move(parts)});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
cachedUnions[cacheTypeIds(std::move(tmps))] = result;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeId Normalizer::intersectionType(TypeId here, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
here = follow(here);
|
|
|
|
|
there = follow(there);
|
|
|
|
|
|
|
|
|
|
if (here == there)
|
|
|
|
|
return here;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(here) || get<AnyType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return here;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(there) || get<AnyType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return there;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
TypeIds tmps;
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const IntersectionType* utv = get<IntersectionType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeIds heres;
|
|
|
|
|
heres.insert(begin(utv), end(utv));
|
|
|
|
|
tmps.insert(heres.begin(), heres.end());
|
|
|
|
|
cachedIntersections[cacheTypeIds(std::move(heres))] = here;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
tmps.insert(here);
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const IntersectionType* utv = get<IntersectionType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeIds theres;
|
|
|
|
|
theres.insert(begin(utv), end(utv));
|
|
|
|
|
tmps.insert(theres.begin(), theres.end());
|
|
|
|
|
cachedIntersections[cacheTypeIds(std::move(theres))] = there;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
tmps.insert(there);
|
|
|
|
|
|
|
|
|
|
if (tmps.size() == 1)
|
|
|
|
|
return *tmps.begin();
|
|
|
|
|
|
|
|
|
|
auto cacheHit = cachedIntersections.find(&tmps);
|
|
|
|
|
if (cacheHit != cachedIntersections.end())
|
|
|
|
|
return cacheHit->second;
|
|
|
|
|
|
|
|
|
|
std::vector<TypeId> parts;
|
|
|
|
|
parts.insert(parts.end(), tmps.begin(), tmps.end());
|
2023-01-04 20:53:17 +00:00
|
|
|
|
TypeId result = arena->addType(IntersectionType{std::move(parts)});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
cachedIntersections[cacheTypeIds(std::move(tmps))] = result;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::clearCaches()
|
|
|
|
|
{
|
|
|
|
|
cachedNormals.clear();
|
|
|
|
|
cachedIntersections.clear();
|
|
|
|
|
cachedUnions.clear();
|
|
|
|
|
cachedTypeIds.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------- Normalizing unions
|
|
|
|
|
TypeId Normalizer::unionOfTops(TypeId here, TypeId there)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(here) || get<AnyType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return there;
|
|
|
|
|
else
|
|
|
|
|
return here;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeId Normalizer::unionOfBools(TypeId here, TypeId there)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return here;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const BooleanSingleton* hbool = get<BooleanSingleton>(get<SingletonType>(here)))
|
|
|
|
|
if (const BooleanSingleton* tbool = get<BooleanSingleton>(get<SingletonType>(there)))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (hbool->value == tbool->value)
|
|
|
|
|
return here;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return builtinTypes->booleanType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::unionClassesWithClass(TypeIds& heres, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
if (heres.count(there))
|
|
|
|
|
return;
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const ClassType* tctv = get<ClassType>(there);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
|
|
|
|
|
for (auto it = heres.begin(); it != heres.end();)
|
|
|
|
|
{
|
|
|
|
|
TypeId here = *it;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const ClassType* hctv = get<ClassType>(here);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (isSubclass(tctv, hctv))
|
|
|
|
|
return;
|
|
|
|
|
else if (isSubclass(hctv, tctv))
|
|
|
|
|
it = heres.erase(it);
|
|
|
|
|
else
|
|
|
|
|
it++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
heres.insert(there);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::unionClasses(TypeIds& heres, const TypeIds& theres)
|
|
|
|
|
{
|
|
|
|
|
for (TypeId there : theres)
|
|
|
|
|
unionClassesWithClass(heres, there);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
static bool isSubclass(TypeId test, TypeId parent)
|
|
|
|
|
{
|
|
|
|
|
const ClassType* testCtv = get<ClassType>(test);
|
|
|
|
|
const ClassType* parentCtv = get<ClassType>(parent);
|
|
|
|
|
|
|
|
|
|
LUAU_ASSERT(testCtv);
|
|
|
|
|
LUAU_ASSERT(parentCtv);
|
|
|
|
|
|
|
|
|
|
return isSubclass(testCtv, parentCtv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::unionClassesWithClass(NormalizedClassType& heres, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
|
|
|
|
{
|
|
|
|
|
TypeId hereTy = *it;
|
|
|
|
|
TypeIds& hereNegations = heres.classes.at(hereTy);
|
|
|
|
|
|
|
|
|
|
// If the incoming class is a subclass of another class in the map, we
|
|
|
|
|
// must ensure that it is negated by one of the negations in the same
|
|
|
|
|
// cluster. If it isn't, we do not need to insert it - the subtyping
|
|
|
|
|
// relationship is already handled by this entry. If it is, we must
|
|
|
|
|
// insert it, to capture the presence of this particular subtype.
|
|
|
|
|
if (isSubclass(there, hereTy))
|
|
|
|
|
{
|
|
|
|
|
for (auto nIt = hereNegations.begin(); nIt != hereNegations.end();)
|
|
|
|
|
{
|
|
|
|
|
TypeId hereNegation = *nIt;
|
|
|
|
|
|
|
|
|
|
// If the incoming class is a subclass of one of the negations,
|
|
|
|
|
// we must insert it into the class map.
|
|
|
|
|
if (isSubclass(there, hereNegation))
|
|
|
|
|
{
|
|
|
|
|
heres.pushPair(there, TypeIds{});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// If the incoming class is a superclass of one of the
|
|
|
|
|
// negations, then the negation no longer applies and must be
|
|
|
|
|
// removed. This is also true if they are equal. Since classes
|
|
|
|
|
// are, at this time, entirely persistent (we do not clone
|
|
|
|
|
// them), a pointer identity check is sufficient.
|
|
|
|
|
else if (isSubclass(hereNegation, there))
|
|
|
|
|
{
|
|
|
|
|
nIt = hereNegations.erase(nIt);
|
|
|
|
|
}
|
|
|
|
|
// If the incoming class is unrelated to the negation, we move
|
|
|
|
|
// on to the next item.
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++nIt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If, at the end of the above loop, we haven't returned, that means
|
|
|
|
|
// that the class is not a subclass of one of the negations, and is
|
|
|
|
|
// covered by the existing subtype relationship. We can return now.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// If the incoming class is a superclass of another class in the map, we
|
|
|
|
|
// need to replace the existing class with the incoming class,
|
|
|
|
|
// preserving the relevant negations.
|
|
|
|
|
else if (isSubclass(hereTy, there))
|
|
|
|
|
{
|
|
|
|
|
TypeIds negations = std::move(hereNegations);
|
|
|
|
|
it = heres.ordering.erase(it);
|
|
|
|
|
heres.classes.erase(hereTy);
|
|
|
|
|
|
|
|
|
|
heres.pushPair(there, std::move(negations));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the incoming class is unrelated to the class in the map, we move
|
|
|
|
|
// on. If we do not otherwise exit from this method body, we will
|
|
|
|
|
// eventually fall out of this loop and insert the incoming class, which
|
|
|
|
|
// we have proven to be completely unrelated to any class in the map,
|
|
|
|
|
// into the map itself.
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
heres.pushPair(there, TypeIds{});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::unionClasses(NormalizedClassType& heres, const NormalizedClassType& theres)
|
|
|
|
|
{
|
|
|
|
|
// This method bears much similarity with unionClassesWithClass, but is
|
|
|
|
|
// solving a more general problem. In unionClassesWithClass, we are dealing
|
|
|
|
|
// with a singular positive type. Since it's one type, we can use early
|
|
|
|
|
// returns as control flow. Since it's guaranteed to be positive, we do not
|
|
|
|
|
// have negations to worry about combining. The two aspects combine to make
|
|
|
|
|
// the tasks this method must perform different enough to warrant a separate
|
|
|
|
|
// implementation.
|
|
|
|
|
|
|
|
|
|
for (const TypeId thereTy : theres.ordering)
|
|
|
|
|
{
|
|
|
|
|
const TypeIds& thereNegations = theres.classes.at(thereTy);
|
|
|
|
|
|
|
|
|
|
// If it happens that there are _no_ classes in the current map, or the
|
|
|
|
|
// incoming class is completely unrelated to any class in the current
|
|
|
|
|
// map, we must insert the incoming pair as-is.
|
|
|
|
|
bool insert = true;
|
|
|
|
|
|
|
|
|
|
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
|
|
|
|
{
|
|
|
|
|
TypeId hereTy = *it;
|
|
|
|
|
TypeIds& hereNegations = heres.classes.at(hereTy);
|
|
|
|
|
|
|
|
|
|
if (isSubclass(thereTy, hereTy))
|
|
|
|
|
{
|
|
|
|
|
bool inserted = false;
|
|
|
|
|
for (auto nIt = hereNegations.begin(); nIt != hereNegations.end();)
|
|
|
|
|
{
|
|
|
|
|
TypeId hereNegateTy = *nIt;
|
|
|
|
|
|
|
|
|
|
// If the incoming class is a subclass of one of the negations,
|
|
|
|
|
// we must insert it into the class map.
|
|
|
|
|
if (isSubclass(thereTy, hereNegateTy))
|
|
|
|
|
{
|
|
|
|
|
// We do not concern ourselves with iterator
|
|
|
|
|
// invalidation here because we will break out of the
|
|
|
|
|
// loop over `heres` when `inserted` is set, and we do
|
|
|
|
|
// not read from the iterator after this point.
|
|
|
|
|
inserted = true;
|
|
|
|
|
heres.pushPair(thereTy, thereNegations);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// If the incoming class is a superclass of one of the
|
|
|
|
|
// negations, then the negation no longer applies and must
|
|
|
|
|
// be removed. This is also true if they are equal. Since
|
|
|
|
|
// classes are, at this time, entirely persistent (we do not
|
|
|
|
|
// clone them), a pointer identity check is sufficient.
|
|
|
|
|
else if (isSubclass(hereNegateTy, thereTy))
|
|
|
|
|
{
|
|
|
|
|
inserted = true;
|
|
|
|
|
nIt = hereNegations.erase(nIt);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// If the incoming class is unrelated to the negation, we
|
|
|
|
|
// move on to the next item.
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++nIt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inserted)
|
|
|
|
|
{
|
|
|
|
|
insert = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (isSubclass(hereTy, thereTy))
|
|
|
|
|
{
|
|
|
|
|
TypeIds negations = std::move(hereNegations);
|
|
|
|
|
unionClasses(negations, thereNegations);
|
|
|
|
|
|
|
|
|
|
it = heres.ordering.erase(it);
|
|
|
|
|
heres.classes.erase(hereTy);
|
|
|
|
|
heres.pushPair(thereTy, std::move(negations));
|
|
|
|
|
insert = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (hereTy == thereTy)
|
|
|
|
|
{
|
|
|
|
|
unionClasses(hereNegations, thereNegations);
|
|
|
|
|
insert = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (insert)
|
|
|
|
|
{
|
|
|
|
|
heres.pushPair(thereTy, thereNegations);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
void Normalizer::unionStrings(NormalizedStringType& here, const NormalizedStringType& there)
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
if (there.isString())
|
|
|
|
|
here.resetToString();
|
|
|
|
|
else if (here.isUnion() && there.isUnion())
|
|
|
|
|
here.singletons.insert(there.singletons.begin(), there.singletons.end());
|
|
|
|
|
else if (here.isUnion() && there.isIntersection())
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
here.isCofinite = true;
|
|
|
|
|
for (const auto& pair : there.singletons)
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
auto it = here.singletons.find(pair.first);
|
|
|
|
|
if (it != end(here.singletons))
|
|
|
|
|
here.singletons.erase(it);
|
|
|
|
|
else
|
|
|
|
|
here.singletons.insert(pair);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
2022-12-09 19:57:01 +00:00
|
|
|
|
}
|
|
|
|
|
else if (here.isIntersection() && there.isUnion())
|
|
|
|
|
{
|
|
|
|
|
for (const auto& [name, ty] : there.singletons)
|
|
|
|
|
here.singletons.erase(name);
|
|
|
|
|
}
|
|
|
|
|
else if (here.isIntersection() && there.isIntersection())
|
|
|
|
|
{
|
|
|
|
|
auto iter = begin(here.singletons);
|
|
|
|
|
auto endIter = end(here.singletons);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
|
2022-12-09 19:57:01 +00:00
|
|
|
|
while (iter != endIter)
|
|
|
|
|
{
|
|
|
|
|
if (!there.singletons.count(iter->first))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
auto eraseIt = iter;
|
|
|
|
|
++iter;
|
|
|
|
|
here.singletons.erase(eraseIt);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
2022-12-09 19:57:01 +00:00
|
|
|
|
else
|
|
|
|
|
++iter;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2022-12-09 19:57:01 +00:00
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<TypePackId> Normalizer::unionOfTypePacks(TypePackId here, TypePackId there)
|
|
|
|
|
{
|
|
|
|
|
if (here == there)
|
|
|
|
|
return here;
|
|
|
|
|
|
|
|
|
|
std::vector<TypeId> head;
|
|
|
|
|
std::optional<TypePackId> tail;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
bool hereSubThere = true;
|
|
|
|
|
bool thereSubHere = true;
|
|
|
|
|
|
|
|
|
|
TypePackIterator ith = begin(here);
|
|
|
|
|
TypePackIterator itt = begin(there);
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
while (ith != end(here) && itt != end(there))
|
|
|
|
|
{
|
|
|
|
|
TypeId hty = *ith;
|
|
|
|
|
TypeId tty = *itt;
|
|
|
|
|
TypeId ty = unionType(hty, tty);
|
|
|
|
|
if (ty != hty)
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
if (ty != tty)
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
head.push_back(ty);
|
|
|
|
|
ith++;
|
|
|
|
|
itt++;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
|
auto dealWithDifferentArities = [&](TypePackIterator& ith, TypePackIterator itt, TypePackId here, TypePackId there, bool& hereSubThere,
|
|
|
|
|
bool& thereSubHere) {
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (ith != end(here))
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
TypeId tty = builtinTypes->nilType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (std::optional<TypePackId> ttail = itt.tail())
|
|
|
|
|
{
|
|
|
|
|
if (const VariadicTypePack* tvtp = get<VariadicTypePack>(*ttail))
|
|
|
|
|
tty = tvtp->ty;
|
|
|
|
|
else
|
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
// Type packs of different arities are incomparable
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
while (ith != end(here))
|
|
|
|
|
{
|
|
|
|
|
TypeId hty = *ith;
|
|
|
|
|
TypeId ty = unionType(hty, tty);
|
|
|
|
|
if (ty != hty)
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
if (ty != tty)
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
head.push_back(ty);
|
|
|
|
|
ith++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!dealWithDifferentArities(ith, itt, here, there, hereSubThere, thereSubHere))
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
if (!dealWithDifferentArities(itt, ith, there, here, thereSubHere, hereSubThere))
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
if (std::optional<TypePackId> htail = ith.tail())
|
|
|
|
|
{
|
|
|
|
|
if (std::optional<TypePackId> ttail = itt.tail())
|
|
|
|
|
{
|
|
|
|
|
if (*htail == *ttail)
|
|
|
|
|
tail = htail;
|
|
|
|
|
else if (const VariadicTypePack* hvtp = get<VariadicTypePack>(*htail))
|
|
|
|
|
{
|
|
|
|
|
if (const VariadicTypePack* tvtp = get<VariadicTypePack>(*ttail))
|
|
|
|
|
{
|
|
|
|
|
TypeId ty = unionType(hvtp->ty, tvtp->ty);
|
|
|
|
|
if (ty != hvtp->ty)
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
if (ty != tvtp->ty)
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
bool hidden = hvtp->hidden & tvtp->hidden;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
tail = arena->addTypePack(VariadicTypePack{ty, hidden});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
2022-10-14 20:48:41 +01:00
|
|
|
|
else
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
2022-10-14 20:48:41 +01:00
|
|
|
|
else
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
else if (get<VariadicTypePack>(*htail))
|
|
|
|
|
{
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
tail = htail;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
else if (std::optional<TypePackId> ttail = itt.tail())
|
|
|
|
|
{
|
|
|
|
|
if (get<VariadicTypePack>(*ttail))
|
|
|
|
|
{
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
tail = htail;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hereSubThere)
|
|
|
|
|
return there;
|
|
|
|
|
else if (thereSubHere)
|
|
|
|
|
return here;
|
|
|
|
|
if (!head.empty())
|
2022-10-14 20:48:41 +01:00
|
|
|
|
return arena->addTypePack(TypePack{head, tail});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else if (tail)
|
|
|
|
|
return *tail;
|
|
|
|
|
else
|
|
|
|
|
// TODO: Add an emptyPack to singleton types
|
|
|
|
|
return arena->addTypePack({});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<TypeId> Normalizer::unionOfFunctions(TypeId here, TypeId there)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<ErrorType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return here;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<ErrorType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return there;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const FunctionType* hftv = get<FunctionType>(here);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(hftv);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const FunctionType* tftv = get<FunctionType>(there);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(tftv);
|
|
|
|
|
|
|
|
|
|
if (hftv->generics != tftv->generics)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
if (hftv->genericPacks != tftv->genericPacks)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
std::optional<TypePackId> argTypes = intersectionOfTypePacks(hftv->argTypes, tftv->argTypes);
|
|
|
|
|
if (!argTypes)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
std::optional<TypePackId> retTypes = unionOfTypePacks(hftv->retTypes, tftv->retTypes);
|
|
|
|
|
if (!retTypes)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
if (*argTypes == hftv->argTypes && *retTypes == hftv->retTypes)
|
|
|
|
|
return here;
|
|
|
|
|
if (*argTypes == tftv->argTypes && *retTypes == tftv->retTypes)
|
|
|
|
|
return there;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
FunctionType result{*argTypes, *retTypes};
|
2022-10-07 01:23:29 +01:00
|
|
|
|
result.generics = hftv->generics;
|
|
|
|
|
result.genericPacks = hftv->genericPacks;
|
|
|
|
|
return arena->addType(std::move(result));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::unionFunctions(NormalizedFunctionType& heres, const NormalizedFunctionType& theres)
|
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
if (FFlag::LuauNegatedFunctionTypes)
|
|
|
|
|
{
|
|
|
|
|
if (heres.isTop)
|
|
|
|
|
return;
|
|
|
|
|
if (theres.isTop)
|
|
|
|
|
heres.resetToTop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (theres.isNever())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TypeIds tmps;
|
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
|
if (heres.isNever())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
tmps.insert(theres.parts->begin(), theres.parts->end());
|
|
|
|
|
heres.parts = std::move(tmps);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
|
for (TypeId here : *heres.parts)
|
|
|
|
|
for (TypeId there : *theres.parts)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
if (std::optional<TypeId> fun = unionOfFunctions(here, there))
|
|
|
|
|
tmps.insert(*fun);
|
|
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
|
tmps.insert(builtinTypes->errorRecoveryType(there));
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
|
heres.parts = std::move(tmps);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::unionFunctionsWithFunction(NormalizedFunctionType& heres, TypeId there)
|
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
if (heres.isNever())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeIds tmps;
|
|
|
|
|
tmps.insert(there);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
heres.parts = std::move(tmps);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeIds tmps;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
for (TypeId here : *heres.parts)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
if (std::optional<TypeId> fun = unionOfFunctions(here, there))
|
|
|
|
|
tmps.insert(*fun);
|
|
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
|
tmps.insert(builtinTypes->errorRecoveryType(there));
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
2022-11-04 17:33:22 +00:00
|
|
|
|
heres.parts = std::move(tmps);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::unionTablesWithTable(TypeIds& heres, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
// TODO: remove unions of tables where possible
|
|
|
|
|
heres.insert(there);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::unionTables(TypeIds& heres, const TypeIds& theres)
|
|
|
|
|
{
|
|
|
|
|
for (TypeId there : theres)
|
2023-01-27 22:28:31 +00:00
|
|
|
|
{
|
|
|
|
|
if (FFlag::LuauNegatedTableTypes)
|
|
|
|
|
{
|
|
|
|
|
if (there == builtinTypes->tableType)
|
|
|
|
|
{
|
|
|
|
|
heres.clear();
|
|
|
|
|
heres.insert(there);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unionTablesWithTable(heres, there);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unionTablesWithTable(heres, there);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// So why `ignoreSmallerTyvars`?
|
|
|
|
|
//
|
|
|
|
|
// First up, what it does... Every tyvar has an index, and this parameter says to ignore
|
|
|
|
|
// any tyvars in `there` if their index is less than or equal to the parameter.
|
|
|
|
|
// The parameter is always greater than any tyvars mentioned in here, so the result is
|
|
|
|
|
// a lower bound on any tyvars in `here.tyvars`.
|
|
|
|
|
//
|
|
|
|
|
// This is used to maintain in invariant, which is that in any tyvar `X&T`, any any tyvar
|
|
|
|
|
// `Y&U` in `T`, the index of `X` is less than the index of `Y`. This is an implementation
|
|
|
|
|
// of *ordered decision diagrams* (https://en.wikipedia.org/wiki/Binary_decision_diagram#Variable_ordering)
|
|
|
|
|
// which are a compression technique used to save memory usage when representing boolean formulae.
|
|
|
|
|
//
|
|
|
|
|
// The idea is that if you have an out-of-order decision diagram
|
|
|
|
|
// like `Z&(X|Y)`, to re-order it in this case to `(X&Z)|(Y&Z)`.
|
|
|
|
|
// The hope is that by imposing a global order, there's a higher chance of sharing opportunities,
|
|
|
|
|
// and hence reduced memory.
|
|
|
|
|
//
|
|
|
|
|
// And yes, this is essentially a SAT solver hidden inside a typechecker.
|
|
|
|
|
// That's what you get for having a type system with generics, intersection and union types.
|
|
|
|
|
bool Normalizer::unionNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars)
|
|
|
|
|
{
|
|
|
|
|
TypeId tops = unionOfTops(here.tops, there.tops);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
if (FFlag::LuauTransitiveSubtyping && get<UnknownType>(tops) && (get<ErrorType>(here.errors) || get<ErrorType>(there.errors)))
|
|
|
|
|
tops = builtinTypes->anyType;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<NeverType>(tops))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
here.tops = tops;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto it = there.tyvars.begin(); it != there.tyvars.end(); it++)
|
|
|
|
|
{
|
|
|
|
|
TypeId tyvar = it->first;
|
|
|
|
|
const NormalizedType& inter = *it->second;
|
|
|
|
|
int index = tyvarIndex(tyvar);
|
|
|
|
|
if (index <= ignoreSmallerTyvars)
|
|
|
|
|
continue;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
auto [emplaced, fresh] = here.tyvars.emplace(tyvar, std::make_unique<NormalizedType>(NormalizedType{builtinTypes}));
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (fresh)
|
|
|
|
|
if (!unionNormals(*emplaced->second, here, index))
|
|
|
|
|
return false;
|
|
|
|
|
if (!unionNormals(*emplaced->second, inter, index))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
here.booleans = unionOfBools(here.booleans, there.booleans);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
|
unionClasses(here.classes, there.classes);
|
|
|
|
|
else
|
|
|
|
|
unionClasses(here.DEPRECATED_classes, there.DEPRECATED_classes);
|
|
|
|
|
|
|
|
|
|
here.errors = (get<NeverType>(there.errors) ? here.errors : there.errors);
|
|
|
|
|
here.nils = (get<NeverType>(there.nils) ? here.nils : there.nils);
|
|
|
|
|
here.numbers = (get<NeverType>(there.numbers) ? here.numbers : there.numbers);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
unionStrings(here.strings, there.strings);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
here.threads = (get<NeverType>(there.threads) ? here.threads : there.threads);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
unionFunctions(here.functions, there.functions);
|
|
|
|
|
unionTables(here.tables, there.tables);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Normalizer::withinResourceLimits()
|
|
|
|
|
{
|
|
|
|
|
// If cache is too large, clear it
|
|
|
|
|
if (FInt::LuauNormalizeCacheLimit > 0)
|
|
|
|
|
{
|
|
|
|
|
size_t cacheUsage = cachedNormals.size() + cachedIntersections.size() + cachedUnions.size() + cachedTypeIds.size();
|
|
|
|
|
if (cacheUsage > size_t(FInt::LuauNormalizeCacheLimit))
|
|
|
|
|
{
|
|
|
|
|
clearCaches();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check the recursion count
|
|
|
|
|
if (sharedState->counters.recursionLimit > 0)
|
2022-10-14 20:48:41 +01:00
|
|
|
|
if (sharedState->counters.recursionLimit < sharedState->counters.recursionCount)
|
|
|
|
|
return false;
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See above for an explaination of `ignoreSmallerTyvars`.
|
|
|
|
|
bool Normalizer::unionNormalWithTy(NormalizedType& here, TypeId there, int ignoreSmallerTyvars)
|
|
|
|
|
{
|
|
|
|
|
RecursionCounter _rc(&sharedState->counters.recursionCount);
|
|
|
|
|
if (!withinResourceLimits())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
there = follow(there);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<AnyType>(there) || get<UnknownType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeId tops = unionOfTops(here.tops, there);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
if (FFlag::LuauTransitiveSubtyping && get<UnknownType>(tops) && get<ErrorType>(here.errors))
|
|
|
|
|
tops = builtinTypes->anyType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
clearNormal(here);
|
|
|
|
|
here.tops = tops;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-03-17 19:20:37 +00:00
|
|
|
|
else if (!FFlag::LuauTransitiveSubtyping && (get<NeverType>(there) || !get<NeverType>(here.tops)))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return true;
|
2023-03-17 19:20:37 +00:00
|
|
|
|
else if (FFlag::LuauTransitiveSubtyping && (get<NeverType>(there) || get<AnyType>(here.tops)))
|
|
|
|
|
return true;
|
|
|
|
|
else if (FFlag::LuauTransitiveSubtyping && get<ErrorType>(there) && get<UnknownType>(here.tops))
|
|
|
|
|
{
|
|
|
|
|
here.tops = builtinTypes->anyType;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const UnionType* utv = get<UnionType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
for (UnionTypeIterator it = begin(utv); it != end(utv); ++it)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (!unionNormalWithTy(here, *it))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const IntersectionType* itv = get<IntersectionType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
NormalizedType norm{builtinTypes};
|
|
|
|
|
norm.tops = builtinTypes->anyType;
|
|
|
|
|
for (IntersectionTypeIterator it = begin(itv); it != end(itv); ++it)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (!intersectNormalWithTy(norm, *it))
|
|
|
|
|
return false;
|
|
|
|
|
return unionNormals(here, norm);
|
|
|
|
|
}
|
2023-03-17 19:20:37 +00:00
|
|
|
|
else if (FFlag::LuauTransitiveSubtyping && get<UnknownType>(here.tops))
|
|
|
|
|
return true;
|
|
|
|
|
else if (get<GenericType>(there) || get<FreeType>(there) || (FFlag::LuauNormalizeBlockedTypes && get<BlockedType>(there)))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
if (tyvarIndex(there) <= ignoreSmallerTyvars)
|
|
|
|
|
return true;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
NormalizedType inter{builtinTypes};
|
|
|
|
|
inter.tops = builtinTypes->unknownType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.tyvars.insert_or_assign(there, std::make_unique<NormalizedType>(std::move(inter)));
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<FunctionType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
unionFunctionsWithFunction(here.functions, there);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<TableType>(there) || get<MetatableType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
unionTablesWithTable(here.tables, there);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<ClassType>(there))
|
|
|
|
|
{
|
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
|
{
|
|
|
|
|
unionClassesWithClass(here.classes, there);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unionClassesWithClass(here.DEPRECATED_classes, there);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (get<ErrorType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.errors = there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (ptv->type == PrimitiveType::Boolean)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.booleans = there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::NilType)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.nils = there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::Number)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.numbers = there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::String)
|
2022-10-28 11:37:29 +01:00
|
|
|
|
here.strings.resetToString();
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::Thread)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.threads = there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::Function)
|
2022-11-04 17:33:22 +00:00
|
|
|
|
{
|
|
|
|
|
LUAU_ASSERT(FFlag::LuauNegatedFunctionTypes);
|
|
|
|
|
here.functions.resetToTop();
|
|
|
|
|
}
|
2023-01-27 22:28:31 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::Table && FFlag::LuauNegatedTableTypes)
|
|
|
|
|
{
|
|
|
|
|
here.tables.clear();
|
|
|
|
|
here.tables.insert(there);
|
|
|
|
|
}
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const SingletonType* stv = get<SingletonType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
if (get<BooleanSingleton>(stv))
|
|
|
|
|
here.booleans = unionOfBools(here.booleans, there);
|
|
|
|
|
else if (const StringSingleton* sstv = get<StringSingleton>(stv))
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
if (here.strings.isCofinite)
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
auto it = here.strings.singletons.find(sstv->value);
|
|
|
|
|
if (it != here.strings.singletons.end())
|
|
|
|
|
here.strings.singletons.erase(it);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
2022-12-09 19:57:01 +00:00
|
|
|
|
here.strings.singletons.insert({sstv->value, there});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const NegationType* ntv = get<NegationType>(there))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
const NormalizedType* thereNormal = normalize(ntv->ty);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
std::optional<NormalizedType> tn = negateNormal(*thereNormal);
|
|
|
|
|
if (!tn)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!unionNormals(here, *tn))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-03-17 19:20:37 +00:00
|
|
|
|
else if (!FFlag::LuauNormalizeBlockedTypes && get<BlockedType>(there))
|
2023-02-24 21:49:38 +00:00
|
|
|
|
LUAU_ASSERT(!"Internal error: Trying to normalize a BlockedType");
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
|
|
|
|
|
|
|
|
|
for (auto& [tyvar, intersect] : here.tyvars)
|
|
|
|
|
if (!unionNormalWithTy(*intersect, there, tyvarIndex(tyvar)))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
assertInvariant(here);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
|
// ------- Negations
|
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
|
std::optional<NormalizedType> Normalizer::negateNormal(const NormalizedType& here)
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
NormalizedType result{builtinTypes};
|
|
|
|
|
if (!get<NeverType>(here.tops))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
// The negation of unknown or any is never. Easy.
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<NeverType>(here.errors))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
// Negating an error yields the same error.
|
|
|
|
|
result.errors = here.errors;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(here.booleans))
|
|
|
|
|
result.booleans = builtinTypes->booleanType;
|
|
|
|
|
else if (get<PrimitiveType>(here.booleans))
|
|
|
|
|
result.booleans = builtinTypes->neverType;
|
|
|
|
|
else if (auto stv = get<SingletonType>(here.booleans))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
auto boolean = get<BooleanSingleton>(stv);
|
|
|
|
|
LUAU_ASSERT(boolean != nullptr);
|
|
|
|
|
if (boolean->value)
|
2023-01-04 20:53:17 +00:00
|
|
|
|
result.booleans = builtinTypes->falseType;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
|
result.booleans = builtinTypes->trueType;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
|
{
|
|
|
|
|
if (here.classes.isNever())
|
|
|
|
|
{
|
|
|
|
|
resetToTop(builtinTypes, result.classes);
|
|
|
|
|
}
|
|
|
|
|
else if (isTop(builtinTypes, result.classes))
|
|
|
|
|
{
|
|
|
|
|
result.classes.resetToNever();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TypeIds rootNegations{};
|
|
|
|
|
|
|
|
|
|
for (const auto& [hereParent, hereNegations] : here.classes.classes)
|
|
|
|
|
{
|
|
|
|
|
if (hereParent != builtinTypes->classType)
|
|
|
|
|
rootNegations.insert(hereParent);
|
|
|
|
|
|
|
|
|
|
for (TypeId hereNegation : hereNegations)
|
|
|
|
|
unionClassesWithClass(result.classes, hereNegation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!rootNegations.empty())
|
|
|
|
|
result.classes.pushPair(builtinTypes->classType, rootNegations);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.DEPRECATED_classes = negateAll(here.DEPRECATED_classes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.nils = get<NeverType>(here.nils) ? builtinTypes->nilType : builtinTypes->neverType;
|
|
|
|
|
result.numbers = get<NeverType>(here.numbers) ? builtinTypes->numberType : builtinTypes->neverType;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
|
|
|
|
|
result.strings = here.strings;
|
|
|
|
|
result.strings.isCofinite = !result.strings.isCofinite;
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
result.threads = get<NeverType>(here.threads) ? builtinTypes->threadType : builtinTypes->neverType;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
|
/*
|
|
|
|
|
* Things get weird and so, so complicated if we allow negations of
|
|
|
|
|
* arbitrary function types. Ordinary code can never form these kinds of
|
|
|
|
|
* types, so we decline to negate them.
|
|
|
|
|
*/
|
|
|
|
|
if (FFlag::LuauNegatedFunctionTypes)
|
|
|
|
|
{
|
|
|
|
|
if (here.functions.isNever())
|
|
|
|
|
result.functions.resetToTop();
|
|
|
|
|
else if (here.functions.isTop)
|
|
|
|
|
result.functions.resetToNever();
|
|
|
|
|
else
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 22:28:31 +00:00
|
|
|
|
/*
|
|
|
|
|
* It is not possible to negate an arbitrary table type, because function
|
|
|
|
|
* types are not runtime-testable. Thus, we prohibit negation of anything
|
|
|
|
|
* other than `table` and `never`.
|
|
|
|
|
*/
|
|
|
|
|
if (FFlag::LuauNegatedTableTypes)
|
|
|
|
|
{
|
|
|
|
|
if (here.tables.empty())
|
|
|
|
|
result.tables.insert(builtinTypes->tableType);
|
|
|
|
|
else if (here.tables.size() == 1 && here.tables.front() == builtinTypes->tableType)
|
|
|
|
|
result.tables.clear();
|
|
|
|
|
else
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
|
// TODO: negating tables
|
|
|
|
|
// TODO: negating tyvars?
|
2022-11-04 17:33:22 +00:00
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeIds Normalizer::negateAll(const TypeIds& theres)
|
|
|
|
|
{
|
|
|
|
|
TypeIds tys;
|
|
|
|
|
for (TypeId there : theres)
|
|
|
|
|
tys.insert(negate(there));
|
|
|
|
|
return tys;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeId Normalizer::negate(TypeId there)
|
|
|
|
|
{
|
|
|
|
|
there = follow(there);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<AnyType>(there))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
return there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<UnknownType>(there))
|
|
|
|
|
return builtinTypes->neverType;
|
|
|
|
|
else if (get<NeverType>(there))
|
|
|
|
|
return builtinTypes->unknownType;
|
|
|
|
|
else if (auto ntv = get<NegationType>(there))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
return ntv->ty; // TODO: do we want to normalize this?
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (auto utv = get<UnionType>(there))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
std::vector<TypeId> parts;
|
|
|
|
|
for (TypeId option : utv)
|
|
|
|
|
parts.push_back(negate(option));
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return arena->addType(IntersectionType{std::move(parts)});
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (auto itv = get<IntersectionType>(there))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
std::vector<TypeId> options;
|
|
|
|
|
for (TypeId part : itv)
|
|
|
|
|
options.push_back(negate(part));
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return arena->addType(UnionType{std::move(options)});
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return there;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::subtractPrimitive(NormalizedType& here, TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const PrimitiveType* ptv = get<PrimitiveType>(follow(ty));
|
2022-10-28 11:37:29 +01:00
|
|
|
|
LUAU_ASSERT(ptv);
|
|
|
|
|
switch (ptv->type)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
case PrimitiveType::NilType:
|
|
|
|
|
here.nils = builtinTypes->neverType;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
break;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
case PrimitiveType::Boolean:
|
|
|
|
|
here.booleans = builtinTypes->neverType;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
break;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
case PrimitiveType::Number:
|
|
|
|
|
here.numbers = builtinTypes->neverType;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
break;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
case PrimitiveType::String:
|
2022-11-04 17:33:22 +00:00
|
|
|
|
here.strings.resetToNever();
|
|
|
|
|
break;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
case PrimitiveType::Thread:
|
|
|
|
|
here.threads = builtinTypes->neverType;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
break;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
case PrimitiveType::Function:
|
2022-11-04 17:33:22 +00:00
|
|
|
|
here.functions.resetToNever();
|
|
|
|
|
break;
|
2023-01-27 22:28:31 +00:00
|
|
|
|
case PrimitiveType::Table:
|
|
|
|
|
LUAU_ASSERT(FFlag::LuauNegatedTableTypes);
|
|
|
|
|
here.tables.clear();
|
|
|
|
|
break;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::subtractSingleton(NormalizedType& here, TypeId ty)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const SingletonType* stv = get<SingletonType>(ty);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
LUAU_ASSERT(stv);
|
|
|
|
|
|
|
|
|
|
if (const StringSingleton* ss = get<StringSingleton>(stv))
|
|
|
|
|
{
|
|
|
|
|
if (here.strings.isCofinite)
|
2022-12-09 19:57:01 +00:00
|
|
|
|
here.strings.singletons.insert({ss->value, ty});
|
2022-10-28 11:37:29 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
auto it = here.strings.singletons.find(ss->value);
|
|
|
|
|
if (it != here.strings.singletons.end())
|
|
|
|
|
here.strings.singletons.erase(it);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (const BooleanSingleton* bs = get<BooleanSingleton>(stv))
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(here.booleans))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
// Nothing
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<PrimitiveType>(here.booleans))
|
|
|
|
|
here.booleans = bs->value ? builtinTypes->falseType : builtinTypes->trueType;
|
|
|
|
|
else if (auto hereSingleton = get<SingletonType>(here.booleans))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
const BooleanSingleton* hereBooleanSingleton = get<BooleanSingleton>(hereSingleton);
|
|
|
|
|
LUAU_ASSERT(hereBooleanSingleton);
|
|
|
|
|
|
|
|
|
|
// Crucial subtlety: ty (and thus bs) are the value that is being
|
|
|
|
|
// negated out. We therefore reduce to never when the values match,
|
|
|
|
|
// rather than when they differ.
|
|
|
|
|
if (bs->value == hereBooleanSingleton->value)
|
2023-01-04 20:53:17 +00:00
|
|
|
|
here.booleans = builtinTypes->neverType;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// ------- Normalizing intersections
|
|
|
|
|
TypeId Normalizer::intersectionOfTops(TypeId here, TypeId there)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(here) || get<AnyType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return here;
|
|
|
|
|
else
|
|
|
|
|
return there;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeId Normalizer::intersectionOfBools(TypeId here, TypeId there)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return here;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return there;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const BooleanSingleton* hbool = get<BooleanSingleton>(get<SingletonType>(here)))
|
|
|
|
|
if (const BooleanSingleton* tbool = get<BooleanSingleton>(get<SingletonType>(there)))
|
|
|
|
|
return (hbool->value == tbool->value ? here : builtinTypes->neverType);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
return here;
|
|
|
|
|
else
|
|
|
|
|
return there;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
void Normalizer::DEPRECATED_intersectClasses(TypeIds& heres, const TypeIds& theres)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeIds tmp;
|
|
|
|
|
for (auto it = heres.begin(); it != heres.end();)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const ClassType* hctv = get<ClassType>(*it);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(hctv);
|
|
|
|
|
bool keep = false;
|
|
|
|
|
for (TypeId there : theres)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const ClassType* tctv = get<ClassType>(there);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(tctv);
|
|
|
|
|
if (isSubclass(hctv, tctv))
|
|
|
|
|
{
|
|
|
|
|
keep = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (isSubclass(tctv, hctv))
|
|
|
|
|
{
|
|
|
|
|
keep = false;
|
|
|
|
|
tmp.insert(there);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (keep)
|
|
|
|
|
it++;
|
|
|
|
|
else
|
|
|
|
|
it = heres.erase(it);
|
|
|
|
|
}
|
|
|
|
|
heres.insert(tmp.begin(), tmp.end());
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
void Normalizer::DEPRECATED_intersectClassesWithClass(TypeIds& heres, TypeId there)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
bool foundSuper = false;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const ClassType* tctv = get<ClassType>(there);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(tctv);
|
|
|
|
|
for (auto it = heres.begin(); it != heres.end();)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const ClassType* hctv = get<ClassType>(*it);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(hctv);
|
|
|
|
|
if (isSubclass(hctv, tctv))
|
|
|
|
|
it++;
|
|
|
|
|
else if (isSubclass(tctv, hctv))
|
|
|
|
|
{
|
|
|
|
|
foundSuper = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
it = heres.erase(it);
|
|
|
|
|
}
|
|
|
|
|
if (foundSuper)
|
|
|
|
|
{
|
|
|
|
|
heres.clear();
|
|
|
|
|
heres.insert(there);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
void Normalizer::intersectClasses(NormalizedClassType& heres, const NormalizedClassType& theres)
|
|
|
|
|
{
|
|
|
|
|
if (theres.isNever())
|
|
|
|
|
{
|
|
|
|
|
heres.resetToNever();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (isTop(builtinTypes, theres))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For intersections of two distinct class sets, we must normalize to a map
|
|
|
|
|
// where, for each entry, one of the following is true:
|
|
|
|
|
// - The class is the superclass of all other classes in the map
|
|
|
|
|
// - The class is a subclass of another class B in the map _and_ a subclass
|
|
|
|
|
// of one of B's negations.
|
|
|
|
|
//
|
|
|
|
|
// Once we have identified the common superclass, we proceed down the list
|
|
|
|
|
// of class types. For each class and negation pair in the incoming set, we
|
|
|
|
|
// check each entry in the current set.
|
|
|
|
|
// - If the incoming class is exactly identical to a class in the current
|
|
|
|
|
// set, we union the negations together and move on.
|
|
|
|
|
// - If the incoming class is a subclass of a class in the current set, we
|
|
|
|
|
// replace the current class with the incoming class. We keep negations
|
|
|
|
|
// that are a subclass of the incoming class, and discard ones that
|
|
|
|
|
// aren't.
|
|
|
|
|
// - If the incoming class is a superclass of a class in the current set, we
|
|
|
|
|
// take the negations that are a subclass of the current class and union
|
|
|
|
|
// them with the negations for the current class.
|
|
|
|
|
// - If the incoming class is unrelated to any class in the current set, we
|
|
|
|
|
// declare the result of the intersection operation to be never.
|
|
|
|
|
for (const TypeId thereTy : theres.ordering)
|
|
|
|
|
{
|
|
|
|
|
const TypeIds& thereNegations = theres.classes.at(thereTy);
|
|
|
|
|
|
|
|
|
|
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
|
|
|
|
{
|
|
|
|
|
TypeId hereTy = *it;
|
|
|
|
|
TypeIds& hereNegations = heres.classes.at(hereTy);
|
|
|
|
|
|
|
|
|
|
if (isSubclass(thereTy, hereTy))
|
|
|
|
|
{
|
|
|
|
|
TypeIds negations = std::move(hereNegations);
|
|
|
|
|
|
|
|
|
|
for (auto nIt = negations.begin(); nIt != negations.end();)
|
|
|
|
|
{
|
|
|
|
|
if (!isSubclass(*nIt, thereTy))
|
|
|
|
|
{
|
|
|
|
|
nIt = negations.erase(nIt);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++nIt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unionClasses(negations, thereNegations);
|
|
|
|
|
|
|
|
|
|
it = heres.ordering.erase(it);
|
|
|
|
|
heres.classes.erase(hereTy);
|
|
|
|
|
heres.pushPair(thereTy, std::move(negations));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (isSubclass(hereTy, thereTy))
|
|
|
|
|
{
|
|
|
|
|
TypeIds negations = thereNegations;
|
|
|
|
|
|
|
|
|
|
for (auto nIt = negations.begin(); nIt != negations.end();)
|
|
|
|
|
{
|
|
|
|
|
if (!isSubclass(*nIt, hereTy))
|
|
|
|
|
{
|
|
|
|
|
nIt = negations.erase(nIt);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++nIt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unionClasses(hereNegations, negations);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (hereTy == thereTy)
|
|
|
|
|
{
|
|
|
|
|
unionClasses(hereNegations, thereNegations);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
it = heres.ordering.erase(it);
|
|
|
|
|
heres.classes.erase(hereTy);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::intersectClassesWithClass(NormalizedClassType& heres, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
for (auto it = heres.ordering.begin(); it != heres.ordering.end();)
|
|
|
|
|
{
|
|
|
|
|
TypeId hereTy = *it;
|
|
|
|
|
const TypeIds& hereNegations = heres.classes.at(hereTy);
|
|
|
|
|
|
|
|
|
|
// If the incoming class _is_ the current class, we skip it. Maybe
|
|
|
|
|
// another entry will have a different story. We check for this first
|
|
|
|
|
// because isSubclass will be true if the types are equal, and entering
|
|
|
|
|
// either of those branches below will trigger wrong behaviors.
|
|
|
|
|
if (hereTy == there)
|
|
|
|
|
{
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
// If the incoming class is a subclass of this type, we replace the
|
|
|
|
|
// current class with the incoming class. We preserve negations that are
|
|
|
|
|
// a subclass of the incoming class, and discard ones that aren't.
|
|
|
|
|
else if (isSubclass(there, hereTy))
|
|
|
|
|
{
|
|
|
|
|
TypeIds negations = std::move(hereNegations);
|
|
|
|
|
|
|
|
|
|
for (auto nIt = negations.begin(); nIt != negations.end();)
|
|
|
|
|
{
|
|
|
|
|
if (!isSubclass(*nIt, there))
|
|
|
|
|
{
|
|
|
|
|
nIt = negations.erase(nIt);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++nIt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
it = heres.ordering.erase(it);
|
|
|
|
|
heres.classes.erase(hereTy);
|
|
|
|
|
heres.pushPair(there, std::move(negations));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// If the incoming class is a superclass of the current class, we don't
|
|
|
|
|
// insert it into the map.
|
|
|
|
|
else if (isSubclass(hereTy, there))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// If the incoming class is completely unrelated to the current class,
|
|
|
|
|
// we drop the current class from the map.
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
it = heres.ordering.erase(it);
|
|
|
|
|
heres.classes.erase(hereTy);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
void Normalizer::intersectStrings(NormalizedStringType& here, const NormalizedStringType& there)
|
|
|
|
|
{
|
2022-10-28 11:37:29 +01:00
|
|
|
|
if (there.isString())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return;
|
2022-10-28 11:37:29 +01:00
|
|
|
|
if (here.isString())
|
|
|
|
|
here.resetToNever();
|
2022-10-07 01:23:29 +01:00
|
|
|
|
|
2022-12-09 19:57:01 +00:00
|
|
|
|
for (auto it = here.singletons.begin(); it != here.singletons.end();)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
if (there.singletons.count(it->first))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
it++;
|
|
|
|
|
else
|
2022-12-09 19:57:01 +00:00
|
|
|
|
it = here.singletons.erase(it);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<TypePackId> Normalizer::intersectionOfTypePacks(TypePackId here, TypePackId there)
|
|
|
|
|
{
|
|
|
|
|
if (here == there)
|
|
|
|
|
return here;
|
|
|
|
|
|
|
|
|
|
std::vector<TypeId> head;
|
|
|
|
|
std::optional<TypePackId> tail;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
bool hereSubThere = true;
|
|
|
|
|
bool thereSubHere = true;
|
|
|
|
|
|
|
|
|
|
TypePackIterator ith = begin(here);
|
|
|
|
|
TypePackIterator itt = begin(there);
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
while (ith != end(here) && itt != end(there))
|
|
|
|
|
{
|
|
|
|
|
TypeId hty = *ith;
|
|
|
|
|
TypeId tty = *itt;
|
|
|
|
|
TypeId ty = intersectionType(hty, tty);
|
|
|
|
|
if (ty != hty)
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
if (ty != tty)
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
head.push_back(ty);
|
|
|
|
|
ith++;
|
|
|
|
|
itt++;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
|
auto dealWithDifferentArities = [&](TypePackIterator& ith, TypePackIterator itt, TypePackId here, TypePackId there, bool& hereSubThere,
|
|
|
|
|
bool& thereSubHere) {
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (ith != end(here))
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
TypeId tty = builtinTypes->nilType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (std::optional<TypePackId> ttail = itt.tail())
|
|
|
|
|
{
|
|
|
|
|
if (const VariadicTypePack* tvtp = get<VariadicTypePack>(*ttail))
|
|
|
|
|
tty = tvtp->ty;
|
|
|
|
|
else
|
|
|
|
|
// Luau doesn't have intersections of type pack variables
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
// Type packs of different arities are incomparable
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
while (ith != end(here))
|
|
|
|
|
{
|
|
|
|
|
TypeId hty = *ith;
|
|
|
|
|
TypeId ty = intersectionType(hty, tty);
|
|
|
|
|
if (ty != hty)
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
if (ty != tty)
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
head.push_back(ty);
|
|
|
|
|
ith++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!dealWithDifferentArities(ith, itt, here, there, hereSubThere, thereSubHere))
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
if (!dealWithDifferentArities(itt, ith, there, here, thereSubHere, hereSubThere))
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
if (std::optional<TypePackId> htail = ith.tail())
|
|
|
|
|
{
|
|
|
|
|
if (std::optional<TypePackId> ttail = itt.tail())
|
|
|
|
|
{
|
|
|
|
|
if (*htail == *ttail)
|
|
|
|
|
tail = htail;
|
|
|
|
|
else if (const VariadicTypePack* hvtp = get<VariadicTypePack>(*htail))
|
|
|
|
|
{
|
|
|
|
|
if (const VariadicTypePack* tvtp = get<VariadicTypePack>(*ttail))
|
|
|
|
|
{
|
|
|
|
|
TypeId ty = intersectionType(hvtp->ty, tvtp->ty);
|
|
|
|
|
if (ty != hvtp->ty)
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
if (ty != tvtp->ty)
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
bool hidden = hvtp->hidden & tvtp->hidden;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
tail = arena->addTypePack(VariadicTypePack{ty, hidden});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
2022-10-14 20:48:41 +01:00
|
|
|
|
else
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
2022-10-14 20:48:41 +01:00
|
|
|
|
else
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
else if (get<VariadicTypePack>(*htail))
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
else
|
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
else if (std::optional<TypePackId> ttail = itt.tail())
|
|
|
|
|
{
|
|
|
|
|
if (get<VariadicTypePack>(*ttail))
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
else
|
|
|
|
|
// Luau doesn't have unions of type pack variables
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hereSubThere)
|
|
|
|
|
return here;
|
|
|
|
|
else if (thereSubHere)
|
|
|
|
|
return there;
|
|
|
|
|
if (!head.empty())
|
2022-10-14 20:48:41 +01:00
|
|
|
|
return arena->addTypePack(TypePack{head, tail});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else if (tail)
|
|
|
|
|
return *tail;
|
|
|
|
|
else
|
|
|
|
|
// TODO: Add an emptyPack to singleton types
|
|
|
|
|
return arena->addTypePack({});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
if (here == there)
|
|
|
|
|
return here;
|
|
|
|
|
|
|
|
|
|
RecursionCounter _rc(&sharedState->counters.recursionCount);
|
|
|
|
|
if (sharedState->counters.recursionLimit > 0 && sharedState->counters.recursionLimit < sharedState->counters.recursionCount)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
2023-01-27 22:28:31 +00:00
|
|
|
|
if (isPrim(here, PrimitiveType::Table))
|
|
|
|
|
return there;
|
|
|
|
|
else if (isPrim(there, PrimitiveType::Table))
|
|
|
|
|
return here;
|
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
TypeId htable = here;
|
|
|
|
|
TypeId hmtable = nullptr;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const MetatableType* hmtv = get<MetatableType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
htable = hmtv->table;
|
|
|
|
|
hmtable = hmtv->metatable;
|
|
|
|
|
}
|
|
|
|
|
TypeId ttable = there;
|
|
|
|
|
TypeId tmtable = nullptr;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const MetatableType* tmtv = get<MetatableType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
ttable = tmtv->table;
|
|
|
|
|
tmtable = tmtv->metatable;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const TableType* httv = get<TableType>(htable);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(httv);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const TableType* tttv = get<TableType>(ttable);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(tttv);
|
|
|
|
|
|
|
|
|
|
if (httv->state == TableState::Free || tttv->state == TableState::Free)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
if (httv->state == TableState::Generic || tttv->state == TableState::Generic)
|
|
|
|
|
return std::nullopt;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
TableState state = httv->state;
|
|
|
|
|
if (tttv->state == TableState::Unsealed)
|
|
|
|
|
state = tttv->state;
|
|
|
|
|
|
|
|
|
|
TypeLevel level = max(httv->level, tttv->level);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
TableType result{state, level};
|
2022-10-07 01:23:29 +01:00
|
|
|
|
|
|
|
|
|
bool hereSubThere = true;
|
|
|
|
|
bool thereSubHere = true;
|
|
|
|
|
|
|
|
|
|
for (const auto& [name, hprop] : httv->props)
|
|
|
|
|
{
|
|
|
|
|
Property prop = hprop;
|
|
|
|
|
auto tfound = tttv->props.find(name);
|
|
|
|
|
if (tfound == tttv->props.end())
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const auto& [_name, tprop] = *tfound;
|
|
|
|
|
// TODO: variance issues here, which can't be fixed until we have read/write property types
|
|
|
|
|
prop.type = intersectionType(hprop.type, tprop.type);
|
|
|
|
|
hereSubThere &= (prop.type == hprop.type);
|
|
|
|
|
thereSubHere &= (prop.type == tprop.type);
|
|
|
|
|
}
|
|
|
|
|
// TODO: string indexers
|
|
|
|
|
result.props[name] = prop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& [name, tprop] : tttv->props)
|
|
|
|
|
{
|
|
|
|
|
if (httv->props.count(name) == 0)
|
|
|
|
|
{
|
|
|
|
|
result.props[name] = tprop;
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (httv->indexer && tttv->indexer)
|
|
|
|
|
{
|
|
|
|
|
// TODO: What should intersection of indexes be?
|
|
|
|
|
TypeId index = unionType(httv->indexer->indexType, tttv->indexer->indexType);
|
|
|
|
|
TypeId indexResult = intersectionType(httv->indexer->indexResultType, tttv->indexer->indexResultType);
|
|
|
|
|
result.indexer = {index, indexResult};
|
|
|
|
|
hereSubThere &= (httv->indexer->indexType == index) && (httv->indexer->indexResultType == indexResult);
|
|
|
|
|
thereSubHere &= (tttv->indexer->indexType == index) && (tttv->indexer->indexResultType == indexResult);
|
|
|
|
|
}
|
|
|
|
|
else if (httv->indexer)
|
|
|
|
|
{
|
|
|
|
|
result.indexer = httv->indexer;
|
|
|
|
|
thereSubHere = false;
|
|
|
|
|
}
|
|
|
|
|
else if (tttv->indexer)
|
|
|
|
|
{
|
|
|
|
|
result.indexer = tttv->indexer;
|
|
|
|
|
hereSubThere = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeId table;
|
|
|
|
|
if (hereSubThere)
|
|
|
|
|
table = htable;
|
|
|
|
|
else if (thereSubHere)
|
|
|
|
|
table = ttable;
|
|
|
|
|
else
|
|
|
|
|
table = arena->addType(std::move(result));
|
|
|
|
|
|
|
|
|
|
if (tmtable && hmtable)
|
|
|
|
|
{
|
|
|
|
|
// NOTE: this assumes metatables are ivariant
|
|
|
|
|
if (std::optional<TypeId> mtable = intersectionOfTables(hmtable, tmtable))
|
|
|
|
|
{
|
|
|
|
|
if (table == htable && *mtable == hmtable)
|
|
|
|
|
return here;
|
|
|
|
|
else if (table == ttable && *mtable == tmtable)
|
|
|
|
|
return there;
|
|
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return arena->addType(MetatableType{table, *mtable});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
else if (hmtable)
|
|
|
|
|
{
|
|
|
|
|
if (table == htable)
|
|
|
|
|
return here;
|
|
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return arena->addType(MetatableType{table, hmtable});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
else if (tmtable)
|
|
|
|
|
{
|
|
|
|
|
if (table == ttable)
|
|
|
|
|
return there;
|
|
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return arena->addType(MetatableType{table, tmtable});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return table;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::intersectTablesWithTable(TypeIds& heres, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
TypeIds tmp;
|
|
|
|
|
for (TypeId here : heres)
|
|
|
|
|
if (std::optional<TypeId> inter = intersectionOfTables(here, there))
|
|
|
|
|
tmp.insert(*inter);
|
|
|
|
|
heres.retain(tmp);
|
|
|
|
|
heres.insert(tmp.begin(), tmp.end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::intersectTables(TypeIds& heres, const TypeIds& theres)
|
|
|
|
|
{
|
|
|
|
|
TypeIds tmp;
|
|
|
|
|
for (TypeId here : heres)
|
|
|
|
|
for (TypeId there : theres)
|
|
|
|
|
if (std::optional<TypeId> inter = intersectionOfTables(here, there))
|
|
|
|
|
tmp.insert(*inter);
|
|
|
|
|
heres.retain(tmp);
|
|
|
|
|
heres.insert(tmp.begin(), tmp.end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<TypeId> Normalizer::intersectionOfFunctions(TypeId here, TypeId there)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const FunctionType* hftv = get<FunctionType>(here);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(hftv);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const FunctionType* tftv = get<FunctionType>(there);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
LUAU_ASSERT(tftv);
|
|
|
|
|
|
|
|
|
|
if (hftv->generics != tftv->generics)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
if (hftv->genericPacks != tftv->genericPacks)
|
|
|
|
|
return std::nullopt;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-21 18:54:01 +01:00
|
|
|
|
TypePackId argTypes;
|
|
|
|
|
TypePackId retTypes;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
|
2022-10-21 18:54:01 +01:00
|
|
|
|
if (hftv->retTypes == tftv->retTypes)
|
|
|
|
|
{
|
|
|
|
|
std::optional<TypePackId> argTypesOpt = unionOfTypePacks(hftv->argTypes, tftv->argTypes);
|
|
|
|
|
if (!argTypesOpt)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
argTypes = *argTypesOpt;
|
|
|
|
|
retTypes = hftv->retTypes;
|
|
|
|
|
}
|
2023-01-20 20:27:03 +00:00
|
|
|
|
else if (hftv->argTypes == tftv->argTypes)
|
2022-10-21 18:54:01 +01:00
|
|
|
|
{
|
|
|
|
|
std::optional<TypePackId> retTypesOpt = intersectionOfTypePacks(hftv->argTypes, tftv->argTypes);
|
|
|
|
|
if (!retTypesOpt)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
argTypes = hftv->argTypes;
|
|
|
|
|
retTypes = *retTypesOpt;
|
|
|
|
|
}
|
|
|
|
|
else
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
2022-10-21 18:54:01 +01:00
|
|
|
|
if (argTypes == hftv->argTypes && retTypes == hftv->retTypes)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return here;
|
2022-10-21 18:54:01 +01:00
|
|
|
|
if (argTypes == tftv->argTypes && retTypes == tftv->retTypes)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return there;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
FunctionType result{argTypes, retTypes};
|
2022-10-07 01:23:29 +01:00
|
|
|
|
result.generics = hftv->generics;
|
|
|
|
|
result.genericPacks = hftv->genericPacks;
|
|
|
|
|
return arena->addType(std::move(result));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<TypeId> Normalizer::unionSaturatedFunctions(TypeId here, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
// Deep breath...
|
2022-10-14 20:48:41 +01:00
|
|
|
|
//
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// When we come to check overloaded functions for subtyping,
|
|
|
|
|
// we have to compare (F1 & ... & FM) <: (G1 & ... G GN)
|
|
|
|
|
// where each Fi or Gj is a function type. Now that intersection on the right is no
|
|
|
|
|
// problem, since that's true if and only if (F1 & ... & FM) <: Gj for every j.
|
|
|
|
|
// But the intersection on the left is annoying, since we might have
|
|
|
|
|
// (F1 & ... & FM) <: G but no Fi <: G. For example
|
|
|
|
|
//
|
|
|
|
|
// ((number? -> number?) & (string? -> string?)) <: (nil -> nil)
|
|
|
|
|
//
|
|
|
|
|
// So in this case, what we do is define Apply<F, T> for the result of applying
|
|
|
|
|
// a function of type F to an argument of type T, and then F <: (T -> U)
|
|
|
|
|
// if and only if Apply<F, T> <: U. For example:
|
|
|
|
|
//
|
|
|
|
|
// if f : ((number? -> number?) & (string? -> string?))
|
|
|
|
|
// then f(nil) must be nil, so
|
|
|
|
|
// Apply<((number? -> number?) & (string? -> string?)), nil> is nil
|
|
|
|
|
//
|
|
|
|
|
// So subtyping on overloaded functions "just" boils down to defining Apply<F, T>.
|
|
|
|
|
//
|
2022-10-14 20:48:41 +01:00
|
|
|
|
// Now for non-overloaded functions, this is easy!
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// Apply<(R -> S), T> is S if T <: R, and an error type otherwise.
|
|
|
|
|
//
|
|
|
|
|
// But for overloaded functions it's not so simple. We'd like Apply<F1 & ... & FM, T>
|
|
|
|
|
// to just be Apply<F1, T> & ... & Apply<FM, T> but oh dear
|
2022-10-14 20:48:41 +01:00
|
|
|
|
//
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// if f : ((number -> number) & (string -> string))
|
|
|
|
|
// and x : (number | string)
|
|
|
|
|
// then f(x) : (number | string)
|
|
|
|
|
//
|
|
|
|
|
// so we want
|
|
|
|
|
//
|
|
|
|
|
// Apply<((number -> number) & (string -> string)), (number | string)> is (number | string)
|
|
|
|
|
//
|
|
|
|
|
// but
|
2022-10-14 20:48:41 +01:00
|
|
|
|
//
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// Apply<(number -> number), (number | string)> is an error
|
|
|
|
|
// Apply<(string -> string), (number | string)> is an error
|
|
|
|
|
//
|
|
|
|
|
// that is Apply<F, T> should consider all possible combinations of overloads of F,
|
|
|
|
|
// not just individual overloads.
|
|
|
|
|
//
|
|
|
|
|
// For this reason, when we're normalizing function types (in order to check subtyping
|
|
|
|
|
// or perform overload resolution) we should first *union-saturate* them. An overloaded
|
|
|
|
|
// function is union-saturated whenever:
|
|
|
|
|
//
|
|
|
|
|
// if (R -> S) is an overload of F
|
|
|
|
|
// and (T -> U) is an overload of F
|
|
|
|
|
// then ((R | T) -> (S | U)) is a subtype of an overload of F
|
|
|
|
|
//
|
|
|
|
|
// Any overloaded function can be normalized to a union-saturated one by adding enough extra overloads.
|
|
|
|
|
// For example, union-saturating
|
|
|
|
|
//
|
|
|
|
|
// ((number -> number) & (string -> string))
|
|
|
|
|
//
|
|
|
|
|
// is
|
|
|
|
|
//
|
|
|
|
|
// ((number -> number) & (string -> string) & ((number | string) -> (number | string)))
|
|
|
|
|
//
|
|
|
|
|
// For union-saturated overloaded functions, the "obvious" algorithm works:
|
|
|
|
|
//
|
|
|
|
|
// Apply<F1 & ... & FM, T> is Apply<F1, T> & ... & Apply<FM, T>
|
|
|
|
|
//
|
|
|
|
|
// so we can define Apply, so we can perform overloaded function resolution
|
|
|
|
|
// and check subtyping on overloaded function types, yay!
|
|
|
|
|
//
|
|
|
|
|
// This is yet another potential source of exponential blow-up, sigh, since
|
|
|
|
|
// the union-saturation of a function with N overloads may have 2^N overloads
|
|
|
|
|
// (one for every subset). In practice, that hopefully won't happen that often,
|
|
|
|
|
// in particular we only union-saturate overloads with different return types,
|
|
|
|
|
// and there are hopefully not very many cases of that.
|
|
|
|
|
//
|
|
|
|
|
// All of this is mechanically verified in Agda, at https://github.com/luau-lang/agda-typeck
|
|
|
|
|
//
|
|
|
|
|
// It is essentially the algorithm defined in https://pnwamk.github.io/sst-tutorial/
|
|
|
|
|
// except that we're precomputing the union-saturation rather than converting
|
|
|
|
|
// to disjunctive normal form on the fly.
|
|
|
|
|
//
|
|
|
|
|
// This is all built on semantic subtyping:
|
|
|
|
|
//
|
|
|
|
|
// Covariance and Contravariance, Giuseppe Castagna,
|
|
|
|
|
// Logical Methods in Computer Science 16(1), 2022
|
|
|
|
|
// https://arxiv.org/abs/1809.01427
|
2022-10-14 20:48:41 +01:00
|
|
|
|
//
|
2022-10-07 01:23:29 +01:00
|
|
|
|
// A gentle introduction to semantic subtyping, Giuseppe Castagna and Alain Frisch,
|
|
|
|
|
// Proc. Principles and practice of declarative programming 2005, pp 198–208
|
|
|
|
|
// https://doi.org/10.1145/1069774.1069793
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const FunctionType* hftv = get<FunctionType>(here);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (!hftv)
|
|
|
|
|
return std::nullopt;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
const FunctionType* tftv = get<FunctionType>(there);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (!tftv)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
|
|
if (hftv->generics != tftv->generics)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
if (hftv->genericPacks != tftv->genericPacks)
|
|
|
|
|
return std::nullopt;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
std::optional<TypePackId> argTypes = unionOfTypePacks(hftv->argTypes, tftv->argTypes);
|
|
|
|
|
if (!argTypes)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
std::optional<TypePackId> retTypes = unionOfTypePacks(hftv->retTypes, tftv->retTypes);
|
|
|
|
|
if (!retTypes)
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
FunctionType result{*argTypes, *retTypes};
|
2022-10-07 01:23:29 +01:00
|
|
|
|
result.generics = hftv->generics;
|
|
|
|
|
result.genericPacks = hftv->genericPacks;
|
|
|
|
|
return arena->addType(std::move(result));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::intersectFunctionsWithFunction(NormalizedFunctionType& heres, TypeId there)
|
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
if (heres.isNever())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
|
heres.isTop = false;
|
|
|
|
|
|
|
|
|
|
for (auto it = heres.parts->begin(); it != heres.parts->end();)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeId here = *it;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<ErrorType>(here))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
it++;
|
|
|
|
|
else if (std::optional<TypeId> tmp = intersectionOfFunctions(here, there))
|
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
heres.parts->erase(it);
|
|
|
|
|
heres.parts->insert(*tmp);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
it++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeIds tmps;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
for (TypeId here : *heres.parts)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
if (std::optional<TypeId> tmp = unionSaturatedFunctions(here, there))
|
|
|
|
|
tmps.insert(*tmp);
|
|
|
|
|
}
|
2022-11-04 17:33:22 +00:00
|
|
|
|
heres.parts->insert(there);
|
|
|
|
|
heres.parts->insert(tmps.begin(), tmps.end());
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Normalizer::intersectFunctions(NormalizedFunctionType& heres, const NormalizedFunctionType& theres)
|
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
if (heres.isNever())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
else if (theres.isNever())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
heres.resetToNever();
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
}
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
for (TypeId there : *theres.parts)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
intersectFunctionsWithFunction(heres, there);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Normalizer::intersectTyvarsWithTy(NormalizedTyvars& here, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
for (auto it = here.begin(); it != here.end();)
|
|
|
|
|
{
|
|
|
|
|
NormalizedType& inter = *it->second;
|
|
|
|
|
if (!intersectNormalWithTy(inter, there))
|
|
|
|
|
return false;
|
2022-12-02 18:09:59 +00:00
|
|
|
|
if (isShallowInhabited(inter))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
++it;
|
|
|
|
|
else
|
|
|
|
|
it = here.erase(it);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See above for an explaination of `ignoreSmallerTyvars`.
|
|
|
|
|
bool Normalizer::intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<NeverType>(there.tops))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
here.tops = intersectionOfTops(here.tops, there.tops);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (!get<NeverType>(here.tops))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
return unionNormals(here, there, ignoreSmallerTyvars);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
here.booleans = intersectionOfBools(here.booleans, there.booleans);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
|
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
|
{
|
|
|
|
|
intersectClasses(here.classes, there.classes);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DEPRECATED_intersectClasses(here.DEPRECATED_classes, there.DEPRECATED_classes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
here.errors = (get<NeverType>(there.errors) ? there.errors : here.errors);
|
|
|
|
|
here.nils = (get<NeverType>(there.nils) ? there.nils : here.nils);
|
|
|
|
|
here.numbers = (get<NeverType>(there.numbers) ? there.numbers : here.numbers);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
intersectStrings(here.strings, there.strings);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
here.threads = (get<NeverType>(there.threads) ? there.threads : here.threads);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
intersectFunctions(here.functions, there.functions);
|
|
|
|
|
intersectTables(here.tables, there.tables);
|
|
|
|
|
|
|
|
|
|
for (auto& [tyvar, inter] : there.tyvars)
|
|
|
|
|
{
|
|
|
|
|
int index = tyvarIndex(tyvar);
|
|
|
|
|
if (ignoreSmallerTyvars < index)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
auto [found, fresh] = here.tyvars.emplace(tyvar, std::make_unique<NormalizedType>(NormalizedType{builtinTypes}));
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (fresh)
|
|
|
|
|
{
|
|
|
|
|
if (!unionNormals(*found->second, here, index))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (auto it = here.tyvars.begin(); it != here.tyvars.end();)
|
|
|
|
|
{
|
|
|
|
|
TypeId tyvar = it->first;
|
|
|
|
|
NormalizedType& inter = *it->second;
|
|
|
|
|
int index = tyvarIndex(tyvar);
|
|
|
|
|
LUAU_ASSERT(ignoreSmallerTyvars < index);
|
|
|
|
|
auto found = there.tyvars.find(tyvar);
|
|
|
|
|
if (found == there.tyvars.end())
|
|
|
|
|
{
|
|
|
|
|
if (!intersectNormals(inter, there, index))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!intersectNormals(inter, *found->second, index))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-02 18:09:59 +00:00
|
|
|
|
if (isShallowInhabited(inter))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
it++;
|
|
|
|
|
else
|
2022-10-14 20:48:41 +01:00
|
|
|
|
it = here.tyvars.erase(it);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Normalizer::intersectNormalWithTy(NormalizedType& here, TypeId there)
|
|
|
|
|
{
|
|
|
|
|
RecursionCounter _rc(&sharedState->counters.recursionCount);
|
|
|
|
|
if (!withinResourceLimits())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
there = follow(there);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<AnyType>(there) || get<UnknownType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
here.tops = intersectionOfTops(here.tops, there);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (!get<NeverType>(here.tops))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
return unionNormalWithTy(here, there);
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const UnionType* utv = get<UnionType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
NormalizedType norm{builtinTypes};
|
|
|
|
|
for (UnionTypeIterator it = begin(utv); it != end(utv); ++it)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (!unionNormalWithTy(norm, *it))
|
|
|
|
|
return false;
|
|
|
|
|
return intersectNormals(here, norm);
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const IntersectionType* itv = get<IntersectionType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
for (IntersectionTypeIterator it = begin(itv); it != end(itv); ++it)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
if (!intersectNormalWithTy(here, *it))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-03-17 19:20:37 +00:00
|
|
|
|
else if (get<GenericType>(there) || get<FreeType>(there) || (FFlag::LuauNormalizeBlockedTypes && get<BlockedType>(there)))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
NormalizedType thereNorm{builtinTypes};
|
|
|
|
|
NormalizedType topNorm{builtinTypes};
|
|
|
|
|
topNorm.tops = builtinTypes->unknownType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
thereNorm.tyvars.insert_or_assign(there, std::make_unique<NormalizedType>(std::move(topNorm)));
|
|
|
|
|
return intersectNormals(here, thereNorm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NormalizedTyvars tyvars = std::move(here.tyvars);
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const FunctionType* utv = get<FunctionType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
NormalizedFunctionType functions = std::move(here.functions);
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
intersectFunctionsWithFunction(functions, there);
|
|
|
|
|
here.functions = std::move(functions);
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<TableType>(there) || get<MetatableType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeIds tables = std::move(here.tables);
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
intersectTablesWithTable(tables, there);
|
|
|
|
|
here.tables = std::move(tables);
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<ClassType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
|
{
|
|
|
|
|
NormalizedClassType nct = std::move(here.classes);
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
intersectClassesWithClass(nct, there);
|
|
|
|
|
here.classes = std::move(nct);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TypeIds classes = std::move(here.DEPRECATED_classes);
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
DEPRECATED_intersectClassesWithClass(classes, there);
|
|
|
|
|
here.DEPRECATED_classes = std::move(classes);
|
|
|
|
|
}
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<ErrorType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeId errors = here.errors;
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
here.errors = errors;
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeId booleans = here.booleans;
|
|
|
|
|
TypeId nils = here.nils;
|
|
|
|
|
TypeId numbers = here.numbers;
|
|
|
|
|
NormalizedStringType strings = std::move(here.strings);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
NormalizedFunctionType functions = std::move(here.functions);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
TypeId threads = here.threads;
|
2023-01-27 22:28:31 +00:00
|
|
|
|
TypeIds tables = std::move(here.tables);
|
2022-10-07 01:23:29 +01:00
|
|
|
|
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (ptv->type == PrimitiveType::Boolean)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.booleans = booleans;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::NilType)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.nils = nils;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::Number)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.numbers = numbers;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::String)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.strings = std::move(strings);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::Thread)
|
2022-10-07 01:23:29 +01:00
|
|
|
|
here.threads = threads;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::Function)
|
2022-11-04 17:33:22 +00:00
|
|
|
|
{
|
|
|
|
|
LUAU_ASSERT(FFlag::LuauNegatedFunctionTypes);
|
|
|
|
|
here.functions = std::move(functions);
|
|
|
|
|
}
|
2023-01-27 22:28:31 +00:00
|
|
|
|
else if (ptv->type == PrimitiveType::Table)
|
|
|
|
|
{
|
|
|
|
|
LUAU_ASSERT(FFlag::LuauNegatedTableTypes);
|
|
|
|
|
here.tables = std::move(tables);
|
|
|
|
|
}
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const SingletonType* stv = get<SingletonType>(there))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeId booleans = here.booleans;
|
|
|
|
|
NormalizedStringType strings = std::move(here.strings);
|
|
|
|
|
|
|
|
|
|
clearNormal(here);
|
|
|
|
|
|
|
|
|
|
if (get<BooleanSingleton>(stv))
|
|
|
|
|
here.booleans = intersectionOfBools(booleans, there);
|
|
|
|
|
else if (const StringSingleton* sstv = get<StringSingleton>(stv))
|
|
|
|
|
{
|
2022-10-28 11:37:29 +01:00
|
|
|
|
if (strings.includes(sstv->value))
|
2022-12-09 19:57:01 +00:00
|
|
|
|
here.strings.singletons.insert({sstv->value, there});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const NegationType* ntv = get<NegationType>(there))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeId t = follow(ntv->ty);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (const PrimitiveType* ptv = get<PrimitiveType>(t))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
subtractPrimitive(here, ntv->ty);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (const SingletonType* stv = get<SingletonType>(t))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
subtractSingleton(here, follow(ntv->ty));
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<ClassType>(t) && FFlag::LuauNegatedClassTypes)
|
|
|
|
|
{
|
|
|
|
|
const NormalizedType* normal = normalize(t);
|
|
|
|
|
std::optional<NormalizedType> negated = negateNormal(*normal);
|
|
|
|
|
if (!negated)
|
|
|
|
|
return false;
|
|
|
|
|
intersectNormals(here, *negated);
|
|
|
|
|
}
|
|
|
|
|
else if (const UnionType* itv = get<UnionType>(t))
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
for (TypeId part : itv->options)
|
|
|
|
|
{
|
|
|
|
|
const NormalizedType* normalPart = normalize(part);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
std::optional<NormalizedType> negated = negateNormal(*normalPart);
|
|
|
|
|
if (!negated)
|
|
|
|
|
return false;
|
|
|
|
|
intersectNormals(here, *negated);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-03 20:21:14 +00:00
|
|
|
|
else if (get<AnyType>(t))
|
|
|
|
|
{
|
|
|
|
|
// HACK: Refinements sometimes intersect with ~any under the
|
|
|
|
|
// assumption that it is the same as any.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (auto nt = get<NegationType>(t))
|
|
|
|
|
return intersectNormalWithTy(here, nt->ty);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// TODO negated unions, intersections, table, and function.
|
|
|
|
|
// Report a TypeError for other types.
|
|
|
|
|
LUAU_ASSERT(!"Unimplemented");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
else if (get<NeverType>(there) && FFlag::LuauNegatedClassTypes)
|
|
|
|
|
{
|
|
|
|
|
here.classes.resetToNever();
|
|
|
|
|
}
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
LUAU_ASSERT(!"Unreachable");
|
|
|
|
|
|
|
|
|
|
if (!intersectTyvarsWithTy(tyvars, there))
|
|
|
|
|
return false;
|
|
|
|
|
here.tyvars = std::move(tyvars);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -------- Convert back from a normalized type to a type
|
|
|
|
|
TypeId Normalizer::typeFromNormal(const NormalizedType& norm)
|
|
|
|
|
{
|
|
|
|
|
assertInvariant(norm);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<NeverType>(norm.tops))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
return norm.tops;
|
|
|
|
|
|
|
|
|
|
std::vector<TypeId> result;
|
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<NeverType>(norm.booleans))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
result.push_back(norm.booleans);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
|
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
|
{
|
|
|
|
|
if (isTop(builtinTypes, norm.classes))
|
|
|
|
|
{
|
|
|
|
|
result.push_back(builtinTypes->classType);
|
|
|
|
|
}
|
|
|
|
|
else if (!norm.classes.isNever())
|
|
|
|
|
{
|
|
|
|
|
std::vector<TypeId> parts;
|
|
|
|
|
parts.reserve(norm.classes.classes.size());
|
|
|
|
|
|
|
|
|
|
for (const TypeId normTy : norm.classes.ordering)
|
|
|
|
|
{
|
|
|
|
|
const TypeIds& normNegations = norm.classes.classes.at(normTy);
|
|
|
|
|
|
|
|
|
|
if (normNegations.empty())
|
|
|
|
|
{
|
|
|
|
|
parts.push_back(normTy);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
std::vector<TypeId> intersection;
|
|
|
|
|
intersection.reserve(normNegations.size() + 1);
|
|
|
|
|
|
|
|
|
|
intersection.push_back(normTy);
|
|
|
|
|
for (TypeId negation : normNegations)
|
|
|
|
|
{
|
|
|
|
|
intersection.push_back(arena->addType(NegationType{negation}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parts.push_back(arena->addType(IntersectionType{std::move(intersection)}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (parts.size() == 1)
|
|
|
|
|
{
|
|
|
|
|
result.push_back(parts.at(0));
|
|
|
|
|
}
|
|
|
|
|
else if (parts.size() > 1)
|
|
|
|
|
{
|
|
|
|
|
result.push_back(arena->addType(UnionType{std::move(parts)}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result.insert(result.end(), norm.DEPRECATED_classes.begin(), norm.DEPRECATED_classes.end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!get<NeverType>(norm.errors))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
result.push_back(norm.errors);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
if (FFlag::LuauNegatedFunctionTypes && norm.functions.isTop)
|
2023-01-04 20:53:17 +00:00
|
|
|
|
result.push_back(builtinTypes->functionType);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
else if (!norm.functions.isNever())
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
|
if (norm.functions.parts->size() == 1)
|
|
|
|
|
result.push_back(*norm.functions.parts->begin());
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
std::vector<TypeId> parts;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
parts.insert(parts.end(), norm.functions.parts->begin(), norm.functions.parts->end());
|
2023-01-04 20:53:17 +00:00
|
|
|
|
result.push_back(arena->addType(IntersectionType{std::move(parts)}));
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<NeverType>(norm.nils))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
result.push_back(norm.nils);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<NeverType>(norm.numbers))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
result.push_back(norm.numbers);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
if (norm.strings.isString())
|
2023-01-04 20:53:17 +00:00
|
|
|
|
result.push_back(builtinTypes->stringType);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
else if (norm.strings.isUnion())
|
|
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
|
for (auto& [_, ty] : norm.strings.singletons)
|
2022-10-28 11:37:29 +01:00
|
|
|
|
result.push_back(ty);
|
|
|
|
|
}
|
2022-12-09 19:57:01 +00:00
|
|
|
|
else if (norm.strings.isIntersection())
|
2022-10-28 11:37:29 +01:00
|
|
|
|
{
|
|
|
|
|
std::vector<TypeId> parts;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
parts.push_back(builtinTypes->stringType);
|
2022-12-09 19:57:01 +00:00
|
|
|
|
for (const auto& [name, ty] : norm.strings.singletons)
|
2023-01-04 20:53:17 +00:00
|
|
|
|
parts.push_back(arena->addType(NegationType{ty}));
|
2022-10-28 11:37:29 +01:00
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
result.push_back(arena->addType(IntersectionType{std::move(parts)}));
|
2022-10-28 11:37:29 +01:00
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (!get<NeverType>(norm.threads))
|
|
|
|
|
result.push_back(builtinTypes->threadType);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
|
result.insert(result.end(), norm.tables.begin(), norm.tables.end());
|
|
|
|
|
for (auto& [tyvar, intersect] : norm.tyvars)
|
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
|
if (get<NeverType>(intersect->tops))
|
2022-10-07 01:23:29 +01:00
|
|
|
|
{
|
|
|
|
|
TypeId ty = typeFromNormal(*intersect);
|
2023-01-04 20:53:17 +00:00
|
|
|
|
result.push_back(arena->addType(IntersectionType{{tyvar, ty}}));
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
result.push_back(tyvar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.size() == 0)
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return builtinTypes->neverType;
|
2022-10-07 01:23:29 +01:00
|
|
|
|
else if (result.size() == 1)
|
|
|
|
|
return result[0];
|
|
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
|
return arena->addType(UnionType{std::move(result)});
|
2022-10-07 01:23:29 +01:00
|
|
|
|
}
|
2022-04-15 00:57:43 +01:00
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
|
bool isSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice)
|
2023-03-17 19:20:37 +00:00
|
|
|
|
{
|
|
|
|
|
if (!FFlag::LuauTransitiveSubtyping)
|
|
|
|
|
return isConsistentSubtype(subTy, superTy, scope, builtinTypes, ice);
|
|
|
|
|
UnifierSharedState sharedState{&ice};
|
|
|
|
|
TypeArena arena;
|
|
|
|
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
|
|
|
|
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, Location{}, Covariant};
|
|
|
|
|
|
|
|
|
|
u.tryUnify(subTy, superTy);
|
|
|
|
|
return !u.failure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isSubtype(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice)
|
|
|
|
|
{
|
|
|
|
|
if (!FFlag::LuauTransitiveSubtyping)
|
|
|
|
|
return isConsistentSubtype(subPack, superPack, scope, builtinTypes, ice);
|
|
|
|
|
UnifierSharedState sharedState{&ice};
|
|
|
|
|
TypeArena arena;
|
|
|
|
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
|
|
|
|
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, Location{}, Covariant};
|
|
|
|
|
|
|
|
|
|
u.tryUnify(subPack, superPack);
|
|
|
|
|
return !u.failure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isConsistentSubtype(TypeId subTy, TypeId superTy, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice)
|
2022-04-15 00:57:43 +01:00
|
|
|
|
{
|
|
|
|
|
UnifierSharedState sharedState{&ice};
|
|
|
|
|
TypeArena arena;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
2022-10-07 01:23:29 +01:00
|
|
|
|
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, Location{}, Covariant};
|
2022-04-15 00:57:43 +01:00
|
|
|
|
|
|
|
|
|
u.tryUnify(subTy, superTy);
|
|
|
|
|
const bool ok = u.errors.empty() && u.log.empty();
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
|
bool isConsistentSubtype(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope, NotNull<BuiltinTypes> builtinTypes, InternalErrorReporter& ice)
|
2022-06-17 02:05:14 +01:00
|
|
|
|
{
|
|
|
|
|
UnifierSharedState sharedState{&ice};
|
|
|
|
|
TypeArena arena;
|
2023-01-04 20:53:17 +00:00
|
|
|
|
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}};
|
2022-10-07 01:23:29 +01:00
|
|
|
|
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, Location{}, Covariant};
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
|
|
u.tryUnify(subPack, superPack);
|
|
|
|
|
const bool ok = u.errors.empty() && u.log.empty();
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-15 00:57:43 +01:00
|
|
|
|
} // namespace Luau
|