luau/Analysis/src/Unifier.cpp

3102 lines
104 KiB
C++
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Unifier.h"
#include "Luau/Common.h"
2022-10-07 00:55:58 +01:00
#include "Luau/Instantiation.h"
#include "Luau/RecursionCounter.h"
#include "Luau/Scope.h"
2022-12-09 18:07:25 +00:00
#include "Luau/StringUtils.h"
#include "Luau/TimeTrace.h"
#include "Luau/ToString.h"
#include "Luau/TypePack.h"
#include "Luau/TypeUtils.h"
2023-01-03 17:33:19 +00:00
#include "Luau/Type.h"
#include "Luau/VisitType.h"
2023-05-12 13:15:01 +01:00
#include "Luau/TypeFamily.h"
#include <algorithm>
2023-01-27 21:28:45 +00:00
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit)
LUAU_FASTFLAG(LuauErrorRecoveryType)
2022-10-07 00:55:58 +01:00
LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false)
2023-01-20 12:02:39 +00:00
LUAU_FASTFLAGVARIABLE(LuauMaintainScopesInUnifier, false)
2023-03-17 14:59:30 +00:00
LUAU_FASTFLAGVARIABLE(LuauTransitiveSubtyping, false)
2023-04-14 13:05:27 +01:00
LUAU_FASTFLAGVARIABLE(LuauOccursIsntAlwaysFailure, false)
2023-06-02 19:17:31 +01:00
LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
2023-09-15 17:27:45 +01:00
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering, false)
namespace Luau
{
2023-01-03 17:33:19 +00:00
struct PromoteTypeLevels final : TypeOnceVisitor
{
TxnLog& log;
2022-02-11 18:43:14 +00:00
const TypeArena* typeArena = nullptr;
TypeLevel minLevel;
2022-09-29 23:11:54 +01:00
Scope* outerScope = nullptr;
bool useScopes;
PromoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes)
2022-03-11 16:31:18 +00:00
: log(log)
2022-02-11 18:43:14 +00:00
, typeArena(typeArena)
, minLevel(minLevel)
2022-09-29 23:11:54 +01:00
, outerScope(outerScope)
, useScopes(useScopes)
2022-01-06 22:10:07 +00:00
{
}
2022-01-06 22:10:07 +00:00
template<typename TID, typename T>
void promote(TID ty, T* t)
{
2023-07-28 12:37:00 +01:00
if (useScopes && !t)
2022-12-02 10:46:05 +00:00
return;
LUAU_ASSERT(t);
2022-09-29 23:11:54 +01:00
if (useScopes)
{
2022-09-29 23:11:54 +01:00
if (subsumesStrict(outerScope, t->scope))
log.changeScope(ty, NotNull{outerScope});
}
else
{
if (minLevel.subsumesStrict(t->level))
{
log.changeLevel(ty, minLevel);
}
}
}
2022-05-06 00:52:48 +01:00
bool visit(TypeId ty) override
{
2022-02-11 18:43:14 +00:00
// Type levels of types from other modules are already global, so we don't need to promote anything inside
2022-03-24 21:49:08 +00:00
if (ty->owningArena != typeArena)
2022-02-11 18:43:14 +00:00
return false;
return true;
}
2022-05-06 00:52:48 +01:00
bool visit(TypePackId tp) override
{
// Type levels of types from other modules are already global, so we don't need to promote anything inside
if (tp->owningArena != typeArena)
return false;
return true;
}
2023-01-03 17:33:19 +00:00
bool visit(TypeId ty, const FreeType&) override
{
2023-01-03 17:33:19 +00:00
// Surprise, it's actually a BoundType that hasn't been committed yet.
2022-01-06 22:10:07 +00:00
// Calling getMutable on this will trigger an assertion.
2023-01-03 17:33:19 +00:00
if (!log.is<FreeType>(ty))
2022-01-06 22:10:07 +00:00
return true;
2023-01-03 17:33:19 +00:00
promote(ty, log.getMutable<FreeType>(ty));
return true;
}
2023-01-03 17:33:19 +00:00
bool visit(TypeId ty, const FunctionType&) override
{
2022-02-11 18:43:14 +00:00
// Type levels of types from other modules are already global, so we don't need to promote anything inside
2022-03-24 21:49:08 +00:00
if (ty->owningArena != typeArena)
2022-02-11 18:43:14 +00:00
return false;
2022-12-02 10:46:05 +00:00
// Surprise, it's actually a BoundTypePack that hasn't been committed yet.
// Calling getMutable on this will trigger an assertion.
2023-03-10 19:20:04 +00:00
if (!log.is<FunctionType>(ty))
2022-12-02 10:46:05 +00:00
return true;
2023-01-03 17:33:19 +00:00
promote(ty, log.getMutable<FunctionType>(ty));
return true;
}
2023-01-03 17:33:19 +00:00
bool visit(TypeId ty, const TableType& ttv) override
{
2022-02-11 18:43:14 +00:00
// Type levels of types from other modules are already global, so we don't need to promote anything inside
2022-03-24 21:49:08 +00:00
if (ty->owningArena != typeArena)
2022-02-11 18:43:14 +00:00
return false;
2022-01-06 22:10:07 +00:00
if (ttv.state != TableState::Free && ttv.state != TableState::Generic)
return true;
2022-12-02 10:46:05 +00:00
// Surprise, it's actually a BoundTypePack that hasn't been committed yet.
// Calling getMutable on this will trigger an assertion.
2023-03-10 19:20:04 +00:00
if (!log.is<TableType>(ty))
2022-12-02 10:46:05 +00:00
return true;
2023-01-03 17:33:19 +00:00
promote(ty, log.getMutable<TableType>(ty));
return true;
}
2022-05-06 00:52:48 +01:00
bool visit(TypePackId tp, const FreeTypePack&) override
{
2022-01-21 16:23:02 +00:00
// Surprise, it's actually a BoundTypePack that hasn't been committed yet.
// Calling getMutable on this will trigger an assertion.
2022-03-11 16:31:18 +00:00
if (!log.is<FreeTypePack>(tp))
2022-01-21 16:23:02 +00:00
return true;
2022-03-11 16:31:18 +00:00
promote(tp, log.getMutable<FreeTypePack>(tp));
return true;
}
};
2022-09-29 23:11:54 +01:00
static void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes, TypeId ty)
{
2022-02-11 18:43:14 +00:00
// Type levels of types from other modules are already global, so we don't need to promote anything inside
2022-03-24 21:49:08 +00:00
if (ty->owningArena != typeArena)
2022-02-11 18:43:14 +00:00
return;
2022-09-29 23:11:54 +01:00
PromoteTypeLevels ptl{log, typeArena, minLevel, outerScope, useScopes};
2022-05-26 21:33:48 +01:00
ptl.traverse(ty);
}
2022-09-29 23:11:54 +01:00
void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes, TypePackId tp)
{
2022-02-11 18:43:14 +00:00
// Type levels of types from other modules are already global, so we don't need to promote anything inside
2022-03-24 21:49:08 +00:00
if (tp->owningArena != typeArena)
2022-02-11 18:43:14 +00:00
return;
2022-09-29 23:11:54 +01:00
PromoteTypeLevels ptl{log, typeArena, minLevel, outerScope, useScopes};
2022-05-26 21:33:48 +01:00
ptl.traverse(tp);
}
2023-01-03 17:33:19 +00:00
struct SkipCacheForType final : TypeOnceVisitor
{
2022-02-11 18:43:14 +00:00
SkipCacheForType(const DenseHashMap<TypeId, bool>& skipCacheForType, const TypeArena* typeArena)
: skipCacheForType(skipCacheForType)
2022-02-11 18:43:14 +00:00
, typeArena(typeArena)
{
}
2023-01-03 17:33:19 +00:00
bool visit(TypeId, const FreeType&) override
{
result = true;
return false;
}
2023-01-03 17:33:19 +00:00
bool visit(TypeId, const BoundType&) override
{
result = true;
return false;
}
2023-01-03 17:33:19 +00:00
bool visit(TypeId, const GenericType&) override
{
result = true;
return false;
}
2023-03-24 17:34:14 +00:00
bool visit(TypeId, const BlockedType&) override
{
result = true;
return false;
}
bool visit(TypeId, const PendingExpansionType&) override
{
result = true;
return false;
}
2023-01-03 17:33:19 +00:00
bool visit(TypeId ty, const TableType&) override
{
2022-02-11 18:43:14 +00:00
// Types from other modules don't contain mutable elements and are ok to cache
2022-03-24 21:49:08 +00:00
if (ty->owningArena != typeArena)
2022-02-11 18:43:14 +00:00
return false;
2023-01-03 17:33:19 +00:00
TableType& ttv = *getMutable<TableType>(ty);
if (ttv.boundTo)
{
result = true;
return false;
}
if (ttv.state != TableState::Sealed)
{
result = true;
return false;
}
return true;
}
2022-05-06 00:52:48 +01:00
bool visit(TypeId ty) override
{
2022-02-11 18:43:14 +00:00
// Types from other modules don't contain mutable elements and are ok to cache
2022-03-24 21:49:08 +00:00
if (ty->owningArena != typeArena)
2022-02-11 18:43:14 +00:00
return false;
const bool* prev = skipCacheForType.find(ty);
if (prev && *prev)
{
result = true;
return false;
}
return true;
}
2022-05-06 00:52:48 +01:00
bool visit(TypePackId tp) override
{
2022-02-11 18:43:14 +00:00
// Types from other modules don't contain mutable elements and are ok to cache
2022-03-24 21:49:08 +00:00
if (tp->owningArena != typeArena)
2022-02-11 18:43:14 +00:00
return false;
return true;
}
2022-05-06 00:52:48 +01:00
bool visit(TypePackId tp, const FreeTypePack&) override
{
result = true;
return false;
}
2022-05-06 00:52:48 +01:00
bool visit(TypePackId tp, const BoundTypePack&) override
{
result = true;
return false;
}
2022-05-06 00:52:48 +01:00
bool visit(TypePackId tp, const GenericTypePack&) override
{
result = true;
return false;
}
2023-03-24 17:34:14 +00:00
bool visit(TypePackId tp, const BlockedTypePack&) override
{
result = true;
return false;
}
const DenseHashMap<TypeId, bool>& skipCacheForType;
2022-02-11 18:43:14 +00:00
const TypeArena* typeArena = nullptr;
bool result = false;
};
2022-02-24 23:15:41 +00:00
bool Widen::isDirty(TypeId ty)
{
2023-01-03 17:33:19 +00:00
return log->is<SingletonType>(ty);
2022-02-24 23:15:41 +00:00
}
bool Widen::isDirty(TypePackId)
{
return false;
}
TypeId Widen::clean(TypeId ty)
{
LUAU_ASSERT(isDirty(ty));
2023-01-03 17:33:19 +00:00
auto stv = log->getMutable<SingletonType>(ty);
2022-02-24 23:15:41 +00:00
LUAU_ASSERT(stv);
if (get<StringSingleton>(stv))
2023-01-03 17:33:19 +00:00
return builtinTypes->stringType;
2022-02-24 23:15:41 +00:00
else
{
// If this assert trips, it's likely we now have number singletons.
LUAU_ASSERT(get<BooleanSingleton>(stv));
2023-01-03 17:33:19 +00:00
return builtinTypes->booleanType;
2022-02-24 23:15:41 +00:00
}
}
TypePackId Widen::clean(TypePackId)
{
2022-12-02 10:46:05 +00:00
throw InternalCompilerError("Widen attempted to clean a dirty type pack?");
2022-02-24 23:15:41 +00:00
}
bool Widen::ignoreChildren(TypeId ty)
{
2023-06-24 06:33:44 +01:00
if (get<ClassType>(ty))
2022-08-11 21:42:54 +01:00
return true;
2023-01-03 17:33:19 +00:00
return !log->is<UnionType>(ty);
2022-02-24 23:15:41 +00:00
}
2022-05-26 21:33:48 +01:00
TypeId Widen::operator()(TypeId ty)
{
return substitute(ty).value_or(ty);
}
TypePackId Widen::operator()(TypePackId tp)
{
return substitute(tp).value_or(tp);
}
2023-03-03 13:45:38 +00:00
std::optional<TypeError> hasUnificationTooComplex(const ErrorVec& errors)
{
auto isUnificationTooComplex = [](const TypeError& te) {
return nullptr != get<UnificationTooComplex>(te);
};
auto it = std::find_if(errors.begin(), errors.end(), isUnificationTooComplex);
if (it == errors.end())
return std::nullopt;
else
return *it;
}
2023-05-05 20:57:12 +01:00
std::optional<TypeError> hasCountMismatch(const ErrorVec& errors)
{
auto isCountMismatch = [](const TypeError& te) {
return nullptr != get<CountMismatch>(te);
};
auto it = std::find_if(errors.begin(), errors.end(), isCountMismatch);
if (it == errors.end())
return std::nullopt;
else
return *it;
}
// Used for tagged union matching heuristic, returns first singleton type field
2023-01-03 17:33:19 +00:00
static std::optional<std::pair<Luau::Name, const SingletonType*>> getTableMatchTag(TypeId type)
{
2022-02-24 23:15:41 +00:00
if (auto ttv = getTableType(type))
{
2022-02-24 23:15:41 +00:00
for (auto&& [name, prop] : ttv->props)
2022-02-03 23:09:37 +00:00
{
2023-04-28 12:55:55 +01:00
if (auto sing = get<SingletonType>(follow(prop.type())))
2022-02-24 23:15:41 +00:00
return {{name, sing}};
2022-02-03 23:09:37 +00:00
}
}
return std::nullopt;
}
2022-09-29 23:11:54 +01:00
template<typename TY_A, typename TY_B>
static bool subsumes(bool useScopes, TY_A* left, TY_B* right)
{
if (useScopes)
return subsumes(left->scope, right->scope);
else
return left->level.subsumes(right->level);
}
2022-12-02 10:46:05 +00:00
TypeMismatch::Context Unifier::mismatchContext()
{
switch (variance)
{
case Covariant:
return TypeMismatch::CovariantContext;
case Invariant:
return TypeMismatch::InvariantContext;
default:
LUAU_ASSERT(false); // This codepath should be unreachable.
return TypeMismatch::CovariantContext;
}
}
2023-05-25 21:46:51 +01:00
Unifier::Unifier(NotNull<Normalizer> normalizer, NotNull<Scope> scope, const Location& location, Variance variance, TxnLog* parentLog)
2022-10-07 00:55:58 +01:00
: types(normalizer->arena)
2023-01-03 17:33:19 +00:00
, builtinTypes(normalizer->builtinTypes)
2022-10-07 00:55:58 +01:00
, normalizer(normalizer)
2022-08-18 22:04:33 +01:00
, scope(scope)
2022-01-06 22:10:07 +00:00
, log(parentLog)
, location(location)
, variance(variance)
2022-10-07 00:55:58 +01:00
, sharedState(*normalizer->sharedState)
{
LUAU_ASSERT(sharedState.iceHandler);
}
2023-07-28 12:37:00 +01:00
void Unifier::tryUnify(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection, const LiteralProperties* literalProperties)
{
2021-12-02 23:20:08 +00:00
sharedState.counters.iterationCount = 0;
2023-07-28 12:37:00 +01:00
tryUnify_(subTy, superTy, isFunctionCall, isIntersection, literalProperties);
}
2023-03-24 17:34:14 +00:00
static bool isBlocked(const TxnLog& log, TypeId ty)
{
ty = log.follow(ty);
return get<BlockedType>(ty) || get<PendingExpansionType>(ty);
}
2023-05-25 21:46:51 +01:00
static bool isBlocked(const TxnLog& log, TypePackId tp)
{
tp = log.follow(tp);
return get<BlockedTypePack>(tp);
}
2023-07-28 12:37:00 +01:00
void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection, const LiteralProperties* literalProperties)
{
2022-10-21 18:33:43 +01:00
RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
2021-12-02 23:20:08 +00:00
++sharedState.counters.iterationCount;
2022-10-21 18:33:43 +01:00
if (sharedState.counters.iterationLimit > 0 && sharedState.counters.iterationLimit < sharedState.counters.iterationCount)
2022-04-14 22:57:15 +01:00
{
2022-11-04 17:02:37 +00:00
reportError(location, UnificationTooComplex{});
2022-10-21 18:33:43 +01:00
return;
}
2022-03-11 16:31:18 +00:00
superTy = log.follow(superTy);
subTy = log.follow(subTy);
if (superTy == subTy)
return;
2023-07-28 12:37:00 +01:00
if (isBlocked(log, subTy) && isBlocked(log, superTy))
{
blockedTypes.push_back(subTy);
blockedTypes.push_back(superTy);
}
else if (isBlocked(log, subTy))
blockedTypes.push_back(subTy);
else if (isBlocked(log, superTy))
blockedTypes.push_back(superTy);
2023-05-12 13:15:01 +01:00
if (log.get<TypeFamilyInstanceType>(superTy))
{
// We do not report errors from reducing here. This is because we will
// "double-report" errors in some cases, like when trying to unify
// identical type family instantiations like Add<false, false> with
// Add<false, false>.
2023-05-19 19:59:59 +01:00
reduceFamilies(superTy, location, NotNull(types), builtinTypes, scope, normalizer, &log);
2023-05-12 13:15:01 +01:00
superTy = log.follow(superTy);
}
if (log.get<TypeFamilyInstanceType>(subTy))
{
2023-05-19 19:59:59 +01:00
reduceFamilies(subTy, location, NotNull(types), builtinTypes, scope, normalizer, &log);
2023-05-12 13:15:01 +01:00
subTy = log.follow(subTy);
}
// If we can't reduce the families down and we still have type family types
// here, we are stuck. Nothing meaningful can be done here. We don't wish to
// report an error, either.
if (log.get<TypeFamilyInstanceType>(superTy) || log.get<TypeFamilyInstanceType>(subTy))
{
return;
}
2023-01-03 17:33:19 +00:00
auto superFree = log.getMutable<FreeType>(superTy);
auto subFree = log.getMutable<FreeType>(subTy);
2023-07-28 12:37:00 +01:00
if (superFree && subFree && subsumes(useNewSolver, superFree, subFree))
{
2023-04-14 13:05:27 +01:00
if (!occursCheck(subTy, superTy, /* reversed = */ false))
2023-01-03 17:33:19 +00:00
log.replace(subTy, BoundType(superTy));
return;
}
2022-01-06 22:10:07 +00:00
else if (superFree && subFree)
{
2023-04-14 13:05:27 +01:00
if (!occursCheck(superTy, subTy, /* reversed = */ true))
2022-01-06 22:10:07 +00:00
{
2023-07-28 12:37:00 +01:00
if (subsumes(useNewSolver, superFree, subFree))
2022-01-06 22:10:07 +00:00
{
2022-03-11 16:31:18 +00:00
log.changeLevel(subTy, superFree->level);
2022-01-06 22:10:07 +00:00
}
2022-03-11 16:31:18 +00:00
2023-01-03 17:33:19 +00:00
log.replace(superTy, BoundType(subTy));
2021-11-18 22:21:07 +00:00
}
return;
}
2022-01-06 22:10:07 +00:00
else if (superFree)
{
// Unification can't change the level of a generic.
2023-01-03 17:33:19 +00:00
auto subGeneric = log.getMutable<GenericType>(subTy);
2023-07-28 12:37:00 +01:00
if (subGeneric && !subsumes(useNewSolver, subGeneric, superFree))
{
// TODO: a more informative error message? CLI-39912
2022-11-04 17:02:37 +00:00
reportError(location, GenericError{"Generic subtype escaping scope"});
return;
}
2023-04-14 13:05:27 +01:00
if (!occursCheck(superTy, subTy, /* reversed = */ true))
{
2023-07-28 12:37:00 +01:00
promoteTypeLevels(log, types, superFree->level, superFree->scope, useNewSolver, subTy);
2022-05-26 21:33:48 +01:00
2023-01-03 17:33:19 +00:00
Widen widen{types, builtinTypes};
log.replace(superTy, BoundType(widen(subTy)));
}
2021-11-18 22:21:07 +00:00
return;
}
2022-01-06 22:10:07 +00:00
else if (subFree)
{
2023-01-27 21:28:45 +00:00
// Normally, if the subtype is free, it should not be bound to any, unknown, or error types.
// But for bug compatibility, we'll only apply this rule to unknown. Doing this will silence cascading type errors.
if (log.get<UnknownType>(superTy))
return;
2022-07-08 02:05:31 +01:00
// Unification can't change the level of a generic.
2023-01-03 17:33:19 +00:00
auto superGeneric = log.getMutable<GenericType>(superTy);
2023-07-28 12:37:00 +01:00
if (superGeneric && !subsumes(useNewSolver, superGeneric, subFree))
{
// TODO: a more informative error message? CLI-39912
2022-11-04 17:02:37 +00:00
reportError(location, GenericError{"Generic supertype escaping scope"});
return;
}
2023-04-14 13:05:27 +01:00
if (!occursCheck(subTy, superTy, /* reversed = */ false))
{
2023-07-28 12:37:00 +01:00
promoteTypeLevels(log, types, subFree->level, subFree->scope, useNewSolver, superTy);
2023-01-03 17:33:19 +00:00
log.replace(subTy, BoundType(superTy));
}
return;
}
2023-05-12 13:15:01 +01:00
if (hideousFixMeGenericsAreActuallyFree)
{
auto superGeneric = log.getMutable<GenericType>(superTy);
auto subGeneric = log.getMutable<GenericType>(subTy);
2023-07-28 12:37:00 +01:00
if (superGeneric && subGeneric && subsumes(useNewSolver, superGeneric, subGeneric))
2023-05-12 13:15:01 +01:00
{
if (!occursCheck(subTy, superTy, /* reversed = */ false))
log.replace(subTy, BoundType(superTy));
return;
}
else if (superGeneric && subGeneric)
{
if (!occursCheck(superTy, subTy, /* reversed = */ true))
log.replace(superTy, BoundType(subTy));
return;
}
else if (superGeneric)
{
if (!occursCheck(superTy, subTy, /* reversed = */ true))
{
Widen widen{types, builtinTypes};
log.replace(superTy, BoundType(widen(subTy)));
}
return;
}
else if (subGeneric)
{
// Normally, if the subtype is free, it should not be bound to any, unknown, or error types.
// But for bug compatibility, we'll only apply this rule to unknown. Doing this will silence cascading type errors.
if (log.get<UnknownType>(superTy))
return;
if (!occursCheck(subTy, superTy, /* reversed = */ false))
log.replace(subTy, BoundType(superTy));
return;
}
}
2023-02-17 14:53:37 +00:00
if (log.get<AnyType>(superTy))
return tryUnifyWithAny(subTy, builtinTypes->anyType);
2023-01-20 12:02:39 +00:00
2023-03-17 14:59:30 +00:00
if (!FFlag::LuauTransitiveSubtyping && log.get<ErrorType>(superTy))
2023-02-17 14:53:37 +00:00
return tryUnifyWithAny(subTy, builtinTypes->errorType);
2023-03-17 14:59:30 +00:00
if (!FFlag::LuauTransitiveSubtyping && log.get<UnknownType>(superTy))
2023-02-17 14:53:37 +00:00
return tryUnifyWithAny(subTy, builtinTypes->unknownType);
2022-04-14 22:57:15 +01:00
2023-02-17 14:53:37 +00:00
if (log.get<AnyType>(subTy))
2023-03-17 14:59:30 +00:00
{
if (FFlag::LuauTransitiveSubtyping && normalize)
{
// TODO: there are probably cheaper ways to check if any <: T.
const NormalizedType* superNorm = normalizer->normalize(superTy);
2023-09-01 17:38:53 +01:00
if (!superNorm)
2023-09-22 19:10:49 +01:00
return reportError(location, NormalizationTooComplex{});
2023-09-01 17:38:53 +01:00
2023-03-17 14:59:30 +00:00
if (!log.get<AnyType>(superNorm->tops))
failure = true;
}
else
failure = true;
2023-02-17 14:53:37 +00:00
return tryUnifyWithAny(superTy, builtinTypes->anyType);
2023-03-17 14:59:30 +00:00
}
2023-03-17 14:59:30 +00:00
if (!FFlag::LuauTransitiveSubtyping && log.get<ErrorType>(subTy))
2023-02-17 14:53:37 +00:00
return tryUnifyWithAny(superTy, builtinTypes->errorType);
2023-01-20 12:02:39 +00:00
2023-02-17 14:53:37 +00:00
if (log.get<NeverType>(subTy))
return tryUnifyWithAny(superTy, builtinTypes->neverType);
2022-07-08 02:05:31 +01:00
auto& cache = sharedState.cachedUnify;
// What if the types are immutable and we proved their relation before
2022-04-21 22:04:22 +01:00
bool cacheEnabled = !isFunctionCall && !isIntersection && variance == Invariant;
2022-03-24 21:49:08 +00:00
2022-04-21 22:04:22 +01:00
if (cacheEnabled)
2022-03-24 21:49:08 +00:00
{
2022-04-21 22:04:22 +01:00
if (cache.contains({subTy, superTy}))
return;
2022-03-24 21:49:08 +00:00
2022-04-21 22:04:22 +01:00
if (auto error = sharedState.cachedUnifyError.find({subTy, superTy}))
{
2022-11-04 17:02:37 +00:00
reportError(location, *error);
2022-03-24 21:49:08 +00:00
return;
2022-04-21 22:04:22 +01:00
}
2022-03-24 21:49:08 +00:00
}
// If we have seen this pair of types before, we are currently recursing into cyclic types.
// Here, we assume that the types unify. If they do not, we will find out as we roll back
// the stack.
2022-03-11 16:31:18 +00:00
if (log.haveSeen(superTy, subTy))
return;
2022-03-11 16:31:18 +00:00
log.pushSeen(superTy, subTy);
2022-03-24 21:49:08 +00:00
size_t errorCount = errors.size();
2023-07-28 12:37:00 +01:00
if (const UnionType* subUnion = log.getMutable<UnionType>(subTy))
{
2022-09-02 00:00:14 +01:00
tryUnifyUnionWithType(subTy, subUnion, superTy);
2022-02-03 23:09:37 +00:00
}
2023-01-03 17:33:19 +00:00
else if (const IntersectionType* uv = log.getMutable<IntersectionType>(superTy))
2022-02-03 23:09:37 +00:00
{
tryUnifyTypeWithIntersection(subTy, superTy, uv);
}
2023-01-03 17:33:19 +00:00
else if (const UnionType* uv = log.getMutable<UnionType>(superTy))
2022-10-07 00:55:58 +01:00
{
tryUnifyTypeWithUnion(subTy, superTy, uv, cacheEnabled, isFunctionCall);
}
2023-01-03 17:33:19 +00:00
else if (const IntersectionType* uv = log.getMutable<IntersectionType>(subTy))
2022-02-03 23:09:37 +00:00
{
tryUnifyIntersectionWithType(subTy, uv, superTy, cacheEnabled, isFunctionCall);
}
2023-03-17 14:59:30 +00:00
else if (FFlag::LuauTransitiveSubtyping && log.get<AnyType>(subTy))
{
tryUnifyWithAny(superTy, builtinTypes->unknownType);
failure = true;
}
else if (FFlag::LuauTransitiveSubtyping && log.get<ErrorType>(subTy) && log.get<ErrorType>(superTy))
{
// error <: error
}
else if (FFlag::LuauTransitiveSubtyping && log.get<ErrorType>(superTy))
{
tryUnifyWithAny(subTy, builtinTypes->errorType);
failure = true;
}
else if (FFlag::LuauTransitiveSubtyping && log.get<ErrorType>(subTy))
{
tryUnifyWithAny(superTy, builtinTypes->errorType);
failure = true;
}
else if (FFlag::LuauTransitiveSubtyping && log.get<UnknownType>(superTy))
{
// At this point, all the supertypes of `error` have been handled,
// and if `error </: T` then `T <: unknown`.
tryUnifyWithAny(subTy, builtinTypes->unknownType);
}
else if (FFlag::LuauTransitiveSubtyping && log.get<UnknownType>(superTy))
{
tryUnifyWithAny(subTy, builtinTypes->unknownType);
}
2023-01-03 17:33:19 +00:00
else if (log.getMutable<PrimitiveType>(superTy) && log.getMutable<PrimitiveType>(subTy))
2022-02-03 23:09:37 +00:00
tryUnifyPrimitives(subTy, superTy);
2023-01-03 17:33:19 +00:00
else if ((log.getMutable<PrimitiveType>(superTy) || log.getMutable<SingletonType>(superTy)) && log.getMutable<SingletonType>(subTy))
2022-02-03 23:09:37 +00:00
tryUnifySingletons(subTy, superTy);
2023-03-31 13:21:14 +01:00
else if (auto ptv = get<PrimitiveType>(superTy); ptv && ptv->type == PrimitiveType::Function && get<FunctionType>(subTy))
2022-11-04 17:02:37 +00:00
{
// Ok. Do nothing. forall functions F, F <: function
}
2023-05-05 20:57:12 +01:00
else if (isPrim(superTy, PrimitiveType::Table) && (get<TableType>(subTy) || get<MetatableType>(subTy)))
2023-01-27 21:28:45 +00:00
{
// Ok, do nothing: forall tables T, T <: table
}
2023-01-03 17:33:19 +00:00
else if (log.getMutable<FunctionType>(superTy) && log.getMutable<FunctionType>(subTy))
2022-02-03 23:09:37 +00:00
tryUnifyFunctions(subTy, superTy, isFunctionCall);
2023-03-03 13:45:38 +00:00
else if (auto table = log.get<PrimitiveType>(superTy); table && table->type == PrimitiveType::Table)
tryUnify(subTy, builtinTypes->emptyTableType, isFunctionCall, isIntersection);
else if (auto table = log.get<PrimitiveType>(subTy); table && table->type == PrimitiveType::Table)
tryUnify(builtinTypes->emptyTableType, superTy, isFunctionCall, isIntersection);
2023-01-03 17:33:19 +00:00
else if (log.getMutable<TableType>(superTy) && log.getMutable<TableType>(subTy))
2022-02-03 23:09:37 +00:00
{
2023-07-28 12:37:00 +01:00
tryUnifyTables(subTy, superTy, isIntersection, literalProperties);
2022-02-03 23:09:37 +00:00
}
2023-01-27 21:28:45 +00:00
else if (log.get<TableType>(superTy) && (log.get<PrimitiveType>(subTy) || log.get<SingletonType>(subTy)))
2022-07-14 23:39:35 +01:00
{
tryUnifyScalarShape(subTy, superTy, /*reversed*/ false);
}
2023-01-27 21:28:45 +00:00
else if (log.get<TableType>(subTy) && (log.get<PrimitiveType>(superTy) || log.get<SingletonType>(superTy)))
2022-07-14 23:39:35 +01:00
{
tryUnifyScalarShape(subTy, superTy, /*reversed*/ true);
}
2022-02-03 23:09:37 +00:00
2023-01-03 17:33:19 +00:00
// tryUnifyWithMetatable assumes its first argument is a MetatableType. The check is otherwise symmetrical.
else if (log.getMutable<MetatableType>(superTy))
2022-02-03 23:09:37 +00:00
tryUnifyWithMetatable(subTy, superTy, /*reversed*/ false);
2023-01-03 17:33:19 +00:00
else if (log.getMutable<MetatableType>(subTy))
2022-02-03 23:09:37 +00:00
tryUnifyWithMetatable(superTy, subTy, /*reversed*/ true);
2023-01-03 17:33:19 +00:00
else if (log.getMutable<ClassType>(superTy))
2022-02-03 23:09:37 +00:00
tryUnifyWithClass(subTy, superTy, /*reversed*/ false);
// Unification of nonclasses with classes is almost, but not quite symmetrical.
// The order in which we perform this test is significant in the case that both types are classes.
2023-01-03 17:33:19 +00:00
else if (log.getMutable<ClassType>(subTy))
2022-02-03 23:09:37 +00:00
tryUnifyWithClass(subTy, superTy, /*reversed*/ true);
2023-02-03 12:34:12 +00:00
else if (log.get<NegationType>(superTy) || log.get<NegationType>(subTy))
tryUnifyNegations(subTy, superTy);
2022-10-27 23:22:49 +01:00
2023-06-24 06:33:44 +01:00
else if (checkInhabited && !normalizer->isInhabited(subTy))
2023-01-03 17:33:19 +00:00
{
}
2022-02-03 23:09:37 +00:00
else
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, mismatchContext()});
2022-04-21 22:04:22 +01:00
if (cacheEnabled)
2022-03-24 21:49:08 +00:00
cacheResult(subTy, superTy, errorCount);
2022-03-11 16:31:18 +00:00
log.popSeen(superTy, subTy);
2022-02-03 23:09:37 +00:00
}
2023-01-03 17:33:19 +00:00
void Unifier::tryUnifyUnionWithType(TypeId subTy, const UnionType* subUnion, TypeId superTy)
2022-02-03 23:09:37 +00:00
{
2022-10-07 00:55:58 +01:00
// A | B <: T if and only if A <: T and B <: T
2022-02-03 23:09:37 +00:00
bool failed = false;
2023-03-17 14:59:30 +00:00
bool errorsSuppressed = true;
2022-02-03 23:09:37 +00:00
std::optional<TypeError> unificationTooComplex;
std::optional<TypeError> firstFailedOption;
2022-12-02 10:46:05 +00:00
std::vector<TxnLog> logs;
2022-09-02 00:00:14 +01:00
for (TypeId type : subUnion->options)
2022-02-03 23:09:37 +00:00
{
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(type, superTy);
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2022-12-02 10:46:05 +00:00
logs.push_back(std::move(innerState.log));
2022-02-03 23:09:37 +00:00
if (auto e = hasUnificationTooComplex(innerState.errors))
unificationTooComplex = e;
2023-03-17 14:59:30 +00:00
else if (FFlag::LuauTransitiveSubtyping ? innerState.failure : !innerState.errors.empty())
{
2023-03-17 14:59:30 +00:00
// If errors were suppressed, we store the log up, so we can commit it if no other option succeeds.
if (FFlag::LuauTransitiveSubtyping && innerState.errors.empty())
logs.push_back(std::move(innerState.log));
2022-02-03 23:09:37 +00:00
// 'nil' option is skipped from extended report because we present the type in a special way - 'T?'
2023-03-17 14:59:30 +00:00
else if (!firstFailedOption && !isNil(type))
2022-02-03 23:09:37 +00:00
firstFailedOption = {innerState.errors.front()};
2022-02-03 23:09:37 +00:00
failed = true;
2023-03-17 14:59:30 +00:00
errorsSuppressed &= innerState.errors.empty();
2022-02-03 23:09:37 +00:00
}
2022-03-04 16:19:20 +00:00
}
2023-06-02 19:17:31 +01:00
if (FFlag::LuauAlwaysCommitInferencesOfFunctionCalls)
2022-12-02 10:46:05 +00:00
log.concatAsUnion(combineLogsIntoUnion(std::move(logs)), NotNull{types});
else
{
// even if A | B <: T fails, we want to bind some options of T with A | B iff A | B was a subtype of that option.
auto tryBind = [this, subTy](TypeId superOption) {
superOption = log.follow(superOption);
2022-05-06 00:52:48 +01:00
2022-12-02 10:46:05 +00:00
// just skip if the superOption is not free-ish.
2023-01-03 17:33:19 +00:00
auto ttv = log.getMutable<TableType>(superOption);
if (!log.is<FreeType>(superOption) && (!ttv || ttv->state != TableState::Free))
2022-05-26 21:33:48 +01:00
return;
2022-12-02 10:46:05 +00:00
// If superOption is already present in subTy, do nothing. Nothing new has been learned, but the subtype
// test is successful.
2023-01-03 17:33:19 +00:00
if (auto subUnion = get<UnionType>(subTy))
2022-12-02 10:46:05 +00:00
{
if (end(subUnion) != std::find(begin(subUnion), end(subUnion), superOption))
return;
}
// Since we have already checked if S <: T, checking it again will not queue up the type for replacement.
// So we'll have to do it ourselves. We assume they unified cleanly if they are still in the seen set.
if (log.haveSeen(subTy, superOption))
{
// TODO: would it be nice for TxnLog::replace to do this?
2023-01-03 17:33:19 +00:00
if (log.is<TableType>(superOption))
2022-12-02 10:46:05 +00:00
log.bindTable(superOption, subTy);
else
log.replace(superOption, *subTy);
}
};
2023-01-03 17:33:19 +00:00
if (auto superUnion = log.getMutable<UnionType>(superTy))
2022-03-04 16:19:20 +00:00
{
2022-12-02 10:46:05 +00:00
for (TypeId ty : superUnion)
tryBind(ty);
2022-03-04 16:19:20 +00:00
}
2022-12-02 10:46:05 +00:00
else
tryBind(superTy);
}
2022-02-03 23:09:37 +00:00
if (unificationTooComplex)
reportError(*unificationTooComplex);
else if (failed)
{
2022-02-03 23:09:37 +00:00
if (firstFailedOption)
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "Not all union options are compatible.", *firstFailedOption, mismatchContext()});
2023-03-17 14:59:30 +00:00
else if (!FFlag::LuauTransitiveSubtyping || !errorsSuppressed)
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, mismatchContext()});
2023-03-17 14:59:30 +00:00
failure = true;
2022-02-03 23:09:37 +00:00
}
}
2023-01-03 17:33:19 +00:00
void Unifier::tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionType* uv, bool cacheEnabled, bool isFunctionCall)
2022-02-03 23:09:37 +00:00
{
// T <: A | B if T <: A or T <: B
bool found = false;
2023-03-17 14:59:30 +00:00
bool errorsSuppressed = false;
2022-02-03 23:09:37 +00:00
std::optional<TypeError> unificationTooComplex;
2022-02-03 23:09:37 +00:00
size_t failedOptionCount = 0;
std::optional<TypeError> failedOption;
2022-02-03 23:09:37 +00:00
bool foundHeuristic = false;
size_t startIndex = 0;
2022-02-03 23:09:37 +00:00
if (const std::string* subName = getName(subTy))
{
for (size_t i = 0; i < uv->options.size(); ++i)
2022-01-27 21:29:34 +00:00
{
2022-02-03 23:09:37 +00:00
const std::string* optionName = getName(uv->options[i]);
if (optionName && *optionName == *subName)
{
2022-02-03 23:09:37 +00:00
foundHeuristic = true;
startIndex = i;
break;
}
2022-01-27 21:29:34 +00:00
}
2022-02-03 23:09:37 +00:00
}
2022-02-03 23:09:37 +00:00
if (auto subMatchTag = getTableMatchTag(subTy))
{
for (size_t i = 0; i < uv->options.size(); ++i)
2022-01-27 21:29:34 +00:00
{
2022-02-03 23:09:37 +00:00
auto optionMatchTag = getTableMatchTag(uv->options[i]);
if (optionMatchTag && optionMatchTag->first == subMatchTag->first && *optionMatchTag->second == *subMatchTag->second)
{
2022-02-03 23:09:37 +00:00
foundHeuristic = true;
startIndex = i;
break;
}
}
2022-02-03 23:09:37 +00:00
}
2023-03-17 14:59:30 +00:00
if (FFlag::LuauTransitiveSubtyping && !foundHeuristic)
{
for (size_t i = 0; i < uv->options.size(); ++i)
{
TypeId type = uv->options[i];
if (subTy == type)
{
foundHeuristic = true;
startIndex = i;
break;
}
}
}
2022-02-03 23:09:37 +00:00
if (!foundHeuristic && cacheEnabled)
{
auto& cache = sharedState.cachedUnify;
for (size_t i = 0; i < uv->options.size(); ++i)
{
2022-02-03 23:09:37 +00:00
TypeId type = uv->options[i];
2022-04-21 22:04:22 +01:00
if (cache.contains({subTy, type}))
2022-03-24 21:49:08 +00:00
{
2022-04-21 22:04:22 +01:00
startIndex = i;
break;
}
2022-02-03 23:09:37 +00:00
}
}
2022-12-02 10:46:05 +00:00
std::vector<TxnLog> logs;
2022-02-03 23:09:37 +00:00
for (size_t i = 0; i < uv->options.size(); ++i)
{
TypeId type = uv->options[(i + startIndex) % uv->options.size()];
Unifier innerState = makeChildUnifier();
2022-10-07 00:55:58 +01:00
innerState.normalize = false;
2022-02-03 23:09:37 +00:00
innerState.tryUnify_(subTy, type, isFunctionCall);
2023-03-17 14:59:30 +00:00
if (FFlag::LuauTransitiveSubtyping ? !innerState.failure : innerState.errors.empty())
2022-02-03 23:09:37 +00:00
{
found = true;
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2022-12-02 10:46:05 +00:00
logs.push_back(std::move(innerState.log));
else
{
log.concat(std::move(innerState.log));
break;
}
2022-02-03 23:09:37 +00:00
}
2023-03-17 14:59:30 +00:00
else if (FFlag::LuauTransitiveSubtyping && innerState.errors.empty())
{
errorsSuppressed = true;
}
2022-02-03 23:09:37 +00:00
else if (auto e = hasUnificationTooComplex(innerState.errors))
{
2022-02-03 23:09:37 +00:00
unificationTooComplex = e;
}
2022-02-03 23:09:37 +00:00
else if (!isNil(type))
2021-11-12 02:12:39 +00:00
{
2022-02-03 23:09:37 +00:00
failedOptionCount++;
if (!failedOption)
failedOption = {innerState.errors.front()};
2021-11-12 02:12:39 +00:00
}
}
2022-02-03 23:09:37 +00:00
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2022-12-02 10:46:05 +00:00
log.concatAsUnion(combineLogsIntoUnion(std::move(logs)), NotNull{types});
2022-02-03 23:09:37 +00:00
if (unificationTooComplex)
{
reportError(*unificationTooComplex);
}
2023-03-17 14:59:30 +00:00
else if (FFlag::LuauTransitiveSubtyping && !found && normalize)
{
// It is possible that T <: A | B even though T </: A and T </:B
// for example boolean <: true | false.
// We deal with this by type normalization.
const NormalizedType* subNorm = normalizer->normalize(subTy);
const NormalizedType* superNorm = normalizer->normalize(superTy);
Unifier innerState = makeChildUnifier();
if (!subNorm || !superNorm)
2023-09-22 19:10:49 +01:00
return reportError(location, NormalizationTooComplex{});
2023-03-17 14:59:30 +00:00
else if ((failedOptionCount == 1 || foundHeuristic) && failedOption)
2023-03-24 17:34:14 +00:00
innerState.tryUnifyNormalizedTypes(
subTy, superTy, *subNorm, *superNorm, "None of the union options are compatible. For example:", *failedOption);
2023-03-17 14:59:30 +00:00
else
innerState.tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "none of the union options are compatible");
if (!innerState.failure)
log.concat(std::move(innerState.log));
else if (errorsSuppressed || innerState.errors.empty())
failure = true;
else
reportError(std::move(innerState.errors.front()));
}
2022-10-07 00:55:58 +01:00
else if (!found && normalize)
{
// It is possible that T <: A | B even though T </: A and T </:B
// for example boolean <: true | false.
// We deal with this by type normalization.
const NormalizedType* subNorm = normalizer->normalize(subTy);
const NormalizedType* superNorm = normalizer->normalize(superTy);
if (!subNorm || !superNorm)
2023-09-22 19:10:49 +01:00
reportError(location, NormalizationTooComplex{});
2022-10-07 00:55:58 +01:00
else if ((failedOptionCount == 1 || foundHeuristic) && failedOption)
tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "None of the union options are compatible. For example:", *failedOption);
else
tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "none of the union options are compatible");
}
2022-02-03 23:09:37 +00:00
else if (!found)
{
2023-03-17 14:59:30 +00:00
if (FFlag::LuauTransitiveSubtyping && errorsSuppressed)
failure = true;
else if ((failedOptionCount == 1 || foundHeuristic) && failedOption)
2022-12-02 10:46:05 +00:00
reportError(
location, TypeMismatch{superTy, subTy, "None of the union options are compatible. For example:", *failedOption, mismatchContext()});
2022-02-03 23:09:37 +00:00
else
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "none of the union options are compatible", mismatchContext()});
2022-02-03 23:09:37 +00:00
}
}
2023-01-03 17:33:19 +00:00
void Unifier::tryUnifyTypeWithIntersection(TypeId subTy, TypeId superTy, const IntersectionType* uv)
2022-02-03 23:09:37 +00:00
{
std::optional<TypeError> unificationTooComplex;
std::optional<TypeError> firstFailedOption;
2021-11-12 02:12:39 +00:00
2022-12-02 10:46:05 +00:00
std::vector<TxnLog> logs;
2022-10-07 00:55:58 +01:00
// T <: A & B if and only if T <: A and T <: B
2022-02-03 23:09:37 +00:00
for (TypeId type : uv->parts)
{
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(subTy, type, /*isFunctionCall*/ false, /*isIntersection*/ true);
2021-11-12 02:12:39 +00:00
2022-02-03 23:09:37 +00:00
if (auto e = hasUnificationTooComplex(innerState.errors))
unificationTooComplex = e;
else if (!innerState.errors.empty())
{
if (!firstFailedOption)
firstFailedOption = {innerState.errors.front()};
}
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2022-12-02 10:46:05 +00:00
logs.push_back(std::move(innerState.log));
else
log.concat(std::move(innerState.log));
2023-03-17 14:59:30 +00:00
failure |= innerState.failure;
}
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2022-12-02 10:46:05 +00:00
log.concat(combineLogsIntoIntersection(std::move(logs)));
2022-02-03 23:09:37 +00:00
if (unificationTooComplex)
reportError(*unificationTooComplex);
else if (firstFailedOption)
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "Not all intersection parts are compatible.", *firstFailedOption, mismatchContext()});
2022-02-03 23:09:37 +00:00
}
2023-02-03 12:34:12 +00:00
struct NegationTypeFinder : TypeOnceVisitor
{
bool found = false;
bool visit(TypeId ty) override
{
return !found;
}
bool visit(TypeId ty, const NegationType&) override
{
found = true;
return !found;
}
};
2023-01-03 17:33:19 +00:00
void Unifier::tryUnifyIntersectionWithType(TypeId subTy, const IntersectionType* uv, TypeId superTy, bool cacheEnabled, bool isFunctionCall)
2022-02-03 23:09:37 +00:00
{
2022-07-01 00:29:02 +01:00
// A & B <: T if A <: T or B <: T
2022-02-03 23:09:37 +00:00
bool found = false;
2023-03-17 14:59:30 +00:00
bool errorsSuppressed = false;
2022-02-03 23:09:37 +00:00
std::optional<TypeError> unificationTooComplex;
2022-02-03 23:09:37 +00:00
size_t startIndex = 0;
if (cacheEnabled)
{
auto& cache = sharedState.cachedUnify;
for (size_t i = 0; i < uv->parts.size(); ++i)
{
2022-02-03 23:09:37 +00:00
TypeId type = uv->parts[i];
2022-04-21 22:04:22 +01:00
if (cache.contains({type, superTy}))
2022-03-24 21:49:08 +00:00
{
2022-04-21 22:04:22 +01:00
startIndex = i;
break;
}
}
2022-02-03 23:09:37 +00:00
}
2023-07-28 12:37:00 +01:00
if (useNewSolver && normalize)
2023-02-03 12:34:12 +00:00
{
// Sometimes a negation type is inside one of the types, e.g. { p: number } & { p: ~number }.
NegationTypeFinder finder;
finder.traverse(subTy);
if (finder.found)
{
// It is possible that A & B <: T even though A </: T and B </: T
// for example (string?) & ~nil <: string.
// We deal with this by type normalization.
const NormalizedType* subNorm = normalizer->normalize(subTy);
const NormalizedType* superNorm = normalizer->normalize(superTy);
if (subNorm && superNorm)
tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "none of the intersection parts are compatible");
else
2023-09-22 19:10:49 +01:00
reportError(location, NormalizationTooComplex{});
2023-02-03 12:34:12 +00:00
return;
}
}
2022-12-02 10:46:05 +00:00
std::vector<TxnLog> logs;
2022-02-03 23:09:37 +00:00
for (size_t i = 0; i < uv->parts.size(); ++i)
{
TypeId type = uv->parts[(i + startIndex) % uv->parts.size()];
Unifier innerState = makeChildUnifier();
2022-10-07 00:55:58 +01:00
innerState.normalize = false;
2022-02-03 23:09:37 +00:00
innerState.tryUnify_(type, superTy, isFunctionCall);
2023-03-17 14:59:30 +00:00
// TODO: This sets errorSuppressed to true if any of the parts is error-suppressing,
// in paricular any & T is error-suppressing. Really, errorSuppressed should be true if
// all of the parts are error-suppressing, but that fails to typecheck lua-apps.
2022-02-03 23:09:37 +00:00
if (innerState.errors.empty())
2021-11-12 02:12:39 +00:00
{
2022-02-03 23:09:37 +00:00
found = true;
2023-03-17 14:59:30 +00:00
errorsSuppressed = innerState.failure;
2023-07-28 12:37:00 +01:00
if (useNewSolver || (FFlag::LuauTransitiveSubtyping && innerState.failure))
2022-12-02 10:46:05 +00:00
logs.push_back(std::move(innerState.log));
else
{
2023-03-17 14:59:30 +00:00
errorsSuppressed = false;
2022-12-02 10:46:05 +00:00
log.concat(std::move(innerState.log));
break;
}
2022-02-03 23:09:37 +00:00
}
else if (auto e = hasUnificationTooComplex(innerState.errors))
{
unificationTooComplex = e;
2021-11-12 02:12:39 +00:00
}
2022-02-03 23:09:37 +00:00
}
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2022-12-02 10:46:05 +00:00
log.concat(combineLogsIntoIntersection(std::move(logs)));
2023-03-17 14:59:30 +00:00
else if (FFlag::LuauTransitiveSubtyping && errorsSuppressed)
log.concat(std::move(logs.front()));
2022-12-02 10:46:05 +00:00
2022-02-03 23:09:37 +00:00
if (unificationTooComplex)
reportError(*unificationTooComplex);
2022-10-07 00:55:58 +01:00
else if (!found && normalize)
{
// It is possible that A & B <: T even though A </: T and B </: T
// for example string? & number? <: nil.
// We deal with this by type normalization.
const NormalizedType* subNorm = normalizer->normalize(subTy);
const NormalizedType* superNorm = normalizer->normalize(superTy);
if (subNorm && superNorm)
tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "none of the intersection parts are compatible");
else
2023-09-22 19:10:49 +01:00
reportError(location, NormalizationTooComplex{});
2022-10-07 00:55:58 +01:00
}
2022-02-03 23:09:37 +00:00
else if (!found)
{
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "none of the intersection parts are compatible", mismatchContext()});
}
2023-03-17 14:59:30 +00:00
else if (errorsSuppressed)
failure = true;
}
2022-10-13 23:59:53 +01:00
void Unifier::tryUnifyNormalizedTypes(
TypeId subTy, TypeId superTy, const NormalizedType& subNorm, const NormalizedType& superNorm, std::string reason, std::optional<TypeError> error)
2022-10-07 00:55:58 +01:00
{
2023-03-17 14:59:30 +00:00
if (!FFlag::LuauTransitiveSubtyping && get<UnknownType>(superNorm.tops))
2022-10-07 00:55:58 +01:00
return;
2023-03-17 14:59:30 +00:00
else if (get<AnyType>(superNorm.tops))
return;
else if (get<AnyType>(subNorm.tops))
{
failure = true;
return;
}
else if (!FFlag::LuauTransitiveSubtyping && get<UnknownType>(subNorm.tops))
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
2023-01-03 17:33:19 +00:00
if (get<ErrorType>(subNorm.errors))
if (!get<ErrorType>(superNorm.errors))
2023-03-17 14:59:30 +00:00
{
failure = true;
if (!FFlag::LuauTransitiveSubtyping)
reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
return;
}
if (FFlag::LuauTransitiveSubtyping && get<UnknownType>(superNorm.tops))
return;
if (FFlag::LuauTransitiveSubtyping && get<UnknownType>(subNorm.tops))
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
2023-01-03 17:33:19 +00:00
if (get<PrimitiveType>(subNorm.booleans))
2022-10-07 00:55:58 +01:00
{
2023-01-03 17:33:19 +00:00
if (!get<PrimitiveType>(superNorm.booleans))
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
}
2023-01-03 17:33:19 +00:00
else if (const SingletonType* stv = get<SingletonType>(subNorm.booleans))
2022-10-07 00:55:58 +01:00
{
2023-01-03 17:33:19 +00:00
if (!get<PrimitiveType>(superNorm.booleans) && stv != get<SingletonType>(superNorm.booleans))
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
}
2023-01-03 17:33:19 +00:00
if (get<PrimitiveType>(subNorm.nils))
if (!get<PrimitiveType>(superNorm.nils))
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
2023-01-03 17:33:19 +00:00
if (get<PrimitiveType>(subNorm.numbers))
if (!get<PrimitiveType>(superNorm.numbers))
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
2022-10-27 23:22:49 +01:00
if (!isSubtype(subNorm.strings, superNorm.strings))
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
2023-01-03 17:33:19 +00:00
if (get<PrimitiveType>(subNorm.threads))
if (!get<PrimitiveType>(superNorm.errors))
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
2023-05-05 20:57:12 +01:00
for (const auto& [subClass, _] : subNorm.classes.classes)
2022-10-07 00:55:58 +01:00
{
2023-05-05 20:57:12 +01:00
bool found = false;
const ClassType* subCtv = get<ClassType>(subClass);
LUAU_ASSERT(subCtv);
for (const auto& [superClass, superNegations] : superNorm.classes.classes)
2022-10-07 00:55:58 +01:00
{
2023-05-05 20:57:12 +01:00
const ClassType* superCtv = get<ClassType>(superClass);
LUAU_ASSERT(superCtv);
2023-01-03 17:33:19 +00:00
2023-05-05 20:57:12 +01:00
if (isSubclass(subCtv, superCtv))
2022-10-07 00:55:58 +01:00
{
2023-05-05 20:57:12 +01:00
found = true;
2023-01-03 17:33:19 +00:00
2023-05-05 20:57:12 +01:00
for (TypeId negation : superNegations)
2023-02-10 18:50:54 +00:00
{
2023-05-05 20:57:12 +01:00
const ClassType* negationCtv = get<ClassType>(negation);
LUAU_ASSERT(negationCtv);
2023-02-10 18:50:54 +00:00
2023-05-05 20:57:12 +01:00
if (isSubclass(subCtv, negationCtv))
2023-02-10 18:50:54 +00:00
{
2023-05-05 20:57:12 +01:00
found = false;
2023-02-10 18:50:54 +00:00
break;
}
}
2023-05-05 20:57:12 +01:00
if (found)
break;
2022-10-07 00:55:58 +01:00
}
}
2023-05-05 20:57:12 +01:00
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2023-01-03 17:33:19 +00:00
{
2023-05-05 20:57:12 +01:00
for (TypeId superTable : superNorm.tables)
2023-01-03 17:33:19 +00:00
{
2023-05-05 20:57:12 +01:00
Unifier innerState = makeChildUnifier();
innerState.tryUnify(subClass, superTable);
if (innerState.errors.empty())
2023-01-03 17:33:19 +00:00
{
found = true;
2023-05-05 20:57:12 +01:00
log.concat(std::move(innerState.log));
2023-01-03 17:33:19 +00:00
break;
}
2023-05-05 20:57:12 +01:00
else if (auto e = hasUnificationTooComplex(innerState.errors))
return reportError(*e);
2023-01-03 17:33:19 +00:00
}
2023-05-05 20:57:12 +01:00
}
if (!found)
{
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2023-01-03 17:33:19 +00:00
}
2022-10-07 00:55:58 +01:00
}
for (TypeId subTable : subNorm.tables)
{
bool found = false;
for (TypeId superTable : superNorm.tables)
{
2023-05-05 20:57:12 +01:00
if (isPrim(superTable, PrimitiveType::Table))
2023-01-27 21:28:45 +00:00
{
found = true;
break;
}
2022-10-07 00:55:58 +01:00
Unifier innerState = makeChildUnifier();
2023-03-10 19:20:04 +00:00
2023-03-31 13:21:14 +01:00
innerState.tryUnify(subTable, superTable);
2023-03-10 19:20:04 +00:00
2022-10-07 00:55:58 +01:00
if (innerState.errors.empty())
{
found = true;
log.concat(std::move(innerState.log));
break;
}
else if (auto e = hasUnificationTooComplex(innerState.errors))
return reportError(*e);
}
if (!found)
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
}
2022-11-04 17:02:37 +00:00
if (!subNorm.functions.isNever())
2022-10-07 00:55:58 +01:00
{
2022-11-04 17:02:37 +00:00
if (superNorm.functions.isNever())
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2023-03-31 13:21:14 +01:00
for (TypeId superFun : superNorm.functions.parts)
2022-10-07 00:55:58 +01:00
{
Unifier innerState = makeChildUnifier();
2023-01-03 17:33:19 +00:00
const FunctionType* superFtv = get<FunctionType>(superFun);
2022-10-07 00:55:58 +01:00
if (!superFtv)
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
TypePackId tgt = innerState.tryApplyOverloadedFunction(subTy, subNorm.functions, superFtv->argTypes);
innerState.tryUnify_(tgt, superFtv->retTypes);
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
else if (auto e = hasUnificationTooComplex(innerState.errors))
return reportError(*e);
else
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()});
2022-10-07 00:55:58 +01:00
}
}
for (auto& [tyvar, subIntersect] : subNorm.tyvars)
{
auto found = superNorm.tyvars.find(tyvar);
if (found == superNorm.tyvars.end())
tryUnifyNormalizedTypes(subTy, superTy, *subIntersect, superNorm, reason, error);
else
tryUnifyNormalizedTypes(subTy, superTy, *subIntersect, *found->second, reason, error);
if (!errors.empty())
return;
}
}
TypePackId Unifier::tryApplyOverloadedFunction(TypeId function, const NormalizedFunctionType& overloads, TypePackId args)
{
2022-11-04 17:02:37 +00:00
if (overloads.isNever())
2022-10-07 00:55:58 +01:00
{
2022-11-04 17:02:37 +00:00
reportError(location, CannotCallNonFunction{function});
2023-01-03 17:33:19 +00:00
return builtinTypes->errorRecoveryTypePack();
2022-10-07 00:55:58 +01:00
}
std::optional<TypePackId> result;
2023-01-03 17:33:19 +00:00
const FunctionType* firstFun = nullptr;
2023-03-31 13:21:14 +01:00
for (TypeId overload : overloads.parts)
2022-10-07 00:55:58 +01:00
{
2023-01-03 17:33:19 +00:00
if (const FunctionType* ftv = get<FunctionType>(overload))
2022-10-07 00:55:58 +01:00
{
// TODO: instantiate generics?
if (ftv->generics.empty() && ftv->genericPacks.empty())
{
if (!firstFun)
firstFun = ftv;
Unifier innerState = makeChildUnifier();
innerState.tryUnify_(args, ftv->argTypes);
if (innerState.errors.empty())
{
log.concat(std::move(innerState.log));
if (result)
{
2023-01-20 12:02:39 +00:00
innerState.log.clear();
innerState.tryUnify_(*result, ftv->retTypes);
if (innerState.errors.empty())
2022-10-21 18:33:43 +01:00
log.concat(std::move(innerState.log));
2022-10-07 00:55:58 +01:00
// Annoyingly, since we don't support intersection of generic type packs,
// the intersection may fail. We rather arbitrarily use the first matching overload
// in that case.
2022-10-21 18:33:43 +01:00
else if (std::optional<TypePackId> intersect = normalizer->intersectionOfTypePacks(*result, ftv->retTypes))
2022-10-07 00:55:58 +01:00
result = intersect;
}
else
result = ftv->retTypes;
}
else if (auto e = hasUnificationTooComplex(innerState.errors))
{
reportError(*e);
2023-01-03 17:33:19 +00:00
return builtinTypes->errorRecoveryTypePack(args);
2022-10-07 00:55:58 +01:00
}
}
}
}
if (result)
return *result;
else if (firstFun)
{
// TODO: better error reporting?
// The logic for error reporting overload resolution
// is currently over in TypeInfer.cpp, should we move it?
2022-11-04 17:02:37 +00:00
reportError(location, GenericError{"No matching overload."});
2023-01-03 17:33:19 +00:00
return builtinTypes->errorRecoveryTypePack(firstFun->retTypes);
2022-10-07 00:55:58 +01:00
}
else
{
2022-11-04 17:02:37 +00:00
reportError(location, CannotCallNonFunction{function});
2023-01-03 17:33:19 +00:00
return builtinTypes->errorRecoveryTypePack();
2022-10-07 00:55:58 +01:00
}
}
2022-03-24 21:49:08 +00:00
bool Unifier::canCacheResult(TypeId subTy, TypeId superTy)
{
bool* superTyInfo = sharedState.skipCacheForType.find(superTy);
if (superTyInfo && *superTyInfo)
2022-03-24 21:49:08 +00:00
return false;
bool* subTyInfo = sharedState.skipCacheForType.find(subTy);
if (subTyInfo && *subTyInfo)
2022-03-24 21:49:08 +00:00
return false;
auto skipCacheFor = [this](TypeId ty) {
2022-02-11 18:43:14 +00:00
SkipCacheForType visitor{sharedState.skipCacheForType, types};
2022-05-26 21:33:48 +01:00
visitor.traverse(ty);
sharedState.skipCacheForType[ty] = visitor.result;
return visitor.result;
};
if (!superTyInfo && skipCacheFor(superTy))
2022-03-24 21:49:08 +00:00
return false;
if (!subTyInfo && skipCacheFor(subTy))
2022-03-24 21:49:08 +00:00
return false;
return true;
}
void Unifier::cacheResult(TypeId subTy, TypeId superTy, size_t prevErrorCount)
{
if (errors.size() == prevErrorCount)
{
if (canCacheResult(subTy, superTy))
sharedState.cachedUnify.insert({subTy, superTy});
}
else if (errors.size() == prevErrorCount + 1)
{
if (canCacheResult(subTy, superTy))
sharedState.cachedUnifyError[{subTy, superTy}] = errors.back().data;
}
}
2022-01-06 22:10:07 +00:00
struct WeirdIter
{
TypePackId packId;
TxnLog& log;
TypePack* pack;
size_t index;
bool growing;
TypeLevel level;
2022-09-29 23:11:54 +01:00
Scope* scope = nullptr;
2022-01-06 22:10:07 +00:00
WeirdIter(TypePackId packId, TxnLog& log)
: packId(packId)
, log(log)
, pack(log.getMutable<TypePack>(packId))
, index(0)
, growing(false)
{
while (pack && pack->head.empty() && pack->tail)
{
packId = *pack->tail;
pack = log.getMutable<TypePack>(packId);
}
}
WeirdIter(const WeirdIter&) = default;
TypeId& operator*()
{
LUAU_ASSERT(good());
return pack->head[index];
}
bool good() const
{
return pack != nullptr && index < pack->head.size();
}
2023-07-28 12:37:00 +01:00
std::optional<TypePackId> tail() const
{
if (!pack)
return packId;
LUAU_ASSERT(index == pack->head.size());
return pack->tail;
}
2022-01-06 22:10:07 +00:00
bool advance()
{
if (!pack)
return good();
if (index < pack->head.size())
++index;
if (growing || index < pack->head.size())
return good();
if (pack->tail)
{
packId = log.follow(*pack->tail);
pack = log.getMutable<TypePack>(packId);
index = 0;
}
return good();
}
bool canGrow() const
{
2023-04-07 20:56:27 +01:00
return nullptr != log.getMutable<FreeTypePack>(packId);
2022-01-06 22:10:07 +00:00
}
void grow(TypePackId newTail)
{
LUAU_ASSERT(canGrow());
LUAU_ASSERT(log.getMutable<TypePack>(newTail));
2023-04-07 20:56:27 +01:00
auto freePack = log.getMutable<FreeTypePack>(packId);
2023-01-20 12:02:39 +00:00
level = freePack->level;
if (FFlag::LuauMaintainScopesInUnifier && freePack->scope != nullptr)
scope = freePack->scope;
2022-04-14 22:57:15 +01:00
log.replace(packId, BoundTypePack(newTail));
2022-01-06 22:10:07 +00:00
packId = newTail;
pack = log.getMutable<TypePack>(newTail);
index = 0;
growing = true;
}
void pushType(TypeId ty)
{
LUAU_ASSERT(pack);
PendingTypePack* pendingPack = log.queue(packId);
if (TypePack* pending = getMutable<TypePack>(pendingPack))
{
pending->head.push_back(ty);
// We've potentially just replaced the TypePack* that we need to look
// in. We need to replace pack.
pack = pending;
}
else
{
LUAU_ASSERT(!"Pending state for this pack was not a TypePack");
}
}
};
2023-07-28 12:37:00 +01:00
void Unifier::enableNewSolver()
2023-05-12 13:15:01 +01:00
{
2023-07-28 12:37:00 +01:00
useNewSolver = true;
2023-05-12 13:15:01 +01:00
log.useScopes = true;
}
2022-01-06 22:10:07 +00:00
ErrorVec Unifier::canUnify(TypeId subTy, TypeId superTy)
{
Unifier s = makeChildUnifier();
2022-01-06 22:10:07 +00:00
s.tryUnify_(subTy, superTy);
return s.errors;
}
2022-01-06 22:10:07 +00:00
ErrorVec Unifier::canUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall)
{
Unifier s = makeChildUnifier();
2022-01-06 22:10:07 +00:00
s.tryUnify_(subTy, superTy, isFunctionCall);
return s.errors;
}
2022-01-06 22:10:07 +00:00
void Unifier::tryUnify(TypePackId subTp, TypePackId superTp, bool isFunctionCall)
{
2021-12-02 23:20:08 +00:00
sharedState.counters.iterationCount = 0;
2022-01-06 22:10:07 +00:00
tryUnify_(subTp, superTp, isFunctionCall);
}
/*
* This is quite tricky: we are walking two rope-like structures and unifying corresponding elements.
* If one is longer than the other, but the short end is free, we grow it to the required length.
*/
2022-01-06 22:10:07 +00:00
void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCall)
{
2022-10-21 18:33:43 +01:00
RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
2021-12-02 23:20:08 +00:00
++sharedState.counters.iterationCount;
2022-10-21 18:33:43 +01:00
if (sharedState.counters.iterationLimit > 0 && sharedState.counters.iterationLimit < sharedState.counters.iterationCount)
2022-04-14 22:57:15 +01:00
{
2022-11-04 17:02:37 +00:00
reportError(location, UnificationTooComplex{});
2022-10-21 18:33:43 +01:00
return;
}
2022-03-11 16:31:18 +00:00
superTp = log.follow(superTp);
subTp = log.follow(subTp);
while (auto tp = log.getMutable<TypePack>(subTp))
{
if (tp->head.empty() && tp->tail)
subTp = log.follow(*tp->tail);
else
break;
}
while (auto tp = log.getMutable<TypePack>(superTp))
{
if (tp->head.empty() && tp->tail)
superTp = log.follow(*tp->tail);
else
break;
}
if (superTp == subTp)
return;
2022-04-29 02:04:52 +01:00
if (log.haveSeen(superTp, subTp))
2022-03-11 16:31:18 +00:00
return;
2023-05-25 21:46:51 +01:00
if (isBlocked(log, subTp) && isBlocked(log, superTp))
{
blockedTypePacks.push_back(subTp);
blockedTypePacks.push_back(superTp);
}
else if (isBlocked(log, subTp))
blockedTypePacks.push_back(subTp);
else if (isBlocked(log, superTp))
blockedTypePacks.push_back(superTp);
2023-07-28 12:37:00 +01:00
if (auto superFree = log.getMutable<FreeTypePack>(superTp))
{
2023-04-14 13:05:27 +01:00
if (!occursCheck(superTp, subTp, /* reversed = */ true))
{
2023-01-03 17:33:19 +00:00
Widen widen{types, builtinTypes};
2023-07-28 12:37:00 +01:00
if (useNewSolver)
promoteTypeLevels(log, types, superFree->level, superFree->scope, /*useScopes*/ true, subTp);
2022-03-18 00:06:25 +00:00
log.replace(superTp, Unifiable::Bound<TypePackId>(widen(subTp)));
}
2022-03-11 16:31:18 +00:00
}
2023-07-28 12:37:00 +01:00
else if (auto subFree = log.getMutable<FreeTypePack>(subTp))
2022-03-11 16:31:18 +00:00
{
2023-04-14 13:05:27 +01:00
if (!occursCheck(subTp, superTp, /* reversed = */ false))
{
2023-07-28 12:37:00 +01:00
if (useNewSolver)
promoteTypeLevels(log, types, subFree->level, subFree->scope, /*useScopes*/ true, superTp);
2022-03-11 16:31:18 +00:00
log.replace(subTp, Unifiable::Bound<TypePackId>(superTp));
}
2022-03-11 16:31:18 +00:00
}
2023-05-12 13:15:01 +01:00
else if (hideousFixMeGenericsAreActuallyFree && log.getMutable<GenericTypePack>(superTp))
{
if (!occursCheck(superTp, subTp, /* reversed = */ true))
{
Widen widen{types, builtinTypes};
log.replace(superTp, Unifiable::Bound<TypePackId>(widen(subTp)));
}
}
else if (hideousFixMeGenericsAreActuallyFree && log.getMutable<GenericTypePack>(subTp))
{
if (!occursCheck(subTp, superTp, /* reversed = */ false))
{
log.replace(subTp, Unifiable::Bound<TypePackId>(superTp));
}
}
2022-03-11 16:31:18 +00:00
else if (log.getMutable<Unifiable::Error>(superTp))
tryUnifyWithAny(subTp, superTp);
else if (log.getMutable<Unifiable::Error>(subTp))
tryUnifyWithAny(superTp, subTp);
else if (log.getMutable<VariadicTypePack>(superTp))
tryUnifyVariadics(subTp, superTp, false);
else if (log.getMutable<VariadicTypePack>(subTp))
tryUnifyVariadics(superTp, subTp, true);
else if (log.getMutable<TypePack>(superTp) && log.getMutable<TypePack>(subTp))
{
auto superTpv = log.getMutable<TypePack>(superTp);
auto subTpv = log.getMutable<TypePack>(subTp);
2022-03-11 16:31:18 +00:00
// If the size of two heads does not match, but both packs have free tail
// We set the sentinel variable to say so to avoid growing it forever.
2022-04-14 22:57:15 +01:00
auto [superTypes, superTail] = flatten(superTp, log);
auto [subTypes, subTail] = flatten(subTp, log);
2022-03-11 16:31:18 +00:00
bool noInfiniteGrowth = (superTypes.size() != subTypes.size()) && (superTail && log.getMutable<FreeTypePack>(*superTail)) &&
(subTail && log.getMutable<FreeTypePack>(*subTail));
auto superIter = WeirdIter(superTp, log);
auto subIter = WeirdIter(subTp, log);
2022-03-04 16:19:20 +00:00
2023-01-20 12:02:39 +00:00
if (FFlag::LuauMaintainScopesInUnifier)
{
superIter.scope = scope.get();
subIter.scope = scope.get();
}
2022-09-29 23:11:54 +01:00
auto mkFreshType = [this](Scope* scope, TypeLevel level) {
if (FFlag::DebugLuauDeferredConstraintResolution)
return freshType(NotNull{types}, builtinTypes, scope);
else
return types->freshType(scope, level);
2022-03-11 16:31:18 +00:00
};
const TypePackId emptyTp = types->addTypePack(TypePack{{}, std::nullopt});
int loopCount = 0;
do
{
2022-03-11 16:31:18 +00:00
if (FInt::LuauTypeInferTypePackLoopLimit > 0 && loopCount >= FInt::LuauTypeInferTypePackLoopLimit)
ice("Detected possibly infinite TypePack growth");
2022-03-11 16:31:18 +00:00
++loopCount;
if (superIter.good() && subIter.growing)
{
2022-09-29 23:11:54 +01:00
subIter.pushType(mkFreshType(subIter.scope, subIter.level));
}
2022-03-11 16:31:18 +00:00
if (subIter.good() && superIter.growing)
{
2022-09-29 23:11:54 +01:00
superIter.pushType(mkFreshType(superIter.scope, superIter.level));
}
2022-03-11 16:31:18 +00:00
if (superIter.good() && subIter.good())
{
tryUnify_(*subIter, *superIter);
2022-03-11 16:31:18 +00:00
if (!errors.empty() && !firstPackErrorPos)
firstPackErrorPos = loopCount;
2022-03-11 16:31:18 +00:00
superIter.advance();
subIter.advance();
continue;
}
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
// If both are at the end, we're done
if (!superIter.good() && !subIter.good())
{
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2022-12-02 10:46:05 +00:00
{
2023-07-28 12:37:00 +01:00
if (subIter.tail() && superIter.tail())
tryUnify_(*subIter.tail(), *superIter.tail());
else if (subIter.tail())
{
const TypePackId subTail = log.follow(*subIter.tail());
if (log.get<FreeTypePack>(subTail))
tryUnify_(subTail, emptyTp);
else if (log.get<GenericTypePack>(subTail))
reportError(location, TypePackMismatch{subTail, emptyTp});
else if (log.get<VariadicTypePack>(subTail) || log.get<ErrorTypePack>(subTail))
{
// Nothing. This is ok.
}
else
{
ice("Unexpected subtype tail pack " + toString(subTail), location);
}
}
else if (superIter.tail())
{
const TypePackId superTail = log.follow(*superIter.tail());
if (log.get<FreeTypePack>(superTail))
tryUnify_(emptyTp, superTail);
else if (log.get<GenericTypePack>(superTail))
reportError(location, TypePackMismatch{emptyTp, superTail});
else if (log.get<VariadicTypePack>(superTail) || log.get<ErrorTypePack>(superTail))
{
// Nothing. This is ok.
}
else
{
ice("Unexpected supertype tail pack " + toString(superTail), location);
}
}
else
{
// Nothing. This is ok.
}
2022-12-02 10:46:05 +00:00
}
2023-07-28 12:37:00 +01:00
else
2022-12-02 10:46:05 +00:00
{
2023-07-28 12:37:00 +01:00
const bool lFreeTail = superTpv->tail && log.getMutable<FreeTypePack>(log.follow(*superTpv->tail)) != nullptr;
const bool rFreeTail = subTpv->tail && log.getMutable<FreeTypePack>(log.follow(*subTpv->tail)) != nullptr;
if (lFreeTail && rFreeTail)
{
2022-12-02 10:46:05 +00:00
tryUnify_(*subTpv->tail, *superTpv->tail);
2023-07-28 12:37:00 +01:00
}
else if (lFreeTail)
{
tryUnify_(emptyTp, *superTpv->tail);
}
else if (rFreeTail)
{
tryUnify_(emptyTp, *subTpv->tail);
}
else if (subTpv->tail && superTpv->tail)
{
if (log.getMutable<VariadicTypePack>(superIter.packId))
tryUnifyVariadics(subIter.packId, superIter.packId, false, int(subIter.index));
else if (log.getMutable<VariadicTypePack>(subIter.packId))
tryUnifyVariadics(superIter.packId, subIter.packId, true, int(superIter.index));
else
tryUnify_(*subTpv->tail, *superTpv->tail);
}
2022-12-02 10:46:05 +00:00
}
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
break;
}
2022-03-11 16:31:18 +00:00
// If both tails are free, bind one to the other and call it a day
if (superIter.canGrow() && subIter.canGrow())
return tryUnify_(*subIter.pack->tail, *superIter.pack->tail);
2022-03-11 16:31:18 +00:00
// If just one side is free on its tail, grow it to fit the other side.
// FIXME: The tail-most tail of the growing pack should be the same as the tail-most tail of the non-growing pack.
if (superIter.canGrow())
superIter.grow(types->addTypePack(TypePackVar(TypePack{})));
else if (subIter.canGrow())
subIter.grow(types->addTypePack(TypePackVar(TypePack{})));
else
{
// A union type including nil marks an optional argument
2022-10-13 23:59:53 +01:00
if (superIter.good() && isOptional(*superIter))
{
2022-03-11 16:31:18 +00:00
superIter.advance();
continue;
}
2022-10-13 23:59:53 +01:00
else if (subIter.good() && isOptional(*subIter))
{
2022-03-11 16:31:18 +00:00
subIter.advance();
continue;
}
2022-03-11 16:31:18 +00:00
if (log.getMutable<VariadicTypePack>(superIter.packId))
{
tryUnifyVariadics(subIter.packId, superIter.packId, false, int(subIter.index));
return;
}
if (log.getMutable<VariadicTypePack>(subIter.packId))
{
2022-03-11 16:31:18 +00:00
tryUnifyVariadics(superIter.packId, subIter.packId, true, int(superIter.index));
return;
}
2022-01-06 22:10:07 +00:00
2022-10-13 23:59:53 +01:00
if (!isFunctionCall && subIter.good())
2022-03-11 16:31:18 +00:00
{
// Sometimes it is ok to pass too many arguments
return;
}
2022-03-11 16:31:18 +00:00
// This is a bit weird because we don't actually know expected vs actual. We just know
// subtype vs supertype. If we are checking the values returned by a function, we swap
// these to produce the expected error message.
size_t expectedSize = size(superTp);
size_t actualSize = size(subTp);
2022-09-29 23:11:54 +01:00
if (ctx == CountMismatch::FunctionResult || ctx == CountMismatch::ExprListResult)
2022-03-11 16:31:18 +00:00
std::swap(expectedSize, actualSize);
2022-11-04 17:02:37 +00:00
reportError(location, CountMismatch{expectedSize, std::nullopt, actualSize, ctx});
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
while (superIter.good())
2022-01-06 22:10:07 +00:00
{
2023-01-03 17:33:19 +00:00
tryUnify_(*superIter, builtinTypes->errorRecoveryType());
2022-03-11 16:31:18 +00:00
superIter.advance();
}
2022-03-11 16:31:18 +00:00
while (subIter.good())
{
2023-01-03 17:33:19 +00:00
tryUnify_(*subIter, builtinTypes->errorRecoveryType());
2022-03-11 16:31:18 +00:00
subIter.advance();
2022-01-06 22:10:07 +00:00
}
2022-03-11 16:31:18 +00:00
return;
}
} while (!noInfiniteGrowth);
}
else
{
2023-01-20 12:02:39 +00:00
reportError(location, TypePackMismatch{subTp, superTp});
2022-03-11 16:31:18 +00:00
}
}
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
void Unifier::tryUnifyPrimitives(TypeId subTy, TypeId superTy)
{
2023-01-03 17:33:19 +00:00
const PrimitiveType* superPrim = get<PrimitiveType>(superTy);
const PrimitiveType* subPrim = get<PrimitiveType>(subTy);
2022-03-11 16:31:18 +00:00
if (!superPrim || !subPrim)
ice("passed non primitive types to unifyPrimitives");
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
if (superPrim->type != subPrim->type)
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, mismatchContext()});
2022-03-11 16:31:18 +00:00
}
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
void Unifier::tryUnifySingletons(TypeId subTy, TypeId superTy)
{
2023-01-03 17:33:19 +00:00
const PrimitiveType* superPrim = get<PrimitiveType>(superTy);
const SingletonType* superSingleton = get<SingletonType>(superTy);
const SingletonType* subSingleton = get<SingletonType>(subTy);
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
if ((!superPrim && !superSingleton) || !subSingleton)
ice("passed non singleton/primitive types to unifySingletons");
2021-11-18 22:21:07 +00:00
2022-01-06 22:10:07 +00:00
if (superSingleton && *superSingleton == *subSingleton)
2021-11-18 22:21:07 +00:00
return;
2023-01-03 17:33:19 +00:00
if (superPrim && superPrim->type == PrimitiveType::Boolean && get<BooleanSingleton>(subSingleton) && variance == Covariant)
2021-11-18 22:21:07 +00:00
return;
2023-01-03 17:33:19 +00:00
if (superPrim && superPrim->type == PrimitiveType::String && get<StringSingleton>(subSingleton) && variance == Covariant)
2021-11-18 22:21:07 +00:00
return;
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, mismatchContext()});
2021-11-18 22:21:07 +00:00
}
2022-01-06 22:10:07 +00:00
void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCall)
{
2023-01-03 17:33:19 +00:00
FunctionType* superFunction = log.getMutable<FunctionType>(superTy);
FunctionType* subFunction = log.getMutable<FunctionType>(subTy);
2022-01-06 22:10:07 +00:00
if (!superFunction || !subFunction)
ice("passed non-function types to unifyFunction");
2022-01-06 22:10:07 +00:00
size_t numGenerics = superFunction->generics.size();
2022-10-07 00:55:58 +01:00
size_t numGenericPacks = superFunction->genericPacks.size();
bool shouldInstantiate = (numGenerics == 0 && subFunction->generics.size() > 0) || (numGenericPacks == 0 && subFunction->genericPacks.size() > 0);
2022-10-27 23:22:49 +01:00
// TODO: This is unsound when the context is invariant, but the annotation burden without allowing it and without
// read-only properties is too high for lua-apps. Read-only properties _should_ resolve their issue by allowing
// generic methods in tables to be marked read-only.
if (FFlag::LuauInstantiateInSubtyping && shouldInstantiate)
2022-10-07 00:55:58 +01:00
{
Instantiation instantiation{&log, types, builtinTypes, scope->level, scope};
2022-10-07 00:55:58 +01:00
std::optional<TypeId> instantiated = instantiation.substitute(subTy);
if (instantiated.has_value())
{
2023-01-03 17:33:19 +00:00
subFunction = log.getMutable<FunctionType>(*instantiated);
2022-10-07 00:55:58 +01:00
if (!subFunction)
ice("instantiation made a function type into a non-function type in unifyFunction");
numGenerics = std::min(superFunction->generics.size(), subFunction->generics.size());
numGenericPacks = std::min(superFunction->genericPacks.size(), subFunction->genericPacks.size());
}
else
{
2022-11-04 17:02:37 +00:00
reportError(location, UnificationTooComplex{});
2022-10-07 00:55:58 +01:00
}
}
else if (numGenerics != subFunction->generics.size())
{
2022-01-06 22:10:07 +00:00
numGenerics = std::min(superFunction->generics.size(), subFunction->generics.size());
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "different number of generic type parameters", mismatchContext()});
}
2022-01-06 22:10:07 +00:00
if (numGenericPacks != subFunction->genericPacks.size())
{
2022-01-06 22:10:07 +00:00
numGenericPacks = std::min(superFunction->genericPacks.size(), subFunction->genericPacks.size());
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "different number of generic type pack parameters", mismatchContext()});
}
2021-11-12 02:12:39 +00:00
for (size_t i = 0; i < numGenerics; i++)
2022-01-06 22:10:07 +00:00
{
2022-03-11 16:31:18 +00:00
log.pushSeen(superFunction->generics[i], subFunction->generics[i]);
2022-01-06 22:10:07 +00:00
}
2022-04-29 02:04:52 +01:00
for (size_t i = 0; i < numGenericPacks; i++)
2022-03-04 16:19:20 +00:00
{
2022-04-29 02:04:52 +01:00
log.pushSeen(superFunction->genericPacks[i], subFunction->genericPacks[i]);
2022-03-04 16:19:20 +00:00
}
CountMismatch::Context context = ctx;
if (!isFunctionCall)
{
Unifier innerState = makeChildUnifier();
2022-01-27 21:29:34 +00:00
innerState.ctx = CountMismatch::Arg;
innerState.tryUnify_(superFunction->argTypes, subFunction->argTypes, isFunctionCall);
bool reported = !innerState.errors.empty();
2022-01-27 21:29:34 +00:00
if (auto e = hasUnificationTooComplex(innerState.errors))
reportError(*e);
else if (!innerState.errors.empty() && innerState.firstPackErrorPos)
2022-11-04 17:02:37 +00:00
reportError(location, TypeMismatch{superTy, subTy, format("Argument #%d type is not compatible.", *innerState.firstPackErrorPos),
2022-12-02 10:46:05 +00:00
innerState.errors.front(), mismatchContext()});
2022-01-27 21:29:34 +00:00
else if (!innerState.errors.empty())
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "", innerState.errors.front(), mismatchContext()});
2022-09-29 23:11:54 +01:00
innerState.ctx = CountMismatch::FunctionResult;
2022-06-17 01:54:42 +01:00
innerState.tryUnify_(subFunction->retTypes, superFunction->retTypes);
2022-01-27 21:29:34 +00:00
if (!reported)
{
if (auto e = hasUnificationTooComplex(innerState.errors))
2022-01-27 21:29:34 +00:00
reportError(*e);
2022-06-17 01:54:42 +01:00
else if (!innerState.errors.empty() && size(superFunction->retTypes) == 1 && finite(superFunction->retTypes))
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "Return type is not compatible.", innerState.errors.front(), mismatchContext()});
else if (!innerState.errors.empty() && innerState.firstPackErrorPos)
2022-11-04 17:02:37 +00:00
reportError(location, TypeMismatch{superTy, subTy, format("Return #%d type is not compatible.", *innerState.firstPackErrorPos),
2022-12-02 10:46:05 +00:00
innerState.errors.front(), mismatchContext()});
else if (!innerState.errors.empty())
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, "", innerState.errors.front(), mismatchContext()});
}
2022-03-11 16:31:18 +00:00
log.concat(std::move(innerState.log));
}
else
{
ctx = CountMismatch::Arg;
2022-01-06 22:10:07 +00:00
tryUnify_(superFunction->argTypes, subFunction->argTypes, isFunctionCall);
2022-09-29 23:11:54 +01:00
ctx = CountMismatch::FunctionResult;
2022-06-17 01:54:42 +01:00
tryUnify_(subFunction->retTypes, superFunction->retTypes);
}
2022-06-24 02:44:07 +01:00
// Updating the log may have invalidated the function pointers
2023-01-03 17:33:19 +00:00
superFunction = log.getMutable<FunctionType>(superTy);
subFunction = log.getMutable<FunctionType>(subTy);
2022-03-18 00:06:25 +00:00
ctx = context;
2022-04-29 02:04:52 +01:00
for (int i = int(numGenericPacks) - 1; 0 <= i; i--)
2022-03-04 16:19:20 +00:00
{
2022-04-29 02:04:52 +01:00
log.popSeen(superFunction->genericPacks[i], subFunction->genericPacks[i]);
2022-03-04 16:19:20 +00:00
}
2021-11-12 02:12:39 +00:00
for (int i = int(numGenerics) - 1; 0 <= i; i--)
2022-01-06 22:10:07 +00:00
{
2022-03-11 16:31:18 +00:00
log.popSeen(superFunction->generics[i], subFunction->generics[i]);
2022-01-06 22:10:07 +00:00
}
}
namespace
{
struct Resetter
{
explicit Resetter(Variance* variance)
: oldValue(*variance)
, variance(variance)
{
}
Variance oldValue;
Variance* variance;
~Resetter()
{
*variance = oldValue;
}
};
} // namespace
2023-07-28 12:37:00 +01:00
void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, const LiteralProperties* literalProperties)
{
2023-03-03 13:45:38 +00:00
if (isPrim(log.follow(subTy), PrimitiveType::Table))
subTy = builtinTypes->emptyTableType;
if (isPrim(log.follow(superTy), PrimitiveType::Table))
superTy = builtinTypes->emptyTableType;
2023-02-03 12:34:12 +00:00
TypeId activeSubTy = subTy;
2023-01-03 17:33:19 +00:00
TableType* superTable = log.getMutable<TableType>(superTy);
TableType* subTable = log.getMutable<TableType>(subTy);
2022-02-03 23:09:37 +00:00
2022-01-06 22:10:07 +00:00
if (!superTable || !subTable)
ice("passed non-table types to unifyTables");
std::vector<std::string> missingProperties;
std::vector<std::string> extraProperties;
2022-10-07 00:55:58 +01:00
if (FFlag::LuauInstantiateInSubtyping)
{
if (variance == Covariant && subTable->state == TableState::Generic && superTable->state != TableState::Generic)
{
Instantiation instantiation{&log, types, builtinTypes, subTable->level, scope};
2022-10-07 00:55:58 +01:00
std::optional<TypeId> instantiated = instantiation.substitute(subTy);
if (instantiated.has_value())
{
2023-03-10 19:20:04 +00:00
activeSubTy = *instantiated;
subTable = log.getMutable<TableType>(activeSubTy);
2022-10-07 00:55:58 +01:00
if (!subTable)
ice("instantiation made a table type into a non-table type in tryUnifyTables");
}
else
{
2022-11-04 17:02:37 +00:00
reportError(location, UnificationTooComplex{});
2022-10-07 00:55:58 +01:00
}
}
}
// Optimization: First test that the property sets are compatible without doing any recursive unification
2022-03-04 16:19:20 +00:00
if (!subTable->indexer && subTable->state != TableState::Free)
{
2022-01-06 22:10:07 +00:00
for (const auto& [propName, superProp] : superTable->props)
{
2022-01-06 22:10:07 +00:00
auto subIter = subTable->props.find(propName);
2022-02-03 23:09:37 +00:00
2023-04-28 12:55:55 +01:00
if (subIter == subTable->props.end() && subTable->state == TableState::Unsealed && !isOptional(superProp.type()))
2022-05-20 00:46:52 +01:00
missingProperties.push_back(propName);
}
if (!missingProperties.empty())
{
2022-11-04 17:02:37 +00:00
reportError(location, MissingProperties{superTy, subTy, std::move(missingProperties)});
return;
}
}
// And vice versa if we're invariant
2022-04-14 22:57:15 +01:00
if (variance == Invariant && !superTable->indexer && superTable->state != TableState::Unsealed && superTable->state != TableState::Free)
{
2022-01-06 22:10:07 +00:00
for (const auto& [propName, subProp] : subTable->props)
{
2022-01-06 22:10:07 +00:00
auto superIter = superTable->props.find(propName);
2022-02-03 23:09:37 +00:00
2022-06-24 02:44:07 +01:00
if (superIter == superTable->props.end())
2022-05-20 00:46:52 +01:00
extraProperties.push_back(propName);
}
if (!extraProperties.empty())
{
2022-11-04 17:02:37 +00:00
reportError(location, MissingProperties{superTy, subTy, std::move(extraProperties), MissingProperties::Extra});
return;
}
}
2021-11-18 22:21:07 +00:00
// Width subtyping: any property in the supertype must be in the subtype,
// and the types must agree.
2022-01-06 22:10:07 +00:00
for (const auto& [name, prop] : superTable->props)
{
2022-01-06 22:10:07 +00:00
const auto& r = subTable->props.find(name);
if (r != subTable->props.end())
{
// TODO: read-only properties don't need invariance
Resetter resetter{&variance};
2023-07-28 12:37:00 +01:00
if (!literalProperties || !literalProperties->contains(name))
variance = Invariant;
Unifier innerState = makeChildUnifier();
2023-04-28 12:55:55 +01:00
innerState.tryUnify_(r->second.type(), prop.type());
2021-11-12 02:12:39 +00:00
2022-01-06 22:10:07 +00:00
checkChildUnifierTypeMismatch(innerState.errors, name, superTy, subTy);
2021-11-12 02:12:39 +00:00
2022-03-11 16:31:18 +00:00
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
2023-03-17 14:59:30 +00:00
failure |= innerState.failure;
}
2022-03-04 16:19:20 +00:00
else if (subTable->indexer && maybeString(subTable->indexer->indexType))
{
// TODO: read-only indexers don't need invariance
// TODO: really we should only allow this if prop.type is optional.
Resetter resetter{&variance};
2023-07-28 12:37:00 +01:00
if (!literalProperties || !literalProperties->contains(name))
variance = Invariant;
Unifier innerState = makeChildUnifier();
2023-04-28 12:55:55 +01:00
innerState.tryUnify_(subTable->indexer->indexResultType, prop.type());
2021-11-12 02:12:39 +00:00
2022-01-06 22:10:07 +00:00
checkChildUnifierTypeMismatch(innerState.errors, name, superTy, subTy);
2021-11-12 02:12:39 +00:00
2022-03-11 16:31:18 +00:00
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
2023-03-17 14:59:30 +00:00
failure |= innerState.failure;
}
2023-04-28 12:55:55 +01:00
else if (subTable->state == TableState::Unsealed && isOptional(prop.type()))
2022-03-04 16:19:20 +00:00
// This is sound because unsealed table types are precise, so `{ p : T } <: { p : T, q : U? }`
// since if `t : { p : T }` then we are guaranteed that `t.q` is `nil`.
// TODO: if the supertype is written to, the subtype may no longer be precise (alias analysis?)
{
}
2022-01-06 22:10:07 +00:00
else if (subTable->state == TableState::Free)
{
2023-02-03 12:34:12 +00:00
PendingType* pendingSub = log.queue(activeSubTy);
2023-01-03 17:33:19 +00:00
TableType* ttv = getMutable<TableType>(pendingSub);
2022-03-11 16:31:18 +00:00
LUAU_ASSERT(ttv);
ttv->props[name] = prop;
subTable = ttv;
}
else
missingProperties.push_back(name);
2022-03-11 16:31:18 +00:00
2022-04-29 02:04:52 +01:00
// Recursive unification can change the txn log, and invalidate the old
// table. If we detect that this has happened, we start over, with the updated
// txn log.
2023-03-10 19:20:04 +00:00
TypeId superTyNew = log.follow(superTy);
TypeId subTyNew = log.follow(activeSubTy);
2022-11-10 22:04:44 +00:00
2023-03-10 19:20:04 +00:00
// If one of the types stopped being a table altogether, we need to restart from the top
if ((superTy != superTyNew || activeSubTy != subTyNew) && errors.empty())
return tryUnify(subTy, superTy, false, isIntersection);
2022-11-10 22:04:44 +00:00
// Otherwise, restart only the table unification
2023-01-03 17:33:19 +00:00
TableType* newSuperTable = log.getMutable<TableType>(superTyNew);
TableType* newSubTable = log.getMutable<TableType>(subTyNew);
2022-11-10 22:04:44 +00:00
2023-03-10 19:20:04 +00:00
if (superTable != newSuperTable || subTable != newSubTable)
2022-03-11 16:31:18 +00:00
{
2023-09-08 00:24:03 +01:00
if (errors.empty())
{
2023-09-08 00:24:03 +01:00
RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
tryUnifyTables(subTy, superTy, isIntersection);
}
2023-09-08 00:24:03 +01:00
return;
2022-03-11 16:31:18 +00:00
}
}
2022-01-06 22:10:07 +00:00
for (const auto& [name, prop] : subTable->props)
{
2022-01-06 22:10:07 +00:00
if (superTable->props.count(name))
{
// If both lt and rt contain the property, then
// we're done since we already unified them above
}
2022-03-04 16:19:20 +00:00
else if (superTable->indexer && maybeString(superTable->indexer->indexType))
{
// TODO: read-only indexers don't need invariance
// TODO: really we should only allow this if prop.type is optional.
Resetter resetter{&variance};
2023-07-28 12:37:00 +01:00
if (!literalProperties || !literalProperties->contains(name))
variance = Invariant;
Unifier innerState = makeChildUnifier();
2023-09-15 17:27:45 +01:00
if (useNewSolver || FFlag::LuauFixIndexerSubtypingOrdering)
2023-07-28 12:37:00 +01:00
innerState.tryUnify_(prop.type(), superTable->indexer->indexResultType);
else
{
// Incredibly, the old solver depends on this bug somehow.
innerState.tryUnify_(superTable->indexer->indexResultType, prop.type());
}
2021-11-12 02:12:39 +00:00
2022-01-06 22:10:07 +00:00
checkChildUnifierTypeMismatch(innerState.errors, name, superTy, subTy);
2021-11-12 02:12:39 +00:00
2022-03-11 16:31:18 +00:00
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
2023-03-17 14:59:30 +00:00
failure |= innerState.failure;
}
2022-01-06 22:10:07 +00:00
else if (superTable->state == TableState::Unsealed)
{
// TODO: this case is unsound when variance is Invariant, but without it lua-apps fails to typecheck.
// TODO: file a JIRA
// TODO: hopefully readonly/writeonly properties will fix this.
Property clone = prop;
2023-04-28 12:55:55 +01:00
clone.setType(deeplyOptional(clone.type()));
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
PendingType* pendingSuper = log.queue(superTy);
2023-01-03 17:33:19 +00:00
TableType* pendingSuperTtv = getMutable<TableType>(pendingSuper);
2022-03-11 16:31:18 +00:00
pendingSuperTtv->props[name] = clone;
superTable = pendingSuperTtv;
}
else if (variance == Covariant)
{
}
2022-01-06 22:10:07 +00:00
else if (superTable->state == TableState::Free)
{
2022-03-11 16:31:18 +00:00
PendingType* pendingSuper = log.queue(superTy);
2023-01-03 17:33:19 +00:00
TableType* pendingSuperTtv = getMutable<TableType>(pendingSuper);
2022-03-11 16:31:18 +00:00
pendingSuperTtv->props[name] = prop;
superTable = pendingSuperTtv;
}
else
extraProperties.push_back(name);
2022-03-11 16:31:18 +00:00
2023-03-10 19:20:04 +00:00
TypeId superTyNew = log.follow(superTy);
TypeId subTyNew = log.follow(activeSubTy);
2022-12-02 10:46:05 +00:00
2023-03-10 19:20:04 +00:00
// If one of the types stopped being a table altogether, we need to restart from the top
if ((superTy != superTyNew || activeSubTy != subTyNew) && errors.empty())
return tryUnify(subTy, superTy, false, isIntersection);
2022-12-02 10:46:05 +00:00
2022-04-29 02:04:52 +01:00
// Recursive unification can change the txn log, and invalidate the old
// table. If we detect that this has happened, we start over, with the updated
// txn log.
2023-01-03 17:33:19 +00:00
TableType* newSuperTable = log.getMutable<TableType>(superTyNew);
TableType* newSubTable = log.getMutable<TableType>(subTyNew);
2023-02-03 12:34:12 +00:00
2023-03-10 19:20:04 +00:00
if (superTable != newSuperTable || subTable != newSubTable)
2022-03-11 16:31:18 +00:00
{
2023-09-08 00:24:03 +01:00
if (errors.empty())
{
2023-09-08 00:24:03 +01:00
RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
tryUnifyTables(subTy, superTy, isIntersection);
}
2023-09-08 00:24:03 +01:00
return;
2022-03-11 16:31:18 +00:00
}
}
// Unify indexers
2022-01-06 22:10:07 +00:00
if (superTable->indexer && subTable->indexer)
{
// TODO: read-only indexers don't need invariance
Resetter resetter{&variance};
variance = Invariant;
Unifier innerState = makeChildUnifier();
2022-03-24 21:49:08 +00:00
2022-04-21 22:04:22 +01:00
innerState.tryUnify_(subTable->indexer->indexType, superTable->indexer->indexType);
2022-03-24 21:49:08 +00:00
2022-04-21 22:04:22 +01:00
bool reported = !innerState.errors.empty();
2022-03-24 21:49:08 +00:00
2022-04-21 22:04:22 +01:00
checkChildUnifierTypeMismatch(innerState.errors, "[indexer key]", superTy, subTy);
2022-03-24 21:49:08 +00:00
2022-04-21 22:04:22 +01:00
innerState.tryUnify_(subTable->indexer->indexResultType, superTable->indexer->indexResultType);
2022-03-24 21:49:08 +00:00
2022-04-21 22:04:22 +01:00
if (!reported)
checkChildUnifierTypeMismatch(innerState.errors, "[indexer value]", superTy, subTy);
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
if (innerState.errors.empty())
log.concat(std::move(innerState.log));
2023-03-17 14:59:30 +00:00
failure |= innerState.failure;
}
2022-01-06 22:10:07 +00:00
else if (superTable->indexer)
{
2022-01-06 22:10:07 +00:00
if (subTable->state == TableState::Unsealed || subTable->state == TableState::Free)
{
// passing/assigning a table without an indexer to something that has one
// e.g. table.insert(t, 1) where t is a non-sealed table and doesn't have an indexer.
// TODO: we only need to do this if the supertype's indexer is read/write
// since that can add indexed elements.
2022-03-11 16:31:18 +00:00
log.changeIndexer(subTy, superTable->indexer);
}
}
2022-01-06 22:10:07 +00:00
else if (subTable->indexer && variance == Invariant)
{
// Symmetric if we are invariant
2022-01-06 22:10:07 +00:00
if (superTable->state == TableState::Unsealed || superTable->state == TableState::Free)
{
2022-03-11 16:31:18 +00:00
log.changeIndexer(superTy, subTable->indexer);
}
}
2022-04-29 02:04:52 +01:00
// Changing the indexer can invalidate the table pointers.
2023-03-10 19:20:04 +00:00
superTable = log.getMutable<TableType>(log.follow(superTy));
subTable = log.getMutable<TableType>(log.follow(activeSubTy));
2022-12-02 10:46:05 +00:00
2023-03-10 19:20:04 +00:00
if (!superTable || !subTable)
return;
if (!missingProperties.empty())
{
2022-11-04 17:02:37 +00:00
reportError(location, MissingProperties{superTy, subTy, std::move(missingProperties)});
return;
}
if (!extraProperties.empty())
{
2022-11-04 17:02:37 +00:00
reportError(location, MissingProperties{superTy, subTy, std::move(extraProperties), MissingProperties::Extra});
return;
}
/*
2023-01-03 17:33:19 +00:00
* Types are commonly cyclic, so it is entirely possible
* for unifying a property of a table to change the table itself!
* We need to check for this and start over if we notice this occurring.
*
* I believe this is guaranteed to terminate eventually because this will
* only happen when a free table is bound to another table.
*/
2022-01-06 22:10:07 +00:00
if (superTable->boundTo || subTable->boundTo)
return tryUnify_(subTy, superTy);
2022-01-06 22:10:07 +00:00
if (superTable->state == TableState::Free)
{
2022-03-11 16:31:18 +00:00
log.bindTable(superTy, subTy);
}
2022-01-06 22:10:07 +00:00
else if (subTable->state == TableState::Free)
{
2022-03-11 16:31:18 +00:00
log.bindTable(subTy, superTy);
}
}
2022-07-14 23:39:35 +01:00
void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed)
{
TypeId osubTy = subTy;
TypeId osuperTy = superTy;
2023-06-24 06:33:44 +01:00
if (checkInhabited && !normalizer->isInhabited(subTy))
2022-12-02 10:46:05 +00:00
return;
2022-07-14 23:39:35 +01:00
if (reversed)
std::swap(subTy, superTy);
2023-01-03 17:33:19 +00:00
TableType* superTable = log.getMutable<TableType>(superTy);
2022-11-10 22:04:44 +00:00
if (!superTable || superTable->state != TableState::Free)
2022-12-02 10:46:05 +00:00
return reportError(location, TypeMismatch{osuperTy, osubTy, mismatchContext()});
2022-07-14 23:39:35 +01:00
auto fail = [&](std::optional<TypeError> e) {
std::string reason = "The former's metatable does not satisfy the requirements.";
if (e)
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{osuperTy, osubTy, reason, *e, mismatchContext()});
2022-07-14 23:39:35 +01:00
else
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{osuperTy, osubTy, reason, mismatchContext()});
2022-07-14 23:39:35 +01:00
};
// Given t1 where t1 = { lower: (t1) -> (a, b...) }
// It should be the case that `string <: t1` iff `(subtype's metatable).__index <: t1`
2023-01-03 17:33:19 +00:00
if (auto metatable = getMetatable(subTy, builtinTypes))
2022-07-14 23:39:35 +01:00
{
2023-01-03 17:33:19 +00:00
auto mttv = log.get<TableType>(*metatable);
2022-07-14 23:39:35 +01:00
if (!mttv)
fail(std::nullopt);
if (auto it = mttv->props.find("__index"); it != mttv->props.end())
{
2023-04-28 12:55:55 +01:00
TypeId ty = it->second.type();
2022-07-14 23:39:35 +01:00
Unifier child = makeChildUnifier();
child.tryUnify_(ty, superTy);
2023-03-10 19:20:04 +00:00
// To perform subtype <: free table unification, we have tried to unify (subtype's metatable) <: free table
// There is a chance that it was unified with the origial subtype, but then, (subtype's metatable) <: subtype could've failed
// Here we check if we have a new supertype instead of the original free table and try original subtype <: new supertype check
TypeId newSuperTy = child.log.follow(superTy);
2022-11-10 22:04:44 +00:00
2023-03-10 19:20:04 +00:00
if (superTy != newSuperTy && canUnify(subTy, newSuperTy).empty())
{
log.replace(superTy, BoundType{subTy});
return;
2022-11-10 22:04:44 +00:00
}
2022-07-14 23:39:35 +01:00
if (auto e = hasUnificationTooComplex(child.errors))
reportError(*e);
else if (!child.errors.empty())
fail(child.errors.front());
log.concat(std::move(child.log));
2023-03-10 19:20:04 +00:00
// To perform subtype <: free table unification, we have tried to unify (subtype's metatable) <: free table
// We return success because subtype <: free table which means that correct unification is to replace free table with the subtype
if (child.errors.empty())
log.replace(superTy, BoundType{subTy});
2022-11-10 22:04:44 +00:00
2022-07-14 23:39:35 +01:00
return;
}
else
{
return fail(std::nullopt);
}
}
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{osuperTy, osubTy, mismatchContext()});
2022-07-14 23:39:35 +01:00
return;
}
TypeId Unifier::deeplyOptional(TypeId ty, std::unordered_map<TypeId, TypeId> seen)
{
ty = follow(ty);
2022-05-20 00:46:52 +01:00
if (isOptional(ty))
return ty;
2023-01-03 17:33:19 +00:00
else if (const TableType* ttv = get<TableType>(ty))
{
TypeId& result = seen[ty];
if (result)
return result;
result = types->addType(*ttv);
2023-01-03 17:33:19 +00:00
TableType* resultTtv = getMutable<TableType>(result);
for (auto& [name, prop] : resultTtv->props)
2023-04-28 12:55:55 +01:00
prop.setType(deeplyOptional(prop.type(), seen));
2023-01-03 17:33:19 +00:00
return types->addType(UnionType{{builtinTypes->nilType, result}});
}
else
2023-01-03 17:33:19 +00:00
return types->addType(UnionType{{builtinTypes->nilType, ty}});
}
2022-01-06 22:10:07 +00:00
void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
{
2023-01-03 17:33:19 +00:00
const MetatableType* superMetatable = get<MetatableType>(superTy);
2022-01-06 22:10:07 +00:00
if (!superMetatable)
2023-01-03 17:33:19 +00:00
ice("tryUnifyMetatable invoked with non-metatable Type");
2022-12-02 10:46:05 +00:00
TypeError mismatchError = TypeError{location, TypeMismatch{reversed ? subTy : superTy, reversed ? superTy : subTy, mismatchContext()}};
2023-01-03 17:33:19 +00:00
if (const MetatableType* subMetatable = log.getMutable<MetatableType>(subTy))
{
Unifier innerState = makeChildUnifier();
2022-01-06 22:10:07 +00:00
innerState.tryUnify_(subMetatable->table, superMetatable->table);
innerState.tryUnify_(subMetatable->metatable, superMetatable->metatable);
if (auto e = hasUnificationTooComplex(innerState.errors))
2022-01-27 21:29:34 +00:00
reportError(*e);
else if (!innerState.errors.empty())
2022-12-02 10:46:05 +00:00
reportError(
location, TypeMismatch{reversed ? subTy : superTy, reversed ? superTy : subTy, "", innerState.errors.front(), mismatchContext()});
2022-03-11 16:31:18 +00:00
log.concat(std::move(innerState.log));
2023-03-17 14:59:30 +00:00
failure |= innerState.failure;
}
2023-01-03 17:33:19 +00:00
else if (TableType* subTable = log.getMutable<TableType>(subTy))
{
2022-01-06 22:10:07 +00:00
switch (subTable->state)
{
case TableState::Free:
{
2023-07-28 12:37:00 +01:00
if (useNewSolver)
2022-09-29 23:11:54 +01:00
{
Unifier innerState = makeChildUnifier();
bool missingProperty = false;
for (const auto& [propName, prop] : subTable->props)
{
if (std::optional<TypeId> mtPropTy = findTablePropertyRespectingMeta(superTy, propName))
{
2023-04-28 12:55:55 +01:00
innerState.tryUnify(prop.type(), *mtPropTy);
2022-09-29 23:11:54 +01:00
}
else
{
reportError(mismatchError);
missingProperty = true;
break;
}
}
2023-01-03 17:33:19 +00:00
if (const TableType* superTable = log.get<TableType>(log.follow(superMetatable->table)))
2022-09-29 23:11:54 +01:00
{
// TODO: Unify indexers.
}
if (auto e = hasUnificationTooComplex(innerState.errors))
reportError(*e);
else if (!innerState.errors.empty())
2022-12-02 10:46:05 +00:00
reportError(TypeError{location,
TypeMismatch{reversed ? subTy : superTy, reversed ? superTy : subTy, "", innerState.errors.front(), mismatchContext()}});
2022-09-29 23:11:54 +01:00
else if (!missingProperty)
{
log.concat(std::move(innerState.log));
log.bindTable(subTy, superTy);
2023-03-17 14:59:30 +00:00
failure |= innerState.failure;
2022-09-29 23:11:54 +01:00
}
}
else
{
tryUnify_(subTy, superMetatable->table);
log.bindTable(subTy, superTy);
}
break;
}
// We know the shape of sealed, unsealed, and generic tables; you can't add a metatable on to any of these.
case TableState::Sealed:
case TableState::Unsealed:
case TableState::Generic:
2022-01-27 21:29:34 +00:00
reportError(mismatchError);
}
}
2023-01-03 17:33:19 +00:00
else if (log.getMutable<AnyType>(subTy) || log.getMutable<ErrorType>(subTy))
{
}
else
{
2022-01-27 21:29:34 +00:00
reportError(mismatchError);
}
}
// Class unification is almost, but not quite symmetrical. We use the 'reversed' boolean to indicate which scenario we are evaluating.
2022-01-06 22:10:07 +00:00
void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
{
if (reversed)
std::swap(superTy, subTy);
auto fail = [&]() {
if (!reversed)
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, mismatchContext()});
else
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{subTy, superTy, mismatchContext()});
};
2023-01-03 17:33:19 +00:00
const ClassType* superClass = get<ClassType>(superTy);
if (!superClass)
2023-01-03 17:33:19 +00:00
ice("tryUnifyClass invoked with non-class Type");
2023-01-03 17:33:19 +00:00
if (const ClassType* subClass = get<ClassType>(subTy))
{
switch (variance)
{
case Covariant:
if (!isSubclass(subClass, superClass))
return fail();
return;
case Invariant:
if (subClass != superClass)
return fail();
return;
}
ice("Illegal variance setting!");
}
2023-01-03 17:33:19 +00:00
else if (TableType* subTable = getMutable<TableType>(subTy))
{
/**
* A free table is something whose shape we do not exactly know yet.
* Thus, it is entirely reasonable that we might discover that it is being used as some class type.
* In this case, the free table must indeed be that exact class.
* For this to hold, the table must not have any properties that the class does not.
* Further, all properties of the table should unify cleanly with the matching class properties.
* TODO: What does it mean for the table to have an indexer? (probably failure?)
*
* Tables that are not free are known to be actual tables.
*/
2022-01-06 22:10:07 +00:00
if (subTable->state != TableState::Free)
return fail();
bool ok = true;
2022-01-06 22:10:07 +00:00
for (const auto& [propName, prop] : subTable->props)
{
const Property* classProp = lookupClassProp(superClass, propName);
if (!classProp)
{
ok = false;
2022-11-04 17:02:37 +00:00
reportError(location, UnknownProperty{superTy, propName});
}
else
2021-11-12 02:12:39 +00:00
{
Unifier innerState = makeChildUnifier();
2023-04-28 12:55:55 +01:00
innerState.tryUnify_(classProp->type(), prop.type());
2021-11-12 02:12:39 +00:00
checkChildUnifierTypeMismatch(innerState.errors, propName, reversed ? subTy : superTy, reversed ? superTy : subTy);
2021-11-12 02:12:39 +00:00
2022-03-11 16:31:18 +00:00
if (innerState.errors.empty())
{
2022-03-11 16:31:18 +00:00
log.concat(std::move(innerState.log));
2023-03-17 14:59:30 +00:00
failure |= innerState.failure;
2021-11-12 02:12:39 +00:00
}
else
{
2022-03-11 16:31:18 +00:00
ok = false;
2021-11-12 02:12:39 +00:00
}
}
}
2022-01-06 22:10:07 +00:00
if (subTable->indexer)
{
ok = false;
std::string msg = "Class " + superClass->name + " does not have an indexer";
2022-11-04 17:02:37 +00:00
reportError(location, GenericError{msg});
}
if (!ok)
return;
2022-03-11 16:31:18 +00:00
log.bindTable(subTy, superTy);
}
else
return fail();
}
2023-02-03 12:34:12 +00:00
void Unifier::tryUnifyNegations(TypeId subTy, TypeId superTy)
2022-10-27 23:22:49 +01:00
{
2023-02-03 12:34:12 +00:00
if (!log.get<NegationType>(subTy) && !log.get<NegationType>(superTy))
ice("tryUnifyNegations superTy or subTy must be a negation type");
2022-10-27 23:22:49 +01:00
const NormalizedType* subNorm = normalizer->normalize(subTy);
const NormalizedType* superNorm = normalizer->normalize(superTy);
if (!subNorm || !superNorm)
2023-09-22 19:10:49 +01:00
return reportError(location, NormalizationTooComplex{});
2022-10-27 23:22:49 +01:00
// T </: ~U iff T <: U
Unifier state = makeChildUnifier();
state.tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "");
if (state.errors.empty())
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{superTy, subTy, mismatchContext()});
2022-10-27 23:22:49 +01:00
}
static void queueTypePack(std::vector<TypeId>& queue, DenseHashSet<TypePackId>& seenTypePacks, Unifier& state, TypePackId a, TypePackId anyTypePack)
{
while (true)
{
2022-03-11 16:31:18 +00:00
a = state.log.follow(a);
if (seenTypePacks.find(a))
break;
seenTypePacks.insert(a);
2023-04-07 20:56:27 +01:00
if (state.log.getMutable<FreeTypePack>(a))
{
2023-04-07 20:56:27 +01:00
state.log.replace(a, BoundTypePack{anyTypePack});
}
2022-03-11 16:31:18 +00:00
else if (auto tp = state.log.getMutable<TypePack>(a))
{
2022-03-11 16:31:18 +00:00
queue.insert(queue.end(), tp->head.begin(), tp->head.end());
if (tp->tail)
a = *tp->tail;
else
break;
}
}
}
2022-01-06 22:10:07 +00:00
void Unifier::tryUnifyVariadics(TypePackId subTp, TypePackId superTp, bool reversed, int subOffset)
{
2022-03-11 16:31:18 +00:00
const VariadicTypePack* superVariadic = log.getMutable<VariadicTypePack>(superTp);
2023-04-14 13:05:27 +01:00
const TypeId variadicTy = follow(superVariadic->ty);
2022-01-06 22:10:07 +00:00
if (!superVariadic)
ice("passed non-variadic pack to tryUnifyVariadics");
2023-01-20 12:02:39 +00:00
if (const VariadicTypePack* subVariadic = log.get<VariadicTypePack>(subTp))
2022-12-02 10:46:05 +00:00
{
2023-04-14 13:05:27 +01:00
tryUnify_(reversed ? variadicTy : subVariadic->ty, reversed ? subVariadic->ty : variadicTy);
2022-12-02 10:46:05 +00:00
}
2023-01-20 12:02:39 +00:00
else if (log.get<TypePack>(subTp))
{
2022-01-06 22:10:07 +00:00
TypePackIterator subIter = begin(subTp, &log);
TypePackIterator subEnd = end(subTp);
2022-01-06 22:10:07 +00:00
std::advance(subIter, subOffset);
2022-01-06 22:10:07 +00:00
while (subIter != subEnd)
{
2023-04-14 13:05:27 +01:00
tryUnify_(reversed ? variadicTy : *subIter, reversed ? *subIter : variadicTy);
2022-01-06 22:10:07 +00:00
++subIter;
}
2022-01-06 22:10:07 +00:00
if (std::optional<TypePackId> maybeTail = subIter.tail())
{
TypePackId tail = follow(*maybeTail);
2023-05-25 21:46:51 +01:00
if (isBlocked(log, tail))
{
blockedTypePacks.push_back(tail);
}
else if (get<FreeTypePack>(tail))
{
2022-03-11 16:31:18 +00:00
log.replace(tail, BoundTypePack(superTp));
}
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(tail))
{
2023-04-14 13:05:27 +01:00
tryUnify_(vtp->ty, variadicTy);
}
2023-04-07 20:56:27 +01:00
else if (get<GenericTypePack>(tail))
{
2023-05-12 13:15:01 +01:00
if (!hideousFixMeGenericsAreActuallyFree)
reportError(location, GenericError{"Cannot unify variadic and generic packs"});
else
log.replace(tail, BoundTypePack{superTp});
}
else if (get<Unifiable::Error>(tail))
{
// Nothing to do here.
}
else
{
ice("Unknown TypePack kind");
}
}
}
2023-06-24 06:33:44 +01:00
else if (get<AnyType>(variadicTy) && log.get<GenericTypePack>(subTp))
2023-04-14 13:05:27 +01:00
{
// Nothing to do. This is ok.
}
else
{
2022-11-04 17:02:37 +00:00
reportError(location, GenericError{"Failed to unify variadic packs"});
}
}
static void tryUnifyWithAny(std::vector<TypeId>& queue, Unifier& state, DenseHashSet<TypeId>& seen, DenseHashSet<TypePackId>& seenTypePacks,
2022-02-11 18:43:14 +00:00
const TypeArena* typeArena, TypeId anyType, TypePackId anyTypePack)
{
while (!queue.empty())
{
2022-03-11 16:31:18 +00:00
TypeId ty = state.log.follow(queue.back());
queue.pop_back();
2022-02-11 18:43:14 +00:00
2022-03-11 16:31:18 +00:00
// Types from other modules don't have free types
2022-03-24 21:49:08 +00:00
if (ty->owningArena != typeArena)
2022-03-11 16:31:18 +00:00
continue;
2022-02-11 18:43:14 +00:00
2022-03-11 16:31:18 +00:00
if (seen.find(ty))
continue;
2022-03-11 16:31:18 +00:00
seen.insert(ty);
2022-01-06 22:10:07 +00:00
2023-01-03 17:33:19 +00:00
if (state.log.getMutable<FreeType>(ty))
2022-03-11 16:31:18 +00:00
{
2022-07-08 02:05:31 +01:00
// TODO: Only bind if the anyType isn't any, unknown, or error (?)
2023-01-03 17:33:19 +00:00
state.log.replace(ty, BoundType{anyType});
}
2023-01-03 17:33:19 +00:00
else if (auto fun = state.log.getMutable<FunctionType>(ty))
{
2022-03-11 16:31:18 +00:00
queueTypePack(queue, seenTypePacks, state, fun->argTypes, anyTypePack);
2022-06-17 01:54:42 +01:00
queueTypePack(queue, seenTypePacks, state, fun->retTypes, anyTypePack);
2022-03-11 16:31:18 +00:00
}
2023-01-03 17:33:19 +00:00
else if (auto table = state.log.getMutable<TableType>(ty))
2022-03-11 16:31:18 +00:00
{
for (const auto& [_name, prop] : table->props)
2023-04-28 12:55:55 +01:00
queue.push_back(prop.type());
2022-01-06 22:10:07 +00:00
2022-03-11 16:31:18 +00:00
if (table->indexer)
2022-01-06 22:10:07 +00:00
{
2022-03-11 16:31:18 +00:00
queue.push_back(table->indexer->indexType);
queue.push_back(table->indexer->indexResultType);
2022-01-06 22:10:07 +00:00
}
}
2023-01-03 17:33:19 +00:00
else if (auto mt = state.log.getMutable<MetatableType>(ty))
2022-03-11 16:31:18 +00:00
{
queue.push_back(mt->table);
queue.push_back(mt->metatable);
}
2023-01-03 17:33:19 +00:00
else if (state.log.getMutable<ClassType>(ty))
2022-03-11 16:31:18 +00:00
{
2023-01-03 17:33:19 +00:00
// ClassTypes never contain free types.
2022-03-11 16:31:18 +00:00
}
2023-01-03 17:33:19 +00:00
else if (auto union_ = state.log.getMutable<UnionType>(ty))
2022-03-11 16:31:18 +00:00
queue.insert(queue.end(), union_->options.begin(), union_->options.end());
2023-01-03 17:33:19 +00:00
else if (auto intersection = state.log.getMutable<IntersectionType>(ty))
2022-03-11 16:31:18 +00:00
queue.insert(queue.end(), intersection->parts.begin(), intersection->parts.end());
else
{
} // Primitives, any, errors, and generics are left untouched.
}
}
2022-01-06 22:10:07 +00:00
void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
{
2023-01-03 17:33:19 +00:00
LUAU_ASSERT(get<AnyType>(anyTy) || get<ErrorType>(anyTy) || get<UnknownType>(anyTy) || get<NeverType>(anyTy));
2023-02-17 14:53:37 +00:00
// These types are not visited in general loop below
if (log.get<PrimitiveType>(subTy) || log.get<AnyType>(subTy) || log.get<ClassType>(subTy))
return;
2023-01-27 21:28:45 +00:00
TypePackId anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}});
2022-01-06 22:10:07 +00:00
std::vector<TypeId> queue = {subTy};
2021-12-02 23:20:08 +00:00
sharedState.tempSeenTy.clear();
sharedState.tempSeenTp.clear();
2023-01-27 21:28:45 +00:00
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, anyTy, anyTp);
}
2022-01-06 22:10:07 +00:00
void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
{
2022-01-06 22:10:07 +00:00
LUAU_ASSERT(get<Unifiable::Error>(anyTp));
2023-01-03 17:33:19 +00:00
const TypeId anyTy = builtinTypes->errorRecoveryType();
2021-12-02 23:20:08 +00:00
std::vector<TypeId> queue;
2021-12-02 23:20:08 +00:00
sharedState.tempSeenTy.clear();
sharedState.tempSeenTp.clear();
2022-01-06 22:10:07 +00:00
queueTypePack(queue, sharedState.tempSeenTp, *this, subTy, anyTp);
2022-02-11 18:43:14 +00:00
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, anyTy, anyTp);
}
std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, Name name)
{
2023-01-03 17:33:19 +00:00
return Luau::findTablePropertyRespectingMeta(builtinTypes, errors, lhsType, name, location);
}
2022-12-02 10:46:05 +00:00
TxnLog Unifier::combineLogsIntoIntersection(std::vector<TxnLog> logs)
{
2023-07-28 12:37:00 +01:00
LUAU_ASSERT(useNewSolver);
TxnLog result(useNewSolver);
2022-12-02 10:46:05 +00:00
for (TxnLog& log : logs)
result.concatAsIntersections(std::move(log), NotNull{types});
return result;
}
TxnLog Unifier::combineLogsIntoUnion(std::vector<TxnLog> logs)
{
2023-07-28 12:37:00 +01:00
TxnLog result(useNewSolver);
2022-12-02 10:46:05 +00:00
for (TxnLog& log : logs)
result.concatAsUnion(std::move(log), NotNull{types});
return result;
}
2023-04-14 13:05:27 +01:00
bool Unifier::occursCheck(TypeId needle, TypeId haystack, bool reversed)
{
2021-12-02 23:20:08 +00:00
sharedState.tempSeenTy.clear();
2023-04-14 13:05:27 +01:00
bool occurs = occursCheck(sharedState.tempSeenTy, needle, haystack);
if (occurs && FFlag::LuauOccursIsntAlwaysFailure)
{
Unifier innerState = makeChildUnifier();
if (const UnionType* ut = get<UnionType>(haystack))
{
if (reversed)
innerState.tryUnifyUnionWithType(haystack, ut, needle);
else
innerState.tryUnifyTypeWithUnion(needle, haystack, ut, /* cacheEnabled = */ false, /* isFunction = */ false);
}
else if (const IntersectionType* it = get<IntersectionType>(haystack))
{
if (reversed)
innerState.tryUnifyIntersectionWithType(haystack, it, needle, /* cacheEnabled = */ false, /* isFunction = */ false);
else
innerState.tryUnifyTypeWithIntersection(needle, haystack, it);
}
else
{
innerState.failure = true;
}
if (innerState.failure)
{
reportError(location, OccursCheckFailed{});
2023-06-24 06:33:44 +01:00
log.replace(needle, BoundType{builtinTypes->errorRecoveryType()});
2023-04-14 13:05:27 +01:00
}
}
return occurs;
}
2022-08-11 21:42:54 +01:00
bool Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack)
{
2022-10-21 18:33:43 +01:00
RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
2022-08-11 21:42:54 +01:00
bool occurrence = false;
2022-01-06 22:10:07 +00:00
auto check = [&](TypeId tv) {
2022-08-11 21:42:54 +01:00
if (occursCheck(seen, needle, tv))
occurrence = true;
2022-01-06 22:10:07 +00:00
};
2022-03-11 16:31:18 +00:00
needle = log.follow(needle);
haystack = log.follow(haystack);
2022-03-11 16:31:18 +00:00
if (seen.find(haystack))
2022-08-11 21:42:54 +01:00
return false;
2022-03-11 16:31:18 +00:00
seen.insert(haystack);
2023-04-07 20:56:27 +01:00
if (log.getMutable<ErrorType>(needle))
2022-08-11 21:42:54 +01:00
return false;
2023-05-12 13:15:01 +01:00
if (!log.getMutable<FreeType>(needle) && !(hideousFixMeGenericsAreActuallyFree && log.is<GenericType>(needle)))
2022-03-11 16:31:18 +00:00
ice("Expected needle to be free");
2022-03-11 16:31:18 +00:00
if (needle == haystack)
{
2023-04-14 13:05:27 +01:00
if (!FFlag::LuauOccursIsntAlwaysFailure)
{
reportError(location, OccursCheckFailed{});
log.replace(needle, *builtinTypes->errorRecoveryType());
}
2022-08-11 21:42:54 +01:00
return true;
2022-03-11 16:31:18 +00:00
}
2022-01-06 22:10:07 +00:00
2023-05-12 13:15:01 +01:00
if (log.getMutable<FreeType>(haystack) || (hideousFixMeGenericsAreActuallyFree && log.is<GenericType>(haystack)))
2022-08-11 21:42:54 +01:00
return false;
2023-01-03 17:33:19 +00:00
else if (auto a = log.getMutable<UnionType>(haystack))
2022-03-11 16:31:18 +00:00
{
for (TypeId ty : a->options)
check(ty);
}
2023-01-03 17:33:19 +00:00
else if (auto a = log.getMutable<IntersectionType>(haystack))
{
2022-03-11 16:31:18 +00:00
for (TypeId ty : a->parts)
check(ty);
}
2022-08-11 21:42:54 +01:00
return occurrence;
}
2023-04-14 13:05:27 +01:00
bool Unifier::occursCheck(TypePackId needle, TypePackId haystack, bool reversed)
{
2021-12-02 23:20:08 +00:00
sharedState.tempSeenTp.clear();
2023-04-14 13:05:27 +01:00
bool occurs = occursCheck(sharedState.tempSeenTp, needle, haystack);
if (occurs && FFlag::LuauOccursIsntAlwaysFailure)
{
reportError(location, OccursCheckFailed{});
log.replace(needle, *builtinTypes->errorRecoveryTypePack());
}
return occurs;
}
2022-08-11 21:42:54 +01:00
bool Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack)
{
2022-03-11 16:31:18 +00:00
needle = log.follow(needle);
haystack = log.follow(haystack);
2022-03-11 16:31:18 +00:00
if (seen.find(haystack))
2022-08-11 21:42:54 +01:00
return false;
2022-03-11 16:31:18 +00:00
seen.insert(haystack);
2023-04-07 20:56:27 +01:00
if (log.getMutable<ErrorTypePack>(needle))
2022-08-11 21:42:54 +01:00
return false;
2023-05-12 13:15:01 +01:00
if (!log.getMutable<FreeTypePack>(needle) && !(hideousFixMeGenericsAreActuallyFree && log.is<GenericTypePack>(needle)))
2022-03-11 16:31:18 +00:00
ice("Expected needle pack to be free");
2022-01-06 22:10:07 +00:00
2022-10-21 18:33:43 +01:00
RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
2022-01-27 21:29:34 +00:00
2023-01-03 17:33:19 +00:00
while (!log.getMutable<ErrorType>(haystack))
2022-01-06 22:10:07 +00:00
{
2022-03-11 16:31:18 +00:00
if (needle == haystack)
{
2023-04-14 13:05:27 +01:00
if (!FFlag::LuauOccursIsntAlwaysFailure)
{
reportError(location, OccursCheckFailed{});
log.replace(needle, *builtinTypes->errorRecoveryTypePack());
}
2022-01-06 22:10:07 +00:00
2022-08-11 21:42:54 +01:00
return true;
2022-03-11 16:31:18 +00:00
}
2022-03-11 16:31:18 +00:00
if (auto a = get<TypePack>(haystack); a && a->tail)
2022-01-06 22:10:07 +00:00
{
2022-03-11 16:31:18 +00:00
haystack = log.follow(*a->tail);
continue;
}
2022-03-11 16:31:18 +00:00
break;
}
2022-08-11 21:42:54 +01:00
return false;
}
Unifier Unifier::makeChildUnifier()
{
2023-05-25 21:46:51 +01:00
Unifier u = Unifier{normalizer, scope, location, variance, &log};
2022-10-07 00:55:58 +01:00
u.normalize = normalize;
2023-03-03 13:45:38 +00:00
u.checkInhabited = checkInhabited;
2023-05-12 13:15:01 +01:00
2023-07-28 12:37:00 +01:00
if (useNewSolver)
u.enableNewSolver();
2023-05-12 13:15:01 +01:00
2022-04-14 22:57:15 +01:00
return u;
}
// A utility function that appends the given error to the unifier's error log.
// This allows setting a breakpoint wherever the unifier reports an error.
2022-11-04 17:02:37 +00:00
//
// Note: report error accepts its arguments by value intentionally to reduce the stack usage of functions which call `reportError`.
void Unifier::reportError(Location location, TypeErrorData data)
{
errors.emplace_back(std::move(location), std::move(data));
2023-03-17 14:59:30 +00:00
failure = true;
2022-11-04 17:02:37 +00:00
}
// A utility function that appends the given error to the unifier's error log.
// This allows setting a breakpoint wherever the unifier reports an error.
//
// Note: to conserve stack space in calling functions it is generally preferred to call `Unifier::reportError(Location location, TypeErrorData data)`
// instead of this method.
2022-04-14 22:57:15 +01:00
void Unifier::reportError(TypeError err)
{
errors.push_back(std::move(err));
2023-03-17 14:59:30 +00:00
failure = true;
}
void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId wantedType, TypeId givenType)
{
if (auto e = hasUnificationTooComplex(innerErrors))
2022-01-27 21:29:34 +00:00
reportError(*e);
else if (!innerErrors.empty())
2022-12-02 10:46:05 +00:00
reportError(location, TypeMismatch{wantedType, givenType, mismatchContext()});
}
2021-11-12 02:12:39 +00:00
void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const std::string& prop, TypeId wantedType, TypeId givenType)
{
if (auto e = hasUnificationTooComplex(innerErrors))
2022-01-27 21:29:34 +00:00
reportError(*e);
2021-11-12 02:12:39 +00:00
else if (!innerErrors.empty())
2022-12-02 10:46:05 +00:00
reportError(TypeError{location,
TypeMismatch{wantedType, givenType, format("Property '%s' is not compatible.", prop.c_str()), innerErrors.front(), mismatchContext()}});
2021-11-12 02:12:39 +00:00
}
void Unifier::ice(const std::string& message, const Location& location)
{
sharedState.iceHandler->ice(message, location);
}
void Unifier::ice(const std::string& message)
{
sharedState.iceHandler->ice(message);
}
} // namespace Luau