2022-06-03 13:32:20 -07:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
#include "Luau/Anyification.h"
|
2022-08-04 14:27:28 -07:00
|
|
|
#include "Luau/ApplyTypeFunction.h"
|
2023-03-24 10:34:14 -07:00
|
|
|
#include "Luau/Common.h"
|
2022-06-03 13:32:20 -07:00
|
|
|
#include "Luau/ConstraintSolver.h"
|
2022-10-21 10:33:43 -07:00
|
|
|
#include "Luau/DcrLogger.h"
|
2022-06-03 13:32:20 -07:00
|
|
|
#include "Luau/Instantiation.h"
|
2024-02-23 10:40:00 -08:00
|
|
|
#include "Luau/Instantiation2.h"
|
2022-06-23 18:44:07 -07:00
|
|
|
#include "Luau/Location.h"
|
2022-09-01 16:00:14 -07:00
|
|
|
#include "Luau/ModuleResolver.h"
|
2024-02-02 10:20:03 -08:00
|
|
|
#include "Luau/OverloadResolution.h"
|
2022-06-03 13:32:20 -07:00
|
|
|
#include "Luau/Quantify.h"
|
2023-05-19 11:59:59 -07:00
|
|
|
#include "Luau/Simplify.h"
|
2023-07-28 14:37:00 +03:00
|
|
|
#include "Luau/TimeTrace.h"
|
2022-06-03 13:32:20 -07:00
|
|
|
#include "Luau/ToString.h"
|
2023-01-03 19:33:19 +02:00
|
|
|
#include "Luau/Type.h"
|
2023-05-19 11:59:59 -07:00
|
|
|
#include "Luau/TypeFamily.h"
|
|
|
|
#include "Luau/TypeUtils.h"
|
2023-08-04 10:01:35 -07:00
|
|
|
#include "Luau/Unifier2.h"
|
2024-01-12 11:16:39 -08:00
|
|
|
#include "Luau/VecDeque.h"
|
2023-01-03 19:33:19 +02:00
|
|
|
#include "Luau/VisitType.h"
|
2023-12-01 18:04:44 -08:00
|
|
|
#include <algorithm>
|
2023-11-10 10:05:48 -08:00
|
|
|
#include <utility>
|
2022-06-03 13:32:20 -07:00
|
|
|
|
|
|
|
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false);
|
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2023-02-24 10:24:22 -08:00
|
|
|
size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const
|
|
|
|
{
|
|
|
|
size_t result = 0;
|
|
|
|
|
|
|
|
if (const TypeId* ty = get_if<TypeId>(&bci))
|
|
|
|
result = std::hash<TypeId>()(*ty);
|
|
|
|
else if (const TypePackId* tp = get_if<TypePackId>(&bci))
|
|
|
|
result = std::hash<TypePackId>()(*tp);
|
|
|
|
else if (Constraint const* const* c = get_if<const Constraint*>(&bci))
|
|
|
|
result = std::hash<const Constraint*>()(*c);
|
|
|
|
else
|
|
|
|
LUAU_ASSERT(!"Should be unreachable");
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-07-28 20:41:13 -07:00
|
|
|
[[maybe_unused]] static void dumpBindings(NotNull<Scope> scope, ToStringOptions& opts)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
|
|
|
for (const auto& [k, v] : scope->bindings)
|
|
|
|
{
|
2022-11-11 00:04:44 +02:00
|
|
|
auto d = toString(v.typeId, opts);
|
|
|
|
printf("\t%s : %s\n", k.c_str(), d.c_str());
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2022-07-28 20:41:13 -07:00
|
|
|
for (NotNull<Scope> child : scope->children)
|
2022-06-03 13:32:20 -07:00
|
|
|
dumpBindings(child, opts);
|
|
|
|
}
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(TypeArena* arena, NotNull<BuiltinTypes> builtinTypes,
|
2022-09-08 14:44:50 -07:00
|
|
|
const TypeFun& fn, const std::vector<TypeId>& rawTypeArguments, const std::vector<TypePackId>& rawPackArguments)
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
|
|
|
std::vector<TypeId> saturatedTypeArguments;
|
|
|
|
std::vector<TypeId> extraTypes;
|
|
|
|
std::vector<TypePackId> saturatedPackArguments;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < rawTypeArguments.size(); ++i)
|
|
|
|
{
|
|
|
|
TypeId ty = rawTypeArguments[i];
|
|
|
|
|
|
|
|
if (i < fn.typeParams.size())
|
|
|
|
saturatedTypeArguments.push_back(ty);
|
|
|
|
else
|
|
|
|
extraTypes.push_back(ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we collected extra types, put them in a type pack now. This case is
|
|
|
|
// mutually exclusive with the type pack -> type conversion we do below:
|
|
|
|
// extraTypes will only have elements in it if we have more types than we
|
|
|
|
// have parameter slots for them to go into.
|
2023-05-19 11:59:59 -07:00
|
|
|
if (!extraTypes.empty() && !fn.typePackParams.empty())
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
|
|
|
saturatedPackArguments.push_back(arena->addTypePack(extraTypes));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < rawPackArguments.size(); ++i)
|
|
|
|
{
|
|
|
|
TypePackId tp = rawPackArguments[i];
|
|
|
|
|
|
|
|
// If we are short on regular type saturatedTypeArguments and we have a single
|
|
|
|
// element type pack, we can decompose that to the type it contains and
|
|
|
|
// use that as a type parameter.
|
|
|
|
if (saturatedTypeArguments.size() < fn.typeParams.size() && size(tp) == 1 && finite(tp) && first(tp) && saturatedPackArguments.empty())
|
|
|
|
{
|
|
|
|
saturatedTypeArguments.push_back(*first(tp));
|
|
|
|
}
|
2023-05-19 11:59:59 -07:00
|
|
|
else if (saturatedPackArguments.size() < fn.typePackParams.size())
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
|
|
|
saturatedPackArguments.push_back(tp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t typesProvided = saturatedTypeArguments.size();
|
|
|
|
size_t typesRequired = fn.typeParams.size();
|
|
|
|
|
|
|
|
size_t packsProvided = saturatedPackArguments.size();
|
|
|
|
size_t packsRequired = fn.typePackParams.size();
|
|
|
|
|
|
|
|
// Extra types should be accumulated in extraTypes, not saturatedTypeArguments. Extra
|
|
|
|
// packs will be accumulated in saturatedPackArguments, so we don't have an
|
|
|
|
// assertion for that.
|
|
|
|
LUAU_ASSERT(typesProvided <= typesRequired);
|
|
|
|
|
|
|
|
// If we didn't provide enough types, but we did provide a type pack, we
|
|
|
|
// don't want to use defaults. The rationale for this is that if the user
|
|
|
|
// provides a pack but doesn't provide enough types, we want to report an
|
|
|
|
// error, rather than simply using the default saturatedTypeArguments, if they exist. If
|
|
|
|
// they did provide enough types, but not enough packs, we of course want to
|
|
|
|
// use the default packs.
|
|
|
|
bool needsDefaults = (typesProvided < typesRequired && packsProvided == 0) || (typesProvided == typesRequired && packsProvided < packsRequired);
|
|
|
|
|
|
|
|
if (needsDefaults)
|
|
|
|
{
|
|
|
|
// Default types can reference earlier types. It's legal to write
|
|
|
|
// something like
|
|
|
|
// type T<A, B = A> = (A, B) -> number
|
|
|
|
// and we need to respect that. We use an ApplyTypeFunction for this.
|
|
|
|
ApplyTypeFunction atf{arena};
|
|
|
|
|
|
|
|
for (size_t i = 0; i < typesProvided; ++i)
|
|
|
|
atf.typeArguments[fn.typeParams[i].ty] = saturatedTypeArguments[i];
|
|
|
|
|
|
|
|
for (size_t i = typesProvided; i < typesRequired; ++i)
|
|
|
|
{
|
|
|
|
TypeId defaultTy = fn.typeParams[i].defaultValue.value_or(nullptr);
|
|
|
|
|
|
|
|
// We will fill this in with the error type later.
|
|
|
|
if (!defaultTy)
|
|
|
|
break;
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
TypeId instantiatedDefault = atf.substitute(defaultTy).value_or(builtinTypes->errorRecoveryType());
|
2022-08-04 14:27:28 -07:00
|
|
|
atf.typeArguments[fn.typeParams[i].ty] = instantiatedDefault;
|
|
|
|
saturatedTypeArguments.push_back(instantiatedDefault);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < packsProvided; ++i)
|
|
|
|
{
|
|
|
|
atf.typePackArguments[fn.typePackParams[i].tp] = saturatedPackArguments[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = packsProvided; i < packsRequired; ++i)
|
|
|
|
{
|
|
|
|
TypePackId defaultTp = fn.typePackParams[i].defaultValue.value_or(nullptr);
|
|
|
|
|
|
|
|
// We will fill this in with the error type pack later.
|
|
|
|
if (!defaultTp)
|
|
|
|
break;
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
TypePackId instantiatedDefault = atf.substitute(defaultTp).value_or(builtinTypes->errorRecoveryTypePack());
|
2022-08-04 14:27:28 -07:00
|
|
|
atf.typePackArguments[fn.typePackParams[i].tp] = instantiatedDefault;
|
|
|
|
saturatedPackArguments.push_back(instantiatedDefault);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we didn't create an extra type pack from overflowing parameter packs,
|
|
|
|
// and we're still missing a type pack, plug in an empty type pack as the
|
|
|
|
// value of the empty packs.
|
|
|
|
if (extraTypes.empty() && saturatedPackArguments.size() + 1 == fn.typePackParams.size())
|
|
|
|
{
|
|
|
|
saturatedPackArguments.push_back(arena->addTypePack({}));
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to have _something_ when we substitute the generic saturatedTypeArguments,
|
|
|
|
// even if they're missing, so we use the error type as a filler.
|
|
|
|
for (size_t i = saturatedTypeArguments.size(); i < typesRequired; ++i)
|
|
|
|
{
|
2023-01-03 19:33:19 +02:00
|
|
|
saturatedTypeArguments.push_back(builtinTypes->errorRecoveryType());
|
2022-08-04 14:27:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = saturatedPackArguments.size(); i < packsRequired; ++i)
|
|
|
|
{
|
2023-01-03 19:33:19 +02:00
|
|
|
saturatedPackArguments.push_back(builtinTypes->errorRecoveryTypePack());
|
2022-08-04 14:27:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// At this point, these two conditions should be true. If they aren't we
|
|
|
|
// will run into access violations.
|
|
|
|
LUAU_ASSERT(saturatedTypeArguments.size() == fn.typeParams.size());
|
|
|
|
LUAU_ASSERT(saturatedPackArguments.size() == fn.typePackParams.size());
|
|
|
|
|
|
|
|
return {saturatedTypeArguments, saturatedPackArguments};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InstantiationSignature::operator==(const InstantiationSignature& rhs) const
|
|
|
|
{
|
|
|
|
return fn == rhs.fn && arguments == rhs.arguments && packArguments == rhs.packArguments;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t HashInstantiationSignature::operator()(const InstantiationSignature& signature) const
|
|
|
|
{
|
|
|
|
size_t hash = std::hash<TypeId>{}(signature.fn.type);
|
|
|
|
for (const GenericTypeDefinition& p : signature.fn.typeParams)
|
|
|
|
{
|
|
|
|
hash ^= (std::hash<TypeId>{}(p.ty) << 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const GenericTypePackDefinition& p : signature.fn.typePackParams)
|
|
|
|
{
|
|
|
|
hash ^= (std::hash<TypePackId>{}(p.tp) << 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const TypeId a : signature.arguments)
|
|
|
|
{
|
|
|
|
hash ^= (std::hash<TypeId>{}(a) << 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const TypePackId a : signature.packArguments)
|
|
|
|
{
|
|
|
|
hash ^= (std::hash<TypePackId>{}(a) << 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
void dump(ConstraintSolver* cs, ToStringOptions& opts)
|
|
|
|
{
|
|
|
|
printf("constraints:\n");
|
2022-08-25 13:55:08 -07:00
|
|
|
for (NotNull<const Constraint> c : cs->unsolvedConstraints)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-08-25 13:55:08 -07:00
|
|
|
auto it = cs->blockedConstraints.find(c);
|
|
|
|
int blockCount = it == cs->blockedConstraints.end() ? 0 : int(it->second);
|
|
|
|
printf("\t%d\t%s\n", blockCount, toString(*c, opts).c_str());
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-12 15:15:01 +03:00
|
|
|
struct InstantiationQueuer : TypeOnceVisitor
|
|
|
|
{
|
|
|
|
ConstraintSolver* solver;
|
|
|
|
NotNull<Scope> scope;
|
|
|
|
Location location;
|
|
|
|
|
|
|
|
explicit InstantiationQueuer(NotNull<Scope> scope, const Location& location, ConstraintSolver* solver)
|
|
|
|
: solver(solver)
|
|
|
|
, scope(scope)
|
|
|
|
, location(location)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(TypeId ty, const PendingExpansionType& petv) override
|
|
|
|
{
|
|
|
|
solver->pushConstraint(scope, location, TypeAliasExpansionConstraint{ty});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(TypeId ty, const TypeFamilyInstanceType& tfit) override
|
|
|
|
{
|
|
|
|
solver->pushConstraint(scope, location, ReduceConstraint{ty});
|
|
|
|
return true;
|
|
|
|
}
|
2023-05-25 23:46:51 +03:00
|
|
|
|
|
|
|
bool visit(TypeId ty, const ClassType& ctv) override
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2023-05-12 15:15:01 +03:00
|
|
|
};
|
|
|
|
|
2022-11-11 00:04:44 +02:00
|
|
|
ConstraintSolver::ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, std::vector<NotNull<Constraint>> constraints,
|
2023-07-28 14:37:00 +03:00
|
|
|
ModuleName moduleName, NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger, TypeCheckLimits limits)
|
2022-10-06 16:55:58 -07:00
|
|
|
: arena(normalizer->arena)
|
2023-01-03 19:33:19 +02:00
|
|
|
, builtinTypes(normalizer->builtinTypes)
|
2022-10-06 16:55:58 -07:00
|
|
|
, normalizer(normalizer)
|
2022-11-11 00:04:44 +02:00
|
|
|
, constraints(std::move(constraints))
|
2022-06-03 13:32:20 -07:00
|
|
|
, rootScope(rootScope)
|
2022-09-01 16:00:14 -07:00
|
|
|
, currentModuleName(std::move(moduleName))
|
|
|
|
, moduleResolver(moduleResolver)
|
|
|
|
, requireCycles(requireCycles)
|
2022-09-08 14:44:50 -07:00
|
|
|
, logger(logger)
|
2023-07-28 14:37:00 +03:00
|
|
|
, limits(std::move(limits))
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-09-01 16:00:14 -07:00
|
|
|
opts.exhaustive = true;
|
|
|
|
|
2022-11-11 00:04:44 +02:00
|
|
|
for (NotNull<Constraint> c : this->constraints)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-06-16 17:54:42 -07:00
|
|
|
unsolvedConstraints.push_back(c);
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2023-10-27 12:33:36 -07:00
|
|
|
// initialize the reference counts for the free types in this constraint.
|
|
|
|
for (auto ty : c->getFreeTypes())
|
|
|
|
{
|
|
|
|
// increment the reference count for `ty`
|
2024-01-26 18:30:40 -08:00
|
|
|
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
|
|
|
|
refCount += 1;
|
2023-10-27 12:33:36 -07:00
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
for (NotNull<const Constraint> dep : c->dependencies)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
|
|
|
block(dep, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-06 16:55:58 -07:00
|
|
|
void ConstraintSolver::randomize(unsigned seed)
|
|
|
|
{
|
2022-10-14 01:59:53 +03:00
|
|
|
if (unsolvedConstraints.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
unsigned int rng = seed;
|
|
|
|
|
|
|
|
for (size_t i = unsolvedConstraints.size() - 1; i > 0; --i)
|
|
|
|
{
|
|
|
|
// Fisher-Yates shuffle
|
|
|
|
size_t j = rng % (i + 1);
|
|
|
|
|
|
|
|
std::swap(unsolvedConstraints[i], unsolvedConstraints[j]);
|
|
|
|
|
|
|
|
// LCG RNG, constants from Numerical Recipes
|
|
|
|
// This may occasionally result in skewed shuffles due to distribution properties, but this is a debugging tool so it should be good enough
|
|
|
|
rng = rng * 1664525 + 1013904223;
|
|
|
|
}
|
2022-10-06 16:55:58 -07:00
|
|
|
}
|
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
void ConstraintSolver::run()
|
|
|
|
{
|
2022-10-14 01:59:53 +03:00
|
|
|
if (isDone())
|
2022-06-03 13:32:20 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (FFlag::DebugLuauLogSolver)
|
|
|
|
{
|
|
|
|
printf("Starting solver\n");
|
|
|
|
dump(this, opts);
|
2022-11-11 00:04:44 +02:00
|
|
|
printf("Bindings:\n");
|
|
|
|
dumpBindings(rootScope, opts);
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
if (logger)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-09-08 14:44:50 -07:00
|
|
|
logger->captureInitialSolverState(rootScope, unsolvedConstraints);
|
2022-06-16 17:54:42 -07:00
|
|
|
}
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
auto runSolverPass = [&](bool force) {
|
|
|
|
bool progress = false;
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
size_t i = 0;
|
|
|
|
while (i < unsolvedConstraints.size())
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-06-16 17:54:42 -07:00
|
|
|
NotNull<const Constraint> c = unsolvedConstraints[i];
|
|
|
|
if (!force && isBlocked(c))
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-06-16 17:54:42 -07:00
|
|
|
++i;
|
2022-06-03 13:32:20 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-07-28 14:37:00 +03:00
|
|
|
if (limits.finishTime && TimeTrace::getClock() > *limits.finishTime)
|
|
|
|
throwTimeLimitError();
|
|
|
|
if (limits.cancellationToken && limits.cancellationToken->requested())
|
|
|
|
throwUserCancelError();
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{};
|
2022-09-08 14:44:50 -07:00
|
|
|
StepSnapshot snapshot;
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
if (logger)
|
2022-06-16 17:54:42 -07:00
|
|
|
{
|
2022-09-08 14:44:50 -07:00
|
|
|
snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints);
|
2022-06-16 17:54:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool success = tryDispatch(c, force);
|
|
|
|
|
|
|
|
progress |= success;
|
2022-06-03 13:32:20 -07:00
|
|
|
|
|
|
|
if (success)
|
|
|
|
{
|
2022-08-25 13:55:08 -07:00
|
|
|
unblock(c);
|
2022-06-16 17:54:42 -07:00
|
|
|
unsolvedConstraints.erase(unsolvedConstraints.begin() + i);
|
|
|
|
|
2023-10-27 12:33:36 -07:00
|
|
|
// decrement the referenced free types for this constraint if we dispatched successfully!
|
|
|
|
for (auto ty : c->getFreeTypes())
|
2024-01-26 18:30:40 -08:00
|
|
|
{
|
|
|
|
// this is a little weird, but because we're only counting free types in subtyping constraints,
|
|
|
|
// some constraints (like unpack) might actually produce _more_ references to a free type.
|
|
|
|
size_t& refCount = unresolvedConstraints[ty];
|
|
|
|
if (refCount > 0)
|
|
|
|
refCount -= 1;
|
|
|
|
}
|
2023-10-27 12:33:36 -07:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
if (logger)
|
2022-06-16 17:54:42 -07:00
|
|
|
{
|
2022-09-08 14:44:50 -07:00
|
|
|
logger->commitStepSnapshot(snapshot);
|
2022-06-16 17:54:42 -07:00
|
|
|
}
|
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
if (FFlag::DebugLuauLogSolver)
|
|
|
|
{
|
2022-06-16 17:54:42 -07:00
|
|
|
if (force)
|
|
|
|
printf("Force ");
|
2022-06-03 13:32:20 -07:00
|
|
|
printf("Dispatched\n\t%s\n", saveMe.c_str());
|
2023-02-24 10:24:22 -08:00
|
|
|
|
|
|
|
if (force)
|
|
|
|
{
|
|
|
|
printf("Blocked on:\n");
|
|
|
|
|
|
|
|
for (const auto& [bci, cv] : blocked)
|
|
|
|
{
|
|
|
|
if (end(cv) == std::find(begin(cv), end(cv), c))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (auto bty = get_if<TypeId>(&bci))
|
|
|
|
printf("\tType %s\n", toString(*bty, opts).c_str());
|
|
|
|
else if (auto btp = get_if<TypePackId>(&bci))
|
|
|
|
printf("\tPack %s\n", toString(*btp, opts).c_str());
|
|
|
|
else if (auto cc = get_if<const Constraint*>(&bci))
|
|
|
|
printf("\tCons %s\n", toString(**cc, opts).c_str());
|
|
|
|
else
|
|
|
|
LUAU_ASSERT(!"Unreachable??");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
dump(this, opts);
|
|
|
|
}
|
|
|
|
}
|
2022-06-16 17:54:42 -07:00
|
|
|
else
|
|
|
|
++i;
|
|
|
|
|
|
|
|
if (force && success)
|
|
|
|
return true;
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
2022-06-16 17:54:42 -07:00
|
|
|
|
|
|
|
return progress;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool progress = false;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
progress = runSolverPass(false);
|
|
|
|
if (!progress)
|
|
|
|
progress |= runSolverPass(true);
|
2022-06-03 13:32:20 -07:00
|
|
|
} while (progress);
|
|
|
|
|
|
|
|
if (FFlag::DebugLuauLogSolver)
|
2022-06-16 17:54:42 -07:00
|
|
|
{
|
2022-06-03 13:32:20 -07:00
|
|
|
dumpBindings(rootScope, opts);
|
2022-06-16 17:54:42 -07:00
|
|
|
}
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
if (logger)
|
2022-06-16 17:54:42 -07:00
|
|
|
{
|
2022-09-08 14:44:50 -07:00
|
|
|
logger->captureFinalSolverState(rootScope, unsolvedConstraints);
|
2022-06-16 17:54:42 -07:00
|
|
|
}
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2022-10-14 01:59:53 +03:00
|
|
|
bool ConstraintSolver::isDone()
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
|
|
|
return unsolvedConstraints.empty();
|
|
|
|
}
|
|
|
|
|
2023-09-15 09:27:45 -07:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
struct TypeAndLocation
|
|
|
|
{
|
|
|
|
TypeId typeId;
|
|
|
|
Location location;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct FreeTypeSearcher : TypeOnceVisitor
|
|
|
|
{
|
2024-01-12 11:16:39 -08:00
|
|
|
VecDeque<TypeAndLocation>* result;
|
2023-09-15 09:27:45 -07:00
|
|
|
Location location;
|
|
|
|
|
2024-01-12 11:16:39 -08:00
|
|
|
FreeTypeSearcher(VecDeque<TypeAndLocation>* result, Location location)
|
2023-09-15 09:27:45 -07:00
|
|
|
: result(result)
|
|
|
|
, location(location)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(TypeId ty, const FreeType&) override
|
|
|
|
{
|
|
|
|
result->push_back({ty, location});
|
|
|
|
return false;
|
|
|
|
}
|
2024-03-01 15:58:44 +02:00
|
|
|
|
|
|
|
bool visit(TypeId, const ClassType&) override
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2023-09-15 09:27:45 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool force)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-06-16 17:54:42 -07:00
|
|
|
if (!force && isBlocked(constraint))
|
2022-06-03 13:32:20 -07:00
|
|
|
return false;
|
|
|
|
|
|
|
|
bool success = false;
|
|
|
|
|
|
|
|
if (auto sc = get<SubtypeConstraint>(*constraint))
|
2022-06-16 17:54:42 -07:00
|
|
|
success = tryDispatch(*sc, constraint, force);
|
2022-06-03 13:32:20 -07:00
|
|
|
else if (auto psc = get<PackSubtypeConstraint>(*constraint))
|
2022-06-16 17:54:42 -07:00
|
|
|
success = tryDispatch(*psc, constraint, force);
|
2022-06-03 13:32:20 -07:00
|
|
|
else if (auto gc = get<GeneralizationConstraint>(*constraint))
|
2022-06-16 17:54:42 -07:00
|
|
|
success = tryDispatch(*gc, constraint, force);
|
2022-06-03 13:32:20 -07:00
|
|
|
else if (auto ic = get<InstantiationConstraint>(*constraint))
|
2022-06-16 17:54:42 -07:00
|
|
|
success = tryDispatch(*ic, constraint, force);
|
2022-09-01 16:00:14 -07:00
|
|
|
else if (auto ic = get<IterableConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*ic, constraint, force);
|
2022-06-23 18:44:07 -07:00
|
|
|
else if (auto nc = get<NameConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*nc, constraint);
|
2022-08-04 14:27:28 -07:00
|
|
|
else if (auto taec = get<TypeAliasExpansionConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*taec, constraint);
|
2022-09-01 16:00:14 -07:00
|
|
|
else if (auto fcc = get<FunctionCallConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*fcc, constraint);
|
2024-01-26 18:30:40 -08:00
|
|
|
else if (auto fcc = get<FunctionCheckConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*fcc, constraint);
|
2022-09-23 11:32:10 -07:00
|
|
|
else if (auto fcc = get<PrimitiveTypeConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*fcc, constraint);
|
|
|
|
else if (auto hpc = get<HasPropConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*hpc, constraint);
|
2022-11-18 10:45:14 -08:00
|
|
|
else if (auto spc = get<SetPropConstraint>(*constraint))
|
2023-01-20 14:02:39 +02:00
|
|
|
success = tryDispatch(*spc, constraint, force);
|
2023-02-24 10:24:22 -08:00
|
|
|
else if (auto spc = get<SetIndexerConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*spc, constraint, force);
|
2022-11-04 10:02:37 -07:00
|
|
|
else if (auto sottc = get<SingletonOrTopTypeConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*sottc, constraint);
|
2023-02-24 10:24:22 -08:00
|
|
|
else if (auto uc = get<UnpackConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*uc, constraint);
|
2023-10-20 13:36:26 -07:00
|
|
|
else if (auto soc = get<SetOpConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*soc, constraint, force);
|
2023-05-12 15:15:01 +03:00
|
|
|
else if (auto rc = get<ReduceConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*rc, constraint, force);
|
|
|
|
else if (auto rpc = get<ReducePackConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*rpc, constraint, force);
|
2024-02-23 10:40:00 -08:00
|
|
|
else if (auto eqc = get<EqualityConstraint>(*constraint))
|
|
|
|
success = tryDispatch(*eqc, constraint, force);
|
2022-06-03 13:32:20 -07:00
|
|
|
else
|
2022-09-23 11:32:10 -07:00
|
|
|
LUAU_ASSERT(false);
|
2022-06-03 13:32:20 -07:00
|
|
|
|
|
|
|
if (success)
|
|
|
|
unblock(constraint);
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const SubtypeConstraint& c, NotNull<const Constraint> constraint, bool force)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-06-16 17:54:42 -07:00
|
|
|
if (isBlocked(c.subType))
|
|
|
|
return block(c.subType, constraint);
|
|
|
|
else if (isBlocked(c.superType))
|
|
|
|
return block(c.superType, constraint);
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, c.subType, c.superType);
|
|
|
|
|
|
|
|
return true;
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-09-01 16:00:14 -07:00
|
|
|
if (isBlocked(c.subPack))
|
|
|
|
return block(c.subPack, constraint);
|
|
|
|
else if (isBlocked(c.superPack))
|
|
|
|
return block(c.superPack, constraint);
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, c.subPack, c.superPack);
|
|
|
|
|
|
|
|
return true;
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint, bool force)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2023-04-28 14:55:55 +03:00
|
|
|
TypeId generalizedType = follow(c.generalizedType);
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
if (isBlocked(c.sourceType))
|
|
|
|
return block(c.sourceType, constraint);
|
2023-04-28 14:55:55 +03:00
|
|
|
else if (get<PendingExpansionType>(generalizedType))
|
|
|
|
return block(generalizedType, constraint);
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2023-08-04 10:01:35 -07:00
|
|
|
std::optional<QuantifierResult> generalized;
|
|
|
|
|
2023-09-22 11:10:49 -07:00
|
|
|
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
2023-08-04 10:01:35 -07:00
|
|
|
|
2023-09-22 11:10:49 -07:00
|
|
|
std::optional<TypeId> generalizedTy = u2.generalize(c.sourceType);
|
2023-08-04 10:01:35 -07:00
|
|
|
if (generalizedTy)
|
|
|
|
generalized = QuantifierResult{*generalizedTy}; // FIXME insertedGenerics and insertedGenericPacks
|
|
|
|
else
|
|
|
|
reportError(CodeTooComplex{}, constraint->location);
|
|
|
|
|
2023-03-10 11:20:04 -08:00
|
|
|
if (generalized)
|
|
|
|
{
|
2023-04-28 14:55:55 +03:00
|
|
|
if (get<BlockedType>(generalizedType))
|
2023-05-12 15:15:01 +03:00
|
|
|
asMutable(generalizedType)->ty.emplace<BoundType>(generalized->result);
|
2023-03-10 11:20:04 -08:00
|
|
|
else
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, generalizedType, generalized->result);
|
2023-05-12 15:15:01 +03:00
|
|
|
|
|
|
|
for (auto [free, gen] : generalized->insertedGenerics.pairings)
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, free, gen);
|
2023-05-12 15:15:01 +03:00
|
|
|
|
|
|
|
for (auto [free, gen] : generalized->insertedGenericPacks.pairings)
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, free, gen);
|
2023-03-10 11:20:04 -08:00
|
|
|
}
|
2022-06-16 17:54:42 -07:00
|
|
|
else
|
2023-03-10 11:20:04 -08:00
|
|
|
{
|
|
|
|
reportError(CodeTooComplex{}, constraint->location);
|
|
|
|
asMutable(c.generalizedType)->ty.emplace<BoundType>(builtinTypes->errorRecoveryType());
|
|
|
|
}
|
2022-06-16 17:54:42 -07:00
|
|
|
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.generalizedType, constraint->location);
|
|
|
|
unblock(c.sourceType, constraint->location);
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
for (TypeId ty : c.interiorTypes)
|
|
|
|
u2.generalize(ty);
|
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const InstantiationConstraint& c, NotNull<const Constraint> constraint, bool force)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-06-16 17:54:42 -07:00
|
|
|
if (isBlocked(c.superType))
|
|
|
|
return block(c.superType, constraint);
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2023-05-19 11:59:59 -07:00
|
|
|
if (!blockOnPendingTypes(c.superType, constraint))
|
2023-05-12 15:15:01 +03:00
|
|
|
return false;
|
|
|
|
|
2023-08-04 10:01:35 -07:00
|
|
|
// TODO childLimit
|
|
|
|
std::optional<TypeId> instantiated = instantiate(builtinTypes, NotNull{arena}, NotNull{&limits}, constraint->scope, c.superType);
|
2022-06-03 13:32:20 -07:00
|
|
|
|
2023-04-28 14:55:55 +03:00
|
|
|
LUAU_ASSERT(get<BlockedType>(c.subType));
|
2023-05-19 11:59:59 -07:00
|
|
|
|
|
|
|
if (!instantiated.has_value())
|
|
|
|
{
|
|
|
|
reportError(UnificationTooComplex{}, constraint->location);
|
|
|
|
|
|
|
|
asMutable(c.subType)->ty.emplace<BoundType>(errorRecoveryType());
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.subType, constraint->location);
|
2023-05-19 11:59:59 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-04-28 14:55:55 +03:00
|
|
|
asMutable(c.subType)->ty.emplace<BoundType>(*instantiated);
|
2022-06-30 16:29:02 -07:00
|
|
|
|
2023-05-12 15:15:01 +03:00
|
|
|
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
|
|
|
|
queuer.traverse(c.subType);
|
|
|
|
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.subType, constraint->location);
|
2022-06-03 13:32:20 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* for .. in loops can play out in a bunch of different ways depending on
|
|
|
|
* the shape of iteratee.
|
|
|
|
*
|
|
|
|
* iteratee might be:
|
|
|
|
* * (nextFn)
|
|
|
|
* * (nextFn, table)
|
|
|
|
* * (nextFn, table, firstIndex)
|
|
|
|
* * table with a metatable and __index
|
|
|
|
* * table with a metatable and __call but no __index (if the metatable has
|
|
|
|
* both, __index takes precedence)
|
|
|
|
* * table with an indexer but no __index or __call (or no metatable)
|
|
|
|
*
|
|
|
|
* To dispatch this constraint, we need first to know enough about iteratee
|
|
|
|
* to figure out which of the above shapes we are actually working with.
|
|
|
|
*
|
|
|
|
* If `force` is true and we still do not know, we must flag a warning. Type
|
|
|
|
* families are the fix for this.
|
|
|
|
*
|
|
|
|
* Since we need to know all of this stuff about the types of the iteratee,
|
|
|
|
* we have no choice but for ConstraintSolver to also be the thing that
|
|
|
|
* applies constraints to the types of the iterators.
|
|
|
|
*/
|
|
|
|
|
|
|
|
auto block_ = [&](auto&& t) {
|
|
|
|
if (force)
|
|
|
|
{
|
|
|
|
// If we haven't figured out the type of the iteratee by now,
|
|
|
|
// there's nothing we can do.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
block(t, constraint);
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2024-03-01 15:58:44 +02:00
|
|
|
TypePack iterator = extendTypePack(*arena, builtinTypes, c.iterator, 3);
|
|
|
|
if (iterator.head.size() < 3 && iterator.tail && isBlocked(*iterator.tail))
|
|
|
|
return block_(*iterator.tail);
|
2022-09-01 16:00:14 -07:00
|
|
|
|
2022-09-15 15:13:58 -07:00
|
|
|
{
|
|
|
|
bool blocked = false;
|
2024-03-01 15:58:44 +02:00
|
|
|
for (TypeId t : iterator.head)
|
2022-09-15 15:13:58 -07:00
|
|
|
{
|
|
|
|
if (isBlocked(t))
|
|
|
|
{
|
|
|
|
block(t, constraint);
|
|
|
|
blocked = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blocked)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-03-01 15:58:44 +02:00
|
|
|
if (0 == iterator.head.size())
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
2024-03-01 15:58:44 +02:00
|
|
|
unify(constraint, builtinTypes->anyTypePack, c.variables);
|
2022-09-01 16:00:14 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-03-01 15:58:44 +02:00
|
|
|
TypeId nextTy = follow(iterator.head[0]);
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<FreeType>(nextTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
return block_(nextTy);
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<FunctionType>(nextTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
2023-01-03 19:33:19 +02:00
|
|
|
TypeId tableTy = builtinTypes->nilType;
|
2024-03-01 15:58:44 +02:00
|
|
|
if (iterator.head.size() >= 2)
|
|
|
|
tableTy = iterator.head[1];
|
2022-09-01 16:00:14 -07:00
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
TypeId firstIndexTy = builtinTypes->nilType;
|
2024-03-01 15:58:44 +02:00
|
|
|
if (iterator.head.size() >= 3)
|
|
|
|
firstIndexTy = iterator.head[2];
|
2022-09-01 16:00:14 -07:00
|
|
|
|
|
|
|
return tryDispatchIterableFunction(nextTy, tableTy, firstIndexTy, c, constraint, force);
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
2024-03-01 15:58:44 +02:00
|
|
|
return tryDispatchIterableTable(iterator.head[0], c, constraint, force);
|
2022-09-01 16:00:14 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-23 18:44:07 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint)
|
|
|
|
{
|
|
|
|
if (isBlocked(c.namedType))
|
|
|
|
return block(c.namedType, constraint);
|
|
|
|
|
|
|
|
TypeId target = follow(c.namedType);
|
2022-08-11 13:42:54 -07:00
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
if (target->persistent || target->owningArena != arena)
|
2022-08-11 13:42:54 -07:00
|
|
|
return true;
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
if (TableType* ttv = getMutable<TableType>(target))
|
2023-01-27 13:28:45 -08:00
|
|
|
{
|
|
|
|
if (c.synthetic && !ttv->name)
|
|
|
|
ttv->syntheticName = c.name;
|
|
|
|
else
|
2023-02-17 16:53:37 +02:00
|
|
|
{
|
2023-01-27 13:28:45 -08:00
|
|
|
ttv->name = c.name;
|
2023-02-17 16:53:37 +02:00
|
|
|
ttv->instantiatedTypeParams = c.typeParameters;
|
|
|
|
ttv->instantiatedTypePackParams = c.typePackParameters;
|
|
|
|
}
|
2023-01-27 13:28:45 -08:00
|
|
|
}
|
2023-01-03 19:33:19 +02:00
|
|
|
else if (MetatableType* mtv = getMutable<MetatableType>(target))
|
2022-06-23 18:44:07 -07:00
|
|
|
mtv->syntheticName = c.name;
|
2023-01-03 19:33:19 +02:00
|
|
|
else if (get<IntersectionType>(target) || get<UnionType>(target))
|
2022-10-28 01:22:49 +03:00
|
|
|
{
|
|
|
|
// nothing (yet)
|
|
|
|
}
|
2022-06-23 18:44:07 -07:00
|
|
|
else
|
|
|
|
return block(c.namedType, constraint);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
struct InfiniteTypeFinder : TypeOnceVisitor
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
|
|
|
ConstraintSolver* solver;
|
|
|
|
const InstantiationSignature& signature;
|
2022-09-01 16:00:14 -07:00
|
|
|
NotNull<Scope> scope;
|
2022-08-04 14:27:28 -07:00
|
|
|
bool foundInfiniteType = false;
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
explicit InfiniteTypeFinder(ConstraintSolver* solver, const InstantiationSignature& signature, NotNull<Scope> scope)
|
2022-08-04 14:27:28 -07:00
|
|
|
: solver(solver)
|
|
|
|
, signature(signature)
|
2022-09-01 16:00:14 -07:00
|
|
|
, scope(scope)
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
bool visit(TypeId ty, const PendingExpansionType& petv) override
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
2022-09-01 16:00:14 -07:00
|
|
|
std::optional<TypeFun> tf =
|
|
|
|
(petv.prefix) ? scope->lookupImportedType(petv.prefix->value, petv.name.value) : scope->lookupType(petv.name.value);
|
|
|
|
|
|
|
|
if (!tf.has_value())
|
|
|
|
return true;
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
auto [typeArguments, packArguments] = saturateArguments(solver->arena, solver->builtinTypes, *tf, petv.typeArguments, petv.packArguments);
|
2022-08-04 14:27:28 -07:00
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
if (follow(tf->type) == follow(signature.fn.type) && (signature.arguments != typeArguments || signature.packArguments != packArguments))
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
|
|
|
foundInfiniteType = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint)
|
|
|
|
{
|
2023-01-03 19:33:19 +02:00
|
|
|
const PendingExpansionType* petv = get<PendingExpansionType>(follow(c.target));
|
2022-08-04 14:27:28 -07:00
|
|
|
if (!petv)
|
|
|
|
{
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.target, constraint->location);
|
2022-08-04 14:27:28 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-06-16 10:01:18 -07:00
|
|
|
auto bindResult = [this, &c, constraint](TypeId result) {
|
2023-04-28 14:55:55 +03:00
|
|
|
LUAU_ASSERT(get<PendingExpansionType>(c.target));
|
2023-01-03 19:33:19 +02:00
|
|
|
asMutable(c.target)->ty.emplace<BoundType>(result);
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.target, constraint->location);
|
2022-08-04 14:27:28 -07:00
|
|
|
};
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
std::optional<TypeFun> tf = (petv->prefix) ? constraint->scope->lookupImportedType(petv->prefix->value, petv->name.value)
|
|
|
|
: constraint->scope->lookupType(petv->name.value);
|
|
|
|
|
|
|
|
if (!tf.has_value())
|
|
|
|
{
|
|
|
|
reportError(UnknownSymbol{petv->name.value, UnknownSymbol::Context::Type}, constraint->location);
|
2022-09-15 15:13:58 -07:00
|
|
|
bindResult(errorRecoveryType());
|
2022-09-01 16:00:14 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-08-04 14:27:28 -07:00
|
|
|
// If there are no parameters to the type function we can just use the type
|
|
|
|
// directly.
|
2022-09-01 16:00:14 -07:00
|
|
|
if (tf->typeParams.empty() && tf->typePackParams.empty())
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
2022-09-01 16:00:14 -07:00
|
|
|
bindResult(tf->type);
|
2022-08-04 14:27:28 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
auto [typeArguments, packArguments] = saturateArguments(arena, builtinTypes, *tf, petv->typeArguments, petv->packArguments);
|
2022-08-04 14:27:28 -07:00
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
bool sameTypes = std::equal(typeArguments.begin(), typeArguments.end(), tf->typeParams.begin(), tf->typeParams.end(), [](auto&& itp, auto&& p) {
|
|
|
|
return itp == p.ty;
|
|
|
|
});
|
2022-08-04 14:27:28 -07:00
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
bool samePacks =
|
|
|
|
std::equal(packArguments.begin(), packArguments.end(), tf->typePackParams.begin(), tf->typePackParams.end(), [](auto&& itp, auto&& p) {
|
2022-08-04 14:27:28 -07:00
|
|
|
return itp == p.tp;
|
|
|
|
});
|
|
|
|
|
|
|
|
// If we're instantiating the type with its generic saturatedTypeArguments we are
|
|
|
|
// performing the identity substitution. We can just short-circuit and bind
|
|
|
|
// to the TypeFun's type.
|
|
|
|
if (sameTypes && samePacks)
|
|
|
|
{
|
2022-09-01 16:00:14 -07:00
|
|
|
bindResult(tf->type);
|
2022-08-04 14:27:28 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
InstantiationSignature signature{
|
2022-09-01 16:00:14 -07:00
|
|
|
*tf,
|
2022-08-04 14:27:28 -07:00
|
|
|
typeArguments,
|
|
|
|
packArguments,
|
|
|
|
};
|
|
|
|
|
|
|
|
// If we use the same signature, we don't need to bother trying to
|
|
|
|
// instantiate the alias again, since the instantiation should be
|
|
|
|
// deterministic.
|
|
|
|
if (TypeId* cached = instantiatedAliases.find(signature))
|
|
|
|
{
|
|
|
|
bindResult(*cached);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In order to prevent infinite types from being expanded and causing us to
|
|
|
|
// cycle infinitely, we need to scan the type function for cases where we
|
|
|
|
// expand the same alias with different type saturatedTypeArguments. See
|
2023-12-15 12:52:08 -08:00
|
|
|
// https://github.com/luau-lang/luau/pull/68 for the RFC responsible for
|
|
|
|
// this. This is a little nicer than using a recursion limit because we can
|
|
|
|
// catch the infinite expansion before actually trying to expand it.
|
2022-09-01 16:00:14 -07:00
|
|
|
InfiniteTypeFinder itf{this, signature, constraint->scope};
|
|
|
|
itf.traverse(tf->type);
|
2022-08-04 14:27:28 -07:00
|
|
|
|
|
|
|
if (itf.foundInfiniteType)
|
|
|
|
{
|
|
|
|
// TODO (CLI-56761): Report an error.
|
2022-09-15 15:13:58 -07:00
|
|
|
bindResult(errorRecoveryType());
|
2023-02-17 16:53:37 +02:00
|
|
|
reportError(GenericError{"Recursive type being used with different parameters"}, constraint->location);
|
2022-08-04 14:27:28 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ApplyTypeFunction applyTypeFunction{arena};
|
|
|
|
for (size_t i = 0; i < typeArguments.size(); ++i)
|
|
|
|
{
|
2022-09-01 16:00:14 -07:00
|
|
|
applyTypeFunction.typeArguments[tf->typeParams[i].ty] = typeArguments[i];
|
2022-08-04 14:27:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < packArguments.size(); ++i)
|
|
|
|
{
|
2022-09-01 16:00:14 -07:00
|
|
|
applyTypeFunction.typePackArguments[tf->typePackParams[i].tp] = packArguments[i];
|
2022-08-04 14:27:28 -07:00
|
|
|
}
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
std::optional<TypeId> maybeInstantiated = applyTypeFunction.substitute(tf->type);
|
2022-08-04 14:27:28 -07:00
|
|
|
// Note that ApplyTypeFunction::encounteredForwardedType is never set in
|
|
|
|
// DCR, because we do not use free types for forward-declared generic
|
|
|
|
// aliases.
|
|
|
|
|
|
|
|
if (!maybeInstantiated.has_value())
|
|
|
|
{
|
|
|
|
// TODO (CLI-56761): Report an error.
|
2022-09-15 15:13:58 -07:00
|
|
|
bindResult(errorRecoveryType());
|
2022-08-04 14:27:28 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeId instantiated = *maybeInstantiated;
|
|
|
|
TypeId target = follow(instantiated);
|
2022-08-11 13:42:54 -07:00
|
|
|
|
2023-01-20 14:02:39 +02:00
|
|
|
// The application is not recursive, so we need to queue up application of
|
|
|
|
// any child type function instantiations within the result in order for it
|
|
|
|
// to be complete.
|
|
|
|
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
|
|
|
|
queuer.traverse(target);
|
|
|
|
|
2023-03-31 15:21:14 +03:00
|
|
|
if (target->persistent || target->owningArena != arena)
|
2023-01-20 14:02:39 +02:00
|
|
|
{
|
|
|
|
bindResult(target);
|
2022-08-11 13:42:54 -07:00
|
|
|
return true;
|
2023-01-20 14:02:39 +02:00
|
|
|
}
|
2022-08-11 13:42:54 -07:00
|
|
|
|
2022-08-04 14:27:28 -07:00
|
|
|
// Type function application will happily give us the exact same type if
|
|
|
|
// there are e.g. generic saturatedTypeArguments that go unused.
|
2022-09-01 16:00:14 -07:00
|
|
|
bool needsClone = follow(tf->type) == target;
|
2022-08-04 14:27:28 -07:00
|
|
|
// Only tables have the properties we're trying to set.
|
2023-01-03 19:33:19 +02:00
|
|
|
TableType* ttv = getMutableTableType(target);
|
2022-08-04 14:27:28 -07:00
|
|
|
|
|
|
|
if (ttv)
|
|
|
|
{
|
|
|
|
if (needsClone)
|
|
|
|
{
|
|
|
|
// Substitution::clone is a shallow clone. If this is a
|
|
|
|
// metatable type, we want to mutate its table, so we need to
|
|
|
|
// explicitly clone that table as well. If we don't, we will
|
|
|
|
// mutate another module's type surface and cause a
|
|
|
|
// use-after-free.
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<MetatableType>(target))
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
|
|
|
instantiated = applyTypeFunction.clone(target);
|
2023-01-03 19:33:19 +02:00
|
|
|
MetatableType* mtv = getMutable<MetatableType>(instantiated);
|
2022-08-04 14:27:28 -07:00
|
|
|
mtv->table = applyTypeFunction.clone(mtv->table);
|
2023-01-03 19:33:19 +02:00
|
|
|
ttv = getMutable<TableType>(mtv->table);
|
2022-08-04 14:27:28 -07:00
|
|
|
}
|
2023-01-03 19:33:19 +02:00
|
|
|
else if (get<TableType>(target))
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
|
|
|
instantiated = applyTypeFunction.clone(target);
|
2023-01-03 19:33:19 +02:00
|
|
|
ttv = getMutable<TableType>(instantiated);
|
2022-08-04 14:27:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
target = follow(instantiated);
|
|
|
|
}
|
|
|
|
|
|
|
|
ttv->instantiatedTypeParams = typeArguments;
|
|
|
|
ttv->instantiatedTypePackParams = packArguments;
|
|
|
|
// TODO: Fill in definitionModuleName.
|
|
|
|
}
|
|
|
|
|
|
|
|
bindResult(target);
|
|
|
|
|
|
|
|
instantiatedAliases[signature] = target;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint)
|
|
|
|
{
|
|
|
|
TypeId fn = follow(c.fn);
|
2023-02-17 16:53:37 +02:00
|
|
|
TypePackId argsPack = follow(c.argsPack);
|
2022-09-01 16:00:14 -07:00
|
|
|
TypePackId result = follow(c.result);
|
|
|
|
|
2023-09-22 11:10:49 -07:00
|
|
|
if (isBlocked(fn) || hasUnresolvedConstraints(fn))
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
|
|
|
return block(c.fn, constraint);
|
|
|
|
}
|
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
// if we're calling an error type, the result is an error type, and that's that.
|
|
|
|
if (get<ErrorType>(fn))
|
|
|
|
{
|
|
|
|
asMutable(c.result)->ty.emplace<BoundTypePack>(builtinTypes->errorTypePack);
|
|
|
|
unblock(c.result, constraint->location);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
auto [argsHead, argsTail] = flatten(argsPack);
|
|
|
|
|
|
|
|
bool blocked = false;
|
|
|
|
for (TypeId t : argsHead)
|
|
|
|
{
|
|
|
|
if (isBlocked(t))
|
|
|
|
{
|
|
|
|
block(t, constraint);
|
|
|
|
blocked = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argsTail && isBlocked(*argsTail))
|
|
|
|
{
|
|
|
|
block(*argsTail, constraint);
|
|
|
|
blocked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blocked)
|
|
|
|
return false;
|
|
|
|
|
2023-02-10 10:50:54 -08:00
|
|
|
auto collapse = [](const auto* t) -> std::optional<TypeId> {
|
|
|
|
auto it = begin(t);
|
|
|
|
auto endIt = end(t);
|
|
|
|
|
|
|
|
LUAU_ASSERT(it != endIt);
|
|
|
|
TypeId fst = follow(*it);
|
|
|
|
while (it != endIt)
|
|
|
|
{
|
|
|
|
if (follow(*it) != fst)
|
|
|
|
return std::nullopt;
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fst;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Sometimes the `fn` type is a union/intersection, but whose constituents are all the same pointer.
|
|
|
|
if (auto ut = get<UnionType>(fn))
|
|
|
|
fn = collapse(ut).value_or(fn);
|
|
|
|
else if (auto it = get<IntersectionType>(fn))
|
|
|
|
fn = collapse(it).value_or(fn);
|
|
|
|
|
2022-10-21 10:33:43 -07:00
|
|
|
// We don't support magic __call metamethods.
|
2023-01-03 19:33:19 +02:00
|
|
|
if (std::optional<TypeId> callMm = findMetatableEntry(builtinTypes, errors, fn, "__call", constraint->location))
|
2022-10-21 10:33:43 -07:00
|
|
|
{
|
2023-11-17 10:15:31 -08:00
|
|
|
argsHead.insert(argsHead.begin(), fn);
|
2022-10-21 10:33:43 -07:00
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
if (argsTail && isBlocked(*argsTail))
|
|
|
|
return block(*argsTail, constraint);
|
|
|
|
|
|
|
|
argsPack = arena->addTypePack(TypePack{std::move(argsHead), argsTail});
|
2023-10-06 10:31:16 -07:00
|
|
|
fn = follow(*callMm);
|
2022-10-21 10:33:43 -07:00
|
|
|
asMutable(c.result)->ty.emplace<FreeTypePack>(constraint->scope);
|
|
|
|
}
|
2023-02-10 10:50:54 -08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
const FunctionType* ftv = get<FunctionType>(fn);
|
|
|
|
bool usedMagic = false;
|
|
|
|
|
|
|
|
if (ftv)
|
|
|
|
{
|
|
|
|
if (ftv->dcrMagicFunction)
|
|
|
|
usedMagic = ftv->dcrMagicFunction(MagicFunctionCallContext{NotNull(this), c.callSite, c.argsPack, result});
|
2022-10-21 10:33:43 -07:00
|
|
|
|
2023-02-10 10:50:54 -08:00
|
|
|
if (ftv->dcrMagicRefinement)
|
|
|
|
ftv->dcrMagicRefinement(MagicRefinementContext{constraint->scope, c.callSite, c.discriminantTypes});
|
|
|
|
}
|
2022-09-01 16:00:14 -07:00
|
|
|
|
2023-02-17 16:53:37 +02:00
|
|
|
if (!usedMagic)
|
2023-02-10 10:50:54 -08:00
|
|
|
asMutable(c.result)->ty.emplace<FreeTypePack>(constraint->scope);
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
|
|
|
|
2023-02-10 10:50:54 -08:00
|
|
|
for (std::optional<TypeId> ty : c.discriminantTypes)
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
2023-02-10 10:50:54 -08:00
|
|
|
if (!ty || !isBlocked(*ty))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// We use `any` here because the discriminant type may be pointed at by both branches,
|
|
|
|
// where the discriminant type is not negated, and the other where it is negated, i.e.
|
|
|
|
// `unknown ~ unknown` and `~unknown ~ never`, so `T & unknown ~ T` and `T & ~unknown ~ never`
|
|
|
|
// v.s.
|
|
|
|
// `any ~ any` and `~any ~ any`, so `T & any ~ T` and `T & ~any ~ T`
|
2022-09-15 15:13:58 -07:00
|
|
|
//
|
2023-02-10 10:50:54 -08:00
|
|
|
// In practice, users cannot negate `any`, so this is an implementation detail we can always change.
|
|
|
|
*asMutable(follow(*ty)) = BoundType{builtinTypes->anyType};
|
2022-09-15 15:13:58 -07:00
|
|
|
}
|
2023-02-10 10:50:54 -08:00
|
|
|
|
2024-02-02 10:20:03 -08:00
|
|
|
OverloadResolver resolver{
|
|
|
|
builtinTypes, NotNull{arena}, normalizer, constraint->scope, NotNull{&iceReporter}, NotNull{&limits}, c.callSite->location};
|
|
|
|
auto [status, overload] = resolver.selectOverload(fn, argsPack);
|
|
|
|
TypeId overloadToUse = fn;
|
|
|
|
if (status == OverloadResolver::Analysis::Ok)
|
|
|
|
overloadToUse = overload;
|
|
|
|
|
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
TypeId inferredTy = arena->addType(FunctionType{TypeLevel{}, constraint->scope.get(), argsPack, c.result});
|
|
|
|
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
|
|
|
|
2024-02-02 10:20:03 -08:00
|
|
|
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
|
2024-01-26 18:30:40 -08:00
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty())
|
|
|
|
{
|
|
|
|
Instantiation2 instantiation{arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions)};
|
|
|
|
|
|
|
|
std::optional<TypePackId> subst = instantiation.substitute(result);
|
|
|
|
|
|
|
|
if (!subst)
|
|
|
|
{
|
|
|
|
reportError(CodeTooComplex{}, constraint->location);
|
|
|
|
result = builtinTypes->errorTypePack;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = *subst;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c.result != result)
|
|
|
|
asMutable(c.result)->ty.emplace<BoundTypePack>(result);
|
|
|
|
}
|
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
for (const auto& [expanded, additions] : u2.expandedFreeTypes)
|
|
|
|
{
|
|
|
|
for (TypeId addition : additions)
|
|
|
|
upperBoundContributors[expanded].push_back(std::make_pair(constraint->location, addition));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (occursCheckPassed && c.callSite)
|
|
|
|
(*c.astOverloadResolvedTypes)[c.callSite] = inferredTy;
|
|
|
|
|
|
|
|
unblock(c.result, constraint->location);
|
|
|
|
|
|
|
|
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
|
2024-02-02 10:20:03 -08:00
|
|
|
queuer.traverse(overloadToUse);
|
2024-01-26 18:30:40 -08:00
|
|
|
queuer.traverse(inferredTy);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint)
|
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
TypeId fn = follow(c.fn);
|
2024-01-26 18:30:40 -08:00
|
|
|
const TypePackId argsPack = follow(c.argsPack);
|
|
|
|
|
|
|
|
if (isBlocked(fn))
|
|
|
|
return block(fn, constraint);
|
|
|
|
|
|
|
|
if (isBlocked(argsPack))
|
2024-02-09 09:32:52 -08:00
|
|
|
return true;
|
2024-01-26 18:30:40 -08:00
|
|
|
|
2023-09-29 17:22:06 -07:00
|
|
|
// We know the type of the function and the arguments it expects to receive.
|
|
|
|
// We also know the TypeIds of the actual arguments that will be passed.
|
|
|
|
//
|
|
|
|
// Bidirectional type checking: Force those TypeIds to be the expected
|
|
|
|
// arguments. If something is incoherent, we'll spot it in type checking.
|
|
|
|
//
|
|
|
|
// Most important detail: If a function argument is a lambda, we also want
|
|
|
|
// to force unannotated argument types of that lambda to be the expected
|
|
|
|
// types.
|
|
|
|
|
|
|
|
// FIXME: Bidirectional type checking of overloaded functions is not yet supported.
|
2024-02-09 09:32:52 -08:00
|
|
|
const FunctionType* ftv = get<FunctionType>(fn);
|
|
|
|
if (!ftv)
|
|
|
|
return true;
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
DenseHashMap<TypeId, TypeId> replacements{nullptr};
|
|
|
|
DenseHashMap<TypePackId, TypePackId> replacementPacks{nullptr};
|
|
|
|
|
|
|
|
for (auto generic : ftv->generics)
|
|
|
|
replacements[generic] = builtinTypes->unknownType;
|
|
|
|
|
|
|
|
for (auto genericPack : ftv->genericPacks)
|
|
|
|
replacementPacks[genericPack] = builtinTypes->unknownTypePack;
|
|
|
|
|
|
|
|
// If the type of the function has generics, we don't actually want to push any of the generics themselves
|
|
|
|
// into the argument types as expected types because this creates an unnecessary loop. Instead, we want to
|
|
|
|
// replace these types with `unknown` (and `...unknown`) to keep any structure but not create the cycle.
|
|
|
|
if (!replacements.empty() || !replacementPacks.empty())
|
|
|
|
{
|
|
|
|
Replacer replacer{arena, std::move(replacements), std::move(replacementPacks)};
|
|
|
|
|
|
|
|
std::optional<TypeId> res = replacer.substitute(fn);
|
|
|
|
if (res)
|
|
|
|
{
|
|
|
|
fn = *res;
|
|
|
|
ftv = get<FunctionType>(*res);
|
|
|
|
LUAU_ASSERT(ftv);
|
|
|
|
|
|
|
|
// we've potentially copied type families here, so we need to reproduce their reduce constraint.
|
|
|
|
reproduceConstraints(constraint->scope, constraint->location, replacer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-02-09 09:32:52 -08:00
|
|
|
const std::vector<TypeId> expectedArgs = flatten(ftv->argTypes).first;
|
|
|
|
const std::vector<TypeId> argPackHead = flatten(argsPack).first;
|
|
|
|
|
2024-03-01 15:58:44 +02:00
|
|
|
// If this is a self call, the types will have more elements than the AST call.
|
|
|
|
// We don't attempt to perform bidirectional inference on the self type.
|
|
|
|
const size_t typeOffset = c.callSite->self ? 1 : 0;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < c.callSite->args.size && i + typeOffset < expectedArgs.size() && i + typeOffset < argPackHead.size(); ++i)
|
2023-09-29 17:22:06 -07:00
|
|
|
{
|
2024-03-01 15:58:44 +02:00
|
|
|
const TypeId expectedArgTy = follow(expectedArgs[i + typeOffset]);
|
|
|
|
const TypeId actualArgTy = follow(argPackHead[i + typeOffset]);
|
2024-02-09 09:32:52 -08:00
|
|
|
const AstExpr* expr = c.callSite->args.data[i];
|
2023-09-29 17:22:06 -07:00
|
|
|
|
2024-02-09 09:32:52 -08:00
|
|
|
(*c.astExpectedTypes)[expr] = expectedArgTy;
|
2024-01-26 18:30:40 -08:00
|
|
|
|
2024-02-09 09:32:52 -08:00
|
|
|
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
|
|
|
|
const FunctionType* lambdaTy = get<FunctionType>(actualArgTy);
|
|
|
|
const AstExprFunction* lambdaExpr = expr->as<AstExprFunction>();
|
2023-09-29 17:22:06 -07:00
|
|
|
|
2024-02-09 09:32:52 -08:00
|
|
|
if (expectedLambdaTy && lambdaTy && lambdaExpr)
|
|
|
|
{
|
|
|
|
const std::vector<TypeId> expectedLambdaArgTys = flatten(expectedLambdaTy->argTypes).first;
|
|
|
|
const std::vector<TypeId> lambdaArgTys = flatten(lambdaTy->argTypes).first;
|
2023-09-29 17:22:06 -07:00
|
|
|
|
2024-02-09 09:32:52 -08:00
|
|
|
for (size_t j = 0; j < expectedLambdaArgTys.size() && j < lambdaArgTys.size() && j < lambdaExpr->args.size; ++j)
|
|
|
|
{
|
|
|
|
if (!lambdaExpr->args.data[j]->annotation && get<FreeType>(follow(lambdaArgTys[j])))
|
2023-09-29 17:22:06 -07:00
|
|
|
{
|
2024-02-09 09:32:52 -08:00
|
|
|
asMutable(lambdaArgTys[j])->ty.emplace<BoundType>(expectedLambdaArgTys[j]);
|
2023-09-29 17:22:06 -07:00
|
|
|
}
|
|
|
|
}
|
2024-02-09 09:32:52 -08:00
|
|
|
}
|
2024-02-16 03:25:31 +02:00
|
|
|
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() ||
|
|
|
|
expr->is<AstExprConstantNil>() || expr->is<AstExprTable>())
|
2024-02-09 09:32:52 -08:00
|
|
|
{
|
|
|
|
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
|
|
|
u2.unify(actualArgTy, expectedArgTy);
|
2023-09-29 17:22:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-23 11:32:10 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint)
|
|
|
|
{
|
2024-01-26 18:30:40 -08:00
|
|
|
std::optional<TypeId> expectedType = c.expectedType ? std::make_optional<TypeId>(follow(*c.expectedType)) : std::nullopt;
|
|
|
|
if (expectedType && (isBlocked(*expectedType) || get<PendingExpansionType>(*expectedType)))
|
|
|
|
return block(*expectedType, constraint);
|
2022-09-23 11:32:10 -07:00
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
const FreeType* freeType = get<FreeType>(follow(c.freeType));
|
2023-04-28 14:55:55 +03:00
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
// if this is no longer a free type, then we're done.
|
|
|
|
if (!freeType)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// We will wait if there are any other references to the free type mentioned here.
|
|
|
|
// This is probably the only thing that makes this not insane to do.
|
|
|
|
if (auto refCount = unresolvedConstraints.find(c.freeType); refCount && *refCount > 1)
|
|
|
|
{
|
|
|
|
block(c.freeType, constraint);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeId bindTo = c.primitiveType;
|
|
|
|
|
|
|
|
if (freeType->upperBound != c.primitiveType && maybeSingleton(freeType->upperBound))
|
|
|
|
bindTo = freeType->lowerBound;
|
|
|
|
else if (expectedType && maybeSingleton(*expectedType))
|
|
|
|
bindTo = freeType->lowerBound;
|
|
|
|
|
|
|
|
asMutable(c.freeType)->ty.emplace<BoundType>(bindTo);
|
2022-09-23 11:32:10 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConstraintSolver::tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint)
|
|
|
|
{
|
2023-11-17 10:15:31 -08:00
|
|
|
const TypeId subjectType = follow(c.subjectType);
|
|
|
|
const TypeId resultType = follow(c.resultType);
|
2022-09-23 11:32:10 -07:00
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
LUAU_ASSERT(get<BlockedType>(resultType));
|
2023-04-28 14:55:55 +03:00
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
if (isBlocked(subjectType) || get<PendingExpansionType>(subjectType) || get<TypeFamilyInstanceType>(subjectType))
|
2022-09-23 11:32:10 -07:00
|
|
|
return block(subjectType, constraint);
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
auto [blocked, result] = lookupTableProp(subjectType, c.prop, c.context, c.suppressSimplification);
|
2023-03-03 15:45:38 +02:00
|
|
|
if (!blocked.empty())
|
2022-12-02 12:46:05 +02:00
|
|
|
{
|
2023-03-03 15:45:38 +02:00
|
|
|
for (TypeId blocked : blocked)
|
|
|
|
block(blocked, constraint);
|
2022-09-23 11:32:10 -07:00
|
|
|
|
2022-11-18 10:45:14 -08:00
|
|
|
return false;
|
|
|
|
}
|
2022-09-23 11:32:10 -07:00
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
bindBlockedType(resultType, result.value_or(builtinTypes->anyType), c.subjectType, constraint->location);
|
|
|
|
unblock(resultType, constraint->location);
|
2022-11-18 10:45:14 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isUnsealedTable(TypeId ty)
|
|
|
|
{
|
|
|
|
ty = follow(ty);
|
2023-01-03 19:33:19 +02:00
|
|
|
const TableType* ttv = get<TableType>(ty);
|
2022-11-18 10:45:14 -08:00
|
|
|
return ttv && ttv->state == TableState::Unsealed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-03-24 10:34:14 -07:00
|
|
|
* Given a path into a set of nested unsealed tables `ty`, insert a new property `replaceTy` as the leaf-most property.
|
2022-11-18 10:45:14 -08:00
|
|
|
*
|
2023-03-24 10:34:14 -07:00
|
|
|
* Fails and does nothing if every table along the way is not unsealed.
|
2022-11-18 10:45:14 -08:00
|
|
|
*
|
2023-03-24 10:34:14 -07:00
|
|
|
* Mutates the innermost table type in-place.
|
2022-11-18 10:45:14 -08:00
|
|
|
*/
|
2023-03-24 10:34:14 -07:00
|
|
|
static void updateTheTableType(
|
|
|
|
NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId ty, const std::vector<std::string>& path, TypeId replaceTy)
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
|
|
|
if (path.empty())
|
2023-03-24 10:34:14 -07:00
|
|
|
return;
|
2022-11-18 10:45:14 -08:00
|
|
|
|
|
|
|
// First walk the path and ensure that it's unsealed tables all the way
|
|
|
|
// to the end.
|
|
|
|
{
|
|
|
|
TypeId t = ty;
|
|
|
|
for (size_t i = 0; i < path.size() - 1; ++i)
|
2022-09-23 11:32:10 -07:00
|
|
|
{
|
2022-11-18 10:45:14 -08:00
|
|
|
if (!isUnsealedTable(t))
|
2023-03-24 10:34:14 -07:00
|
|
|
return;
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
const TableType* tbl = get<TableType>(t);
|
2022-11-18 10:45:14 -08:00
|
|
|
auto it = tbl->props.find(path[i]);
|
|
|
|
if (it == tbl->props.end())
|
2023-03-24 10:34:14 -07:00
|
|
|
return;
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-04-28 14:55:55 +03:00
|
|
|
t = follow(it->second.type());
|
2022-09-23 11:32:10 -07:00
|
|
|
}
|
|
|
|
|
2022-11-18 10:45:14 -08:00
|
|
|
// The last path segment should not be a property of the table at all.
|
|
|
|
// We are not changing property types. We are only admitting this one
|
|
|
|
// new property to be appended.
|
|
|
|
if (!isUnsealedTable(t))
|
2023-03-24 10:34:14 -07:00
|
|
|
return;
|
2023-01-03 19:33:19 +02:00
|
|
|
const TableType* tbl = get<TableType>(t);
|
2022-11-18 10:45:14 -08:00
|
|
|
if (0 != tbl->props.count(path.back()))
|
2023-03-24 10:34:14 -07:00
|
|
|
return;
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
2022-09-23 11:32:10 -07:00
|
|
|
|
2023-03-24 10:34:14 -07:00
|
|
|
TypeId t = ty;
|
|
|
|
ErrorVec dummy;
|
2022-11-18 10:45:14 -08:00
|
|
|
|
|
|
|
for (size_t i = 0; i < path.size() - 1; ++i)
|
2022-09-23 11:32:10 -07:00
|
|
|
{
|
2023-07-28 14:37:00 +03:00
|
|
|
t = follow(t);
|
2024-02-23 10:40:00 -08:00
|
|
|
auto propTy = findTablePropertyRespectingMeta(builtinTypes, dummy, t, path[i], ValueContext::LValue, Location{});
|
2023-03-24 10:34:14 -07:00
|
|
|
dummy.clear();
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-03-24 10:34:14 -07:00
|
|
|
if (!propTy)
|
|
|
|
return;
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-03-24 10:34:14 -07:00
|
|
|
t = *propTy;
|
2022-09-23 11:32:10 -07:00
|
|
|
}
|
|
|
|
|
2023-03-24 10:34:14 -07:00
|
|
|
const std::string& lastSegment = path.back();
|
|
|
|
|
|
|
|
t = follow(t);
|
|
|
|
TableType* tt = getMutable<TableType>(t);
|
|
|
|
if (auto mt = get<MetatableType>(t))
|
|
|
|
tt = getMutable<TableType>(mt->table);
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-03-24 10:34:14 -07:00
|
|
|
if (!tt)
|
|
|
|
return;
|
|
|
|
|
2023-04-28 14:55:55 +03:00
|
|
|
tt->props[lastSegment].setType(replaceTy);
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
|
|
|
|
2023-01-20 14:02:39 +02:00
|
|
|
bool ConstraintSolver::tryDispatch(const SetPropConstraint& c, NotNull<const Constraint> constraint, bool force)
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
|
|
|
TypeId subjectType = follow(c.subjectType);
|
2024-02-23 10:40:00 -08:00
|
|
|
const TypeId propType = follow(c.propType);
|
2022-11-18 10:45:14 -08:00
|
|
|
|
|
|
|
if (isBlocked(subjectType))
|
|
|
|
return block(subjectType, constraint);
|
|
|
|
|
|
|
|
std::optional<TypeId> existingPropType = subjectType;
|
2024-02-23 10:40:00 -08:00
|
|
|
|
|
|
|
LUAU_ASSERT(!c.path.empty());
|
|
|
|
if (c.path.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < c.path.size(); ++i)
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
const std::string& segment = c.path[i];
|
2023-03-03 15:45:38 +02:00
|
|
|
if (!existingPropType)
|
2022-11-18 10:45:14 -08:00
|
|
|
break;
|
2023-03-03 15:45:38 +02:00
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
ValueContext ctx = i == c.path.size() - 1 ? ValueContext::LValue : ValueContext::RValue;
|
|
|
|
|
|
|
|
auto [blocked, result] = lookupTableProp(*existingPropType, segment, ctx);
|
2023-03-03 15:45:38 +02:00
|
|
|
if (!blocked.empty())
|
|
|
|
{
|
|
|
|
for (TypeId blocked : blocked)
|
|
|
|
block(blocked, constraint);
|
|
|
|
return false;
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
2023-03-03 15:45:38 +02:00
|
|
|
|
|
|
|
existingPropType = result;
|
2022-09-23 11:32:10 -07:00
|
|
|
}
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-06-24 08:33:44 +03:00
|
|
|
auto bind = [&](TypeId a, TypeId b) {
|
2024-03-01 15:58:44 +02:00
|
|
|
bindBlockedType(a, b, subjectType, constraint->location);
|
2022-11-18 10:45:14 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
if (existingPropType)
|
2022-09-23 11:32:10 -07:00
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint->scope, constraint->location, propType, *existingPropType);
|
|
|
|
unify(constraint->scope, constraint->location, *existingPropType, propType);
|
2022-11-18 10:45:14 -08:00
|
|
|
bind(c.resultType, c.subjectType);
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.resultType, constraint->location);
|
2022-11-18 10:45:14 -08:00
|
|
|
return true;
|
|
|
|
}
|
2022-09-23 11:32:10 -07:00
|
|
|
|
2024-03-01 15:58:44 +02:00
|
|
|
const TypeId originalSubjectType = subjectType;
|
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
if (auto mt = get<MetatableType>(subjectType))
|
|
|
|
subjectType = follow(mt->table);
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<FreeType>(subjectType))
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
2023-12-01 18:04:44 -08:00
|
|
|
/*
|
|
|
|
* This should never occur because lookupTableProp() will add bounds to
|
|
|
|
* any free types it encounters. There will always be an
|
|
|
|
* existingPropType if the subject is free.
|
|
|
|
*/
|
|
|
|
LUAU_ASSERT(false);
|
|
|
|
return false;
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
2023-01-03 19:33:19 +02:00
|
|
|
else if (auto ttv = getMutable<TableType>(subjectType))
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
|
|
|
if (ttv->state == TableState::Free)
|
|
|
|
{
|
2023-02-24 10:24:22 -08:00
|
|
|
LUAU_ASSERT(!subjectType->persistent);
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
ttv->props[c.path[0]] = Property{propType};
|
2023-12-01 18:04:44 -08:00
|
|
|
bind(c.resultType, subjectType);
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.resultType, constraint->location);
|
2022-11-18 10:45:14 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (ttv->state == TableState::Unsealed)
|
|
|
|
{
|
2023-02-24 10:24:22 -08:00
|
|
|
LUAU_ASSERT(!subjectType->persistent);
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
updateTheTableType(builtinTypes, NotNull{arena}, subjectType, c.path, propType);
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
|
|
|
}
|
2023-12-01 18:04:44 -08:00
|
|
|
|
2024-03-01 15:58:44 +02:00
|
|
|
bind(c.resultType, originalSubjectType);
|
2023-12-01 18:04:44 -08:00
|
|
|
unblock(c.resultType, constraint->location);
|
|
|
|
return true;
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ConstraintSolver::tryDispatch(const SetIndexerConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
|
|
{
|
|
|
|
TypeId subjectType = follow(c.subjectType);
|
|
|
|
if (isBlocked(subjectType))
|
|
|
|
return block(subjectType, constraint);
|
|
|
|
|
|
|
|
if (auto ft = get<FreeType>(subjectType))
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
2023-02-24 10:24:22 -08:00
|
|
|
Scope* scope = ft->scope;
|
|
|
|
TableType* tt = &asMutable(subjectType)->ty.emplace<TableType>(TableState::Free, TypeLevel{}, scope);
|
|
|
|
tt->indexer = TableIndexer{c.indexType, c.propType};
|
|
|
|
|
|
|
|
asMutable(c.resultType)->ty.emplace<BoundType>(subjectType);
|
2023-08-04 10:01:35 -07:00
|
|
|
TypeId propType = freshType(arena, builtinTypes, scope);
|
|
|
|
asMutable(c.propType)->ty.emplace<BoundType>(propType);
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.propType, constraint->location);
|
|
|
|
unblock(c.resultType, constraint->location);
|
2023-02-24 10:24:22 -08:00
|
|
|
|
2022-11-18 10:45:14 -08:00
|
|
|
return true;
|
2022-09-23 11:32:10 -07:00
|
|
|
}
|
2023-02-24 10:24:22 -08:00
|
|
|
else if (auto tt = get<TableType>(subjectType))
|
|
|
|
{
|
|
|
|
if (tt->indexer)
|
|
|
|
{
|
|
|
|
// TODO This probably has to be invariant.
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, c.indexType, tt->indexer->indexType);
|
2023-02-24 10:24:22 -08:00
|
|
|
asMutable(c.propType)->ty.emplace<BoundType>(tt->indexer->indexResultType);
|
|
|
|
asMutable(c.resultType)->ty.emplace<BoundType>(subjectType);
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.propType, constraint->location);
|
|
|
|
unblock(c.resultType, constraint->location);
|
2023-02-24 10:24:22 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (tt->state == TableState::Free || tt->state == TableState::Unsealed)
|
|
|
|
{
|
2023-08-04 10:01:35 -07:00
|
|
|
TypeId promotedIndexTy = freshType(arena, builtinTypes, tt->scope);
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, c.indexType, promotedIndexTy);
|
2023-05-12 15:15:01 +03:00
|
|
|
|
2023-02-24 10:24:22 -08:00
|
|
|
auto mtt = getMutable<TableType>(subjectType);
|
2023-05-12 15:15:01 +03:00
|
|
|
mtt->indexer = TableIndexer{promotedIndexTy, c.propType};
|
2023-08-04 10:01:35 -07:00
|
|
|
TypeId propType = freshType(arena, builtinTypes, tt->scope);
|
|
|
|
asMutable(c.propType)->ty.emplace<BoundType>(propType);
|
2023-02-24 10:24:22 -08:00
|
|
|
asMutable(c.resultType)->ty.emplace<BoundType>(subjectType);
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.propType, constraint->location);
|
|
|
|
unblock(c.resultType, constraint->location);
|
2023-02-24 10:24:22 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Do not augment sealed or generic tables that lack indexers
|
|
|
|
}
|
2022-09-23 11:32:10 -07:00
|
|
|
|
2023-02-24 10:24:22 -08:00
|
|
|
asMutable(c.propType)->ty.emplace<BoundType>(builtinTypes->errorRecoveryType());
|
|
|
|
asMutable(c.resultType)->ty.emplace<BoundType>(builtinTypes->errorRecoveryType());
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.propType, constraint->location);
|
|
|
|
unblock(c.resultType, constraint->location);
|
2022-09-23 11:32:10 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-04 10:02:37 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const SingletonOrTopTypeConstraint& c, NotNull<const Constraint> constraint)
|
2022-10-21 10:33:43 -07:00
|
|
|
{
|
2022-11-04 10:02:37 -07:00
|
|
|
if (isBlocked(c.discriminantType))
|
|
|
|
return false;
|
2022-10-21 10:33:43 -07:00
|
|
|
|
2022-11-04 10:02:37 -07:00
|
|
|
TypeId followed = follow(c.discriminantType);
|
2022-10-21 10:33:43 -07:00
|
|
|
|
2022-11-04 10:02:37 -07:00
|
|
|
// `nil` is a singleton type too! There's only one value of type `nil`.
|
2023-01-03 19:33:19 +02:00
|
|
|
if (c.negated && (get<SingletonType>(followed) || isNil(followed)))
|
|
|
|
*asMutable(c.resultType) = NegationType{c.discriminantType};
|
|
|
|
else if (!c.negated && get<SingletonType>(followed))
|
|
|
|
*asMutable(c.resultType) = BoundType{c.discriminantType};
|
2022-11-04 10:02:37 -07:00
|
|
|
else
|
2023-05-19 11:59:59 -07:00
|
|
|
*asMutable(c.resultType) = BoundType{builtinTypes->anyType};
|
|
|
|
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(c.resultType, constraint->location);
|
2022-10-21 10:33:43 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-02-24 10:24:22 -08:00
|
|
|
bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint)
|
|
|
|
{
|
|
|
|
TypePackId sourcePack = follow(c.sourcePack);
|
|
|
|
TypePackId resultPack = follow(c.resultPack);
|
|
|
|
|
|
|
|
if (isBlocked(sourcePack))
|
|
|
|
return block(sourcePack, constraint);
|
|
|
|
|
|
|
|
if (isBlocked(resultPack))
|
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
LUAU_ASSERT(resultPack != sourcePack);
|
2023-02-24 10:24:22 -08:00
|
|
|
asMutable(resultPack)->ty.emplace<BoundTypePack>(sourcePack);
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(resultPack, constraint->location);
|
2023-02-24 10:24:22 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypePack srcPack = extendTypePack(*arena, builtinTypes, sourcePack, size(resultPack));
|
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
auto resultIter = begin(resultPack);
|
|
|
|
auto resultEnd = end(resultPack);
|
2023-02-24 10:24:22 -08:00
|
|
|
|
2024-02-02 10:20:03 -08:00
|
|
|
auto apply = [&](TypeId resultTy, TypeId srcTy) {
|
|
|
|
if (auto lt = getMutable<LocalType>(resultTy); c.resultIsLValue && lt)
|
|
|
|
{
|
|
|
|
lt->domain = simplifyUnion(builtinTypes, arena, lt->domain, srcTy).result;
|
|
|
|
LUAU_ASSERT(lt->blockCount > 0);
|
|
|
|
--lt->blockCount;
|
|
|
|
|
|
|
|
LUAU_ASSERT(0 <= lt->blockCount);
|
|
|
|
|
|
|
|
if (0 == lt->blockCount)
|
|
|
|
asMutable(resultTy)->ty.emplace<BoundType>(lt->domain);
|
|
|
|
}
|
|
|
|
else if (get<BlockedType>(resultTy))
|
|
|
|
{
|
|
|
|
if (follow(srcTy) == resultTy)
|
|
|
|
{
|
|
|
|
// It is sometimes the case that we find that a blocked type
|
|
|
|
// is only blocked on itself. This doesn't actually
|
|
|
|
// constitute any meaningful constraint, so we replace it
|
|
|
|
// with a free type.
|
|
|
|
TypeId f = freshType(arena, builtinTypes, constraint->scope);
|
|
|
|
asMutable(resultTy)->ty.emplace<BoundType>(f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
asMutable(resultTy)->ty.emplace<BoundType>(srcTy);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(c.resultIsLValue);
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, resultTy, srcTy);
|
2024-02-02 10:20:03 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
unblock(resultTy, constraint->location);
|
|
|
|
};
|
|
|
|
|
2023-02-24 10:24:22 -08:00
|
|
|
size_t i = 0;
|
2023-11-17 10:15:31 -08:00
|
|
|
while (resultIter != resultEnd)
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
|
|
|
if (i >= srcPack.head.size())
|
|
|
|
break;
|
2023-11-17 10:15:31 -08:00
|
|
|
|
2023-02-24 10:24:22 -08:00
|
|
|
TypeId srcTy = follow(srcPack.head[i]);
|
2023-11-17 10:15:31 -08:00
|
|
|
TypeId resultTy = follow(*resultIter);
|
2023-02-24 10:24:22 -08:00
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
if (resultTy)
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
2024-02-02 10:20:03 -08:00
|
|
|
// when we preserve the error-suppression of types through typestate,
|
|
|
|
// we introduce a union with the error type, so we need to find the local type in those options to update.
|
|
|
|
if (auto ut = getMutable<UnionType>(resultTy))
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
2024-02-02 10:20:03 -08:00
|
|
|
for (auto opt : ut->options)
|
|
|
|
apply(opt, srcTy);
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
else
|
2024-02-02 10:20:03 -08:00
|
|
|
apply(resultTy, srcTy);
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
else
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, resultTy, srcTy);
|
2023-02-24 10:24:22 -08:00
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
++resultIter;
|
2023-02-24 10:24:22 -08:00
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We know that resultPack does not have a tail, but we don't know if
|
|
|
|
// sourcePack is long enough to fill every value. Replace every remaining
|
2023-10-13 12:38:31 -07:00
|
|
|
// result TypeId with `nil`.
|
2023-02-24 10:24:22 -08:00
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
while (resultIter != resultEnd)
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
2023-11-17 10:15:31 -08:00
|
|
|
TypeId resultTy = follow(*resultIter);
|
|
|
|
if (auto lt = getMutable<LocalType>(resultTy); c.resultIsLValue && lt)
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
2023-11-17 10:15:31 -08:00
|
|
|
lt->domain = simplifyUnion(builtinTypes, arena, lt->domain, builtinTypes->nilType).result;
|
|
|
|
LUAU_ASSERT(0 <= lt->blockCount);
|
|
|
|
--lt->blockCount;
|
|
|
|
|
|
|
|
if (0 == lt->blockCount)
|
|
|
|
asMutable(resultTy)->ty.emplace<BoundType>(lt->domain);
|
|
|
|
}
|
|
|
|
else if (get<BlockedType>(*resultIter) || get<PendingExpansionType>(*resultIter))
|
|
|
|
{
|
|
|
|
asMutable(*resultIter)->ty.emplace<BoundType>(builtinTypes->nilType);
|
|
|
|
unblock(*resultIter, constraint->location);
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
++resultIter;
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-10-20 13:36:26 -07:00
|
|
|
bool ConstraintSolver::tryDispatch(const SetOpConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
|
|
{
|
|
|
|
bool blocked = false;
|
|
|
|
for (TypeId ty : c.types)
|
|
|
|
{
|
|
|
|
if (isBlocked(ty))
|
|
|
|
{
|
|
|
|
blocked = true;
|
|
|
|
block(ty, constraint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (blocked && !force)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
LUAU_ASSERT(SetOpConstraint::Union == c.mode);
|
|
|
|
|
|
|
|
TypeId res = builtinTypes->neverType;
|
|
|
|
|
|
|
|
for (TypeId ty : c.types)
|
|
|
|
res = simplifyUnion(builtinTypes, arena, res, ty).result;
|
|
|
|
|
|
|
|
asMutable(c.resultType)->ty.emplace<BoundType>(res);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-05-12 15:15:01 +03:00
|
|
|
bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
|
|
{
|
|
|
|
TypeId ty = follow(c.ty);
|
2024-02-23 10:40:00 -08:00
|
|
|
FamilyGraphReductionResult result =
|
|
|
|
reduceFamilies(ty, constraint->location, TypeFamilyContext{NotNull{this}, constraint->scope, constraint}, force);
|
|
|
|
|
2023-05-12 15:15:01 +03:00
|
|
|
|
|
|
|
for (TypeId r : result.reducedTypes)
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(r, constraint->location);
|
2023-05-12 15:15:01 +03:00
|
|
|
|
|
|
|
for (TypePackId r : result.reducedPacks)
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(r, constraint->location);
|
2023-05-12 15:15:01 +03:00
|
|
|
|
|
|
|
if (force)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
for (TypeId b : result.blockedTypes)
|
|
|
|
block(b, constraint);
|
|
|
|
|
|
|
|
for (TypePackId b : result.blockedPacks)
|
|
|
|
block(b, constraint);
|
|
|
|
|
|
|
|
return result.blockedTypes.empty() && result.blockedPacks.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConstraintSolver::tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
|
|
{
|
|
|
|
TypePackId tp = follow(c.tp);
|
2024-02-23 10:40:00 -08:00
|
|
|
FamilyGraphReductionResult result =
|
|
|
|
reduceFamilies(tp, constraint->location, TypeFamilyContext{NotNull{this}, constraint->scope, constraint}, force);
|
2023-05-12 15:15:01 +03:00
|
|
|
|
|
|
|
for (TypeId r : result.reducedTypes)
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(r, constraint->location);
|
2023-05-12 15:15:01 +03:00
|
|
|
|
|
|
|
for (TypePackId r : result.reducedPacks)
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(r, constraint->location);
|
2023-05-12 15:15:01 +03:00
|
|
|
|
|
|
|
if (force)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
for (TypeId b : result.blockedTypes)
|
|
|
|
block(b, constraint);
|
|
|
|
|
|
|
|
for (TypePackId b : result.blockedPacks)
|
|
|
|
block(b, constraint);
|
|
|
|
|
|
|
|
return result.blockedTypes.empty() && result.blockedPacks.empty();
|
|
|
|
}
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
bool ConstraintSolver::tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
|
|
{
|
|
|
|
unify(constraint->scope, constraint->location, c.resultType, c.assignmentType);
|
|
|
|
unify(constraint->scope, constraint->location, c.assignmentType, c.resultType);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
|
|
{
|
|
|
|
auto block_ = [&](auto&& t) {
|
|
|
|
if (force)
|
|
|
|
{
|
|
|
|
// TODO: I believe it is the case that, if we are asked to force
|
|
|
|
// this constraint, then we can do nothing but fail. I'd like to
|
|
|
|
// find a code sample that gets here.
|
2022-09-23 11:32:10 -07:00
|
|
|
LUAU_ASSERT(false);
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
block(t, constraint);
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// We may have to block here if we don't know what the iteratee type is,
|
|
|
|
// if it's a free table, if we don't know it has a metatable, and so on.
|
|
|
|
iteratorTy = follow(iteratorTy);
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<FreeType>(iteratorTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
return block_(iteratorTy);
|
|
|
|
|
2023-10-13 12:38:31 -07:00
|
|
|
auto unpack = [&](TypeId ty) {
|
|
|
|
TypePackId variadic = arena->addTypePack(VariadicTypePack{ty});
|
2024-01-26 18:30:40 -08:00
|
|
|
pushConstraint(constraint->scope, constraint->location, UnpackConstraint{c.variables, variadic, /* resultIsLValue */ true});
|
2023-05-25 23:46:51 +03:00
|
|
|
};
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<AnyType>(iteratorTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
2023-10-13 12:38:31 -07:00
|
|
|
unpack(builtinTypes->anyType);
|
2022-09-01 16:00:14 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<ErrorType>(iteratorTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
2023-10-13 12:38:31 -07:00
|
|
|
unpack(builtinTypes->errorType);
|
2022-09-01 16:00:14 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-05-25 23:46:51 +03:00
|
|
|
if (get<NeverType>(iteratorTy))
|
|
|
|
{
|
2023-10-13 12:38:31 -07:00
|
|
|
unpack(builtinTypes->neverType);
|
2023-05-25 23:46:51 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
// Irksome: I don't think we have any way to guarantee that this table
|
|
|
|
// type never has a metatable.
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
if (auto iteratorTable = get<TableType>(iteratorTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
2023-03-31 15:21:14 +03:00
|
|
|
/*
|
|
|
|
* We try not to dispatch IterableConstraints over free tables because
|
|
|
|
* it's possible that there are other constraints on the table that will
|
|
|
|
* clarify what we should do.
|
|
|
|
*
|
|
|
|
* We should eventually introduce a type family to talk about iteration.
|
|
|
|
*/
|
|
|
|
if (iteratorTable->state == TableState::Free && !force)
|
|
|
|
return block(iteratorTy, constraint);
|
2022-09-01 16:00:14 -07:00
|
|
|
|
|
|
|
if (iteratorTable->indexer)
|
|
|
|
{
|
|
|
|
TypePackId expectedVariablePack = arena->addTypePack({iteratorTable->indexer->indexType, iteratorTable->indexer->indexResultType});
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, c.variables, expectedVariablePack);
|
2024-01-26 18:30:40 -08:00
|
|
|
|
|
|
|
auto [variableTys, variablesTail] = flatten(c.variables);
|
|
|
|
|
|
|
|
// the local types for the indexer _should_ be all set after unification
|
|
|
|
for (TypeId ty : variableTys)
|
|
|
|
{
|
|
|
|
if (auto lt = getMutable<LocalType>(ty))
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(lt->blockCount > 0);
|
|
|
|
--lt->blockCount;
|
|
|
|
|
|
|
|
LUAU_ASSERT(0 <= lt->blockCount);
|
|
|
|
|
|
|
|
if (0 == lt->blockCount)
|
|
|
|
asMutable(ty)->ty.emplace<BoundType>(lt->domain);
|
|
|
|
}
|
|
|
|
}
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
|
|
|
else
|
2023-10-13 12:38:31 -07:00
|
|
|
unpack(builtinTypes->errorType);
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
2023-01-03 19:33:19 +02:00
|
|
|
else if (std::optional<TypeId> iterFn = findMetatableEntry(builtinTypes, errors, iteratorTy, "__iter", Location{}))
|
2022-09-29 15:11:54 -07:00
|
|
|
{
|
|
|
|
if (isBlocked(*iterFn))
|
|
|
|
{
|
|
|
|
return block(*iterFn, constraint);
|
|
|
|
}
|
|
|
|
|
2023-08-04 10:01:35 -07:00
|
|
|
if (std::optional<TypeId> instantiatedIterFn = instantiate(builtinTypes, arena, NotNull{&limits}, constraint->scope, *iterFn))
|
2022-09-29 15:11:54 -07:00
|
|
|
{
|
2023-01-03 19:33:19 +02:00
|
|
|
if (auto iterFtv = get<FunctionType>(*instantiatedIterFn))
|
2022-09-29 15:11:54 -07:00
|
|
|
{
|
|
|
|
TypePackId expectedIterArgs = arena->addTypePack({iteratorTy});
|
2024-02-23 10:40:00 -08:00
|
|
|
unify(constraint, iterFtv->argTypes, expectedIterArgs);
|
2022-09-29 15:11:54 -07:00
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
TypePack iterRets = extendTypePack(*arena, builtinTypes, iterFtv->retTypes, 2);
|
2022-09-29 15:11:54 -07:00
|
|
|
|
2022-12-02 12:46:05 +02:00
|
|
|
if (iterRets.head.size() < 1)
|
2022-09-29 15:11:54 -07:00
|
|
|
{
|
|
|
|
// We've done what we can; this will get reported as an
|
|
|
|
// error by the type checker.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-12-02 12:46:05 +02:00
|
|
|
TypeId nextFn = iterRets.head[0];
|
2022-09-29 15:11:54 -07:00
|
|
|
|
2023-08-04 10:01:35 -07:00
|
|
|
if (std::optional<TypeId> instantiatedNextFn = instantiate(builtinTypes, arena, NotNull{&limits}, constraint->scope, nextFn))
|
2022-09-29 15:11:54 -07:00
|
|
|
{
|
2024-03-01 15:58:44 +02:00
|
|
|
const FunctionType* nextFn = get<FunctionType>(*instantiatedNextFn);
|
|
|
|
LUAU_ASSERT(nextFn);
|
|
|
|
const TypePackId nextRetPack = nextFn->retTypes;
|
2022-09-29 15:11:54 -07:00
|
|
|
|
2023-10-13 12:38:31 -07:00
|
|
|
pushConstraint(constraint->scope, constraint->location, UnpackConstraint{c.variables, nextRetPack});
|
2024-03-01 15:58:44 +02:00
|
|
|
return true;
|
2022-09-29 15:11:54 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(UnificationTooComplex{}, constraint->location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: Support __call and function overloads (what does an overload even mean for this?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(UnificationTooComplex{}, constraint->location);
|
|
|
|
}
|
|
|
|
}
|
2023-01-03 19:33:19 +02:00
|
|
|
else if (auto iteratorMetatable = get<MetatableType>(iteratorTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
|
|
|
TypeId metaTy = follow(iteratorMetatable->metatable);
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<FreeType>(metaTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
return block_(metaTy);
|
|
|
|
|
2022-09-23 11:32:10 -07:00
|
|
|
LUAU_ASSERT(false);
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
2023-07-28 14:37:00 +03:00
|
|
|
else if (auto primitiveTy = get<PrimitiveType>(iteratorTy); primitiveTy && primitiveTy->type == PrimitiveType::Type::Table)
|
2023-10-13 12:38:31 -07:00
|
|
|
unpack(builtinTypes->unknownType);
|
2022-09-01 16:00:14 -07:00
|
|
|
else
|
2023-10-13 12:38:31 -07:00
|
|
|
unpack(builtinTypes->errorType);
|
2022-09-01 16:00:14 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConstraintSolver::tryDispatchIterableFunction(
|
|
|
|
TypeId nextTy, TypeId tableTy, TypeId firstIndexTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
|
|
{
|
|
|
|
// We need to know whether or not this type is nil or not.
|
|
|
|
// If we don't know, block and reschedule ourselves.
|
|
|
|
firstIndexTy = follow(firstIndexTy);
|
2023-01-03 19:33:19 +02:00
|
|
|
if (get<FreeType>(firstIndexTy))
|
2022-09-01 16:00:14 -07:00
|
|
|
{
|
|
|
|
if (force)
|
2022-09-23 11:32:10 -07:00
|
|
|
LUAU_ASSERT(false);
|
2022-09-01 16:00:14 -07:00
|
|
|
else
|
|
|
|
block(firstIndexTy, constraint);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-03-01 15:58:44 +02:00
|
|
|
const FunctionType* nextFn = get<FunctionType>(nextTy);
|
|
|
|
// If this does not hold, we should've never called `tryDispatchIterableFunction` in the first place.
|
|
|
|
LUAU_ASSERT(nextFn);
|
|
|
|
const TypePackId nextRetPack = nextFn->retTypes;
|
2023-05-25 23:46:51 +03:00
|
|
|
|
2024-03-01 15:58:44 +02:00
|
|
|
// the type of the `nextAstFragment` is the `nextTy`.
|
|
|
|
(*c.astForInNextTypes)[c.nextAstFragment] = nextTy;
|
2022-09-01 16:00:14 -07:00
|
|
|
|
2023-01-27 13:28:45 -08:00
|
|
|
auto it = begin(nextRetPack);
|
|
|
|
std::vector<TypeId> modifiedNextRetHead;
|
|
|
|
|
|
|
|
// The first value is never nil in the context of the loop, even if it's nil
|
|
|
|
// in the next function's return type, because the loop will not advance if
|
|
|
|
// it's nil.
|
|
|
|
if (it != end(nextRetPack))
|
|
|
|
{
|
|
|
|
TypeId firstRet = *it;
|
|
|
|
TypeId modifiedFirstRet = stripNil(builtinTypes, *arena, firstRet);
|
|
|
|
modifiedNextRetHead.push_back(modifiedFirstRet);
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; it != end(nextRetPack); ++it)
|
|
|
|
modifiedNextRetHead.push_back(*it);
|
|
|
|
|
|
|
|
TypePackId modifiedNextRetPack = arena->addTypePack(std::move(modifiedNextRetHead), it.tail());
|
2024-01-26 18:30:40 -08:00
|
|
|
auto psc = pushConstraint(constraint->scope, constraint->location, UnpackConstraint{c.variables, modifiedNextRetPack, /* resultIsLValue */ true});
|
2023-03-17 16:59:30 +02:00
|
|
|
inheritBlocks(constraint, psc);
|
2022-09-01 16:00:14 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-05-19 11:59:59 -07:00
|
|
|
std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTableProp(
|
2024-02-23 10:40:00 -08:00
|
|
|
TypeId subjectType, const std::string& propName, ValueContext context, bool suppressSimplification)
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
2023-11-10 10:05:48 -08:00
|
|
|
DenseHashSet<TypeId> seen{nullptr};
|
2024-02-23 10:40:00 -08:00
|
|
|
return lookupTableProp(subjectType, propName, context, suppressSimplification, seen);
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
|
2023-05-19 11:59:59 -07:00
|
|
|
std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTableProp(
|
2024-02-23 10:40:00 -08:00
|
|
|
TypeId subjectType, const std::string& propName, ValueContext context, bool suppressSimplification, DenseHashSet<TypeId>& seen)
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
2023-11-10 10:05:48 -08:00
|
|
|
if (seen.contains(subjectType))
|
2023-03-03 15:45:38 +02:00
|
|
|
return {};
|
2023-11-10 10:05:48 -08:00
|
|
|
seen.insert(subjectType);
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
subjectType = follow(subjectType);
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
if (isBlocked(subjectType))
|
|
|
|
return {{subjectType}, std::nullopt};
|
|
|
|
else if (get<AnyType>(subjectType) || get<NeverType>(subjectType))
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
2023-03-03 15:45:38 +02:00
|
|
|
return {{}, subjectType};
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
else if (auto ttv = getMutable<TableType>(subjectType))
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
|
|
|
if (auto prop = ttv->props.find(propName); prop != ttv->props.end())
|
2024-02-23 10:40:00 -08:00
|
|
|
{
|
|
|
|
switch (context)
|
|
|
|
{
|
|
|
|
case ValueContext::RValue:
|
|
|
|
if (auto rt = prop->second.readTy)
|
|
|
|
return {{}, rt};
|
|
|
|
break;
|
|
|
|
case ValueContext::LValue:
|
|
|
|
if (auto wt = prop->second.writeTy)
|
|
|
|
return {{}, wt};
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ttv->indexer && maybeString(ttv->indexer->indexType))
|
2023-03-03 15:45:38 +02:00
|
|
|
return {{}, ttv->indexer->indexResultType};
|
2024-02-23 10:40:00 -08:00
|
|
|
|
|
|
|
if (ttv->state == TableState::Free)
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
2023-08-04 10:01:35 -07:00
|
|
|
TypeId result = freshType(arena, builtinTypes, ttv->scope);
|
2024-02-23 10:40:00 -08:00
|
|
|
switch (context)
|
|
|
|
{
|
|
|
|
case ValueContext::RValue:
|
|
|
|
ttv->props[propName].readTy = result;
|
|
|
|
break;
|
|
|
|
case ValueContext::LValue:
|
|
|
|
if (auto it = ttv->props.find(propName); it != ttv->props.end() && it->second.isReadOnly())
|
|
|
|
{
|
|
|
|
// We do infer read-only properties, but we do not infer
|
|
|
|
// separate read and write types.
|
|
|
|
//
|
|
|
|
// If we encounter a case where a free table has a read-only
|
|
|
|
// property that we subsequently sense a write to, we make
|
|
|
|
// the judgement that the property is read-write and that
|
|
|
|
// both the read and write types are the same.
|
|
|
|
|
|
|
|
Property& prop = it->second;
|
|
|
|
|
|
|
|
prop.writeTy = prop.readTy;
|
|
|
|
return {{}, *prop.readTy};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ttv->props[propName] = Property::rw(result);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2023-03-03 15:45:38 +02:00
|
|
|
return {{}, result};
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
}
|
2024-03-01 15:58:44 +02:00
|
|
|
else if (auto mt = get<MetatableType>(subjectType); mt && context == ValueContext::RValue)
|
2023-02-24 10:24:22 -08:00
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
auto [blocked, result] = lookupTableProp(mt->table, propName, context, suppressSimplification, seen);
|
2023-03-03 15:45:38 +02:00
|
|
|
if (!blocked.empty() || result)
|
|
|
|
return {blocked, result};
|
2023-02-24 10:24:22 -08:00
|
|
|
|
|
|
|
TypeId mtt = follow(mt->metatable);
|
|
|
|
|
|
|
|
if (get<BlockedType>(mtt))
|
2023-03-03 15:45:38 +02:00
|
|
|
return {{mtt}, std::nullopt};
|
2023-02-24 10:24:22 -08:00
|
|
|
else if (auto metatable = get<TableType>(mtt))
|
|
|
|
{
|
|
|
|
auto indexProp = metatable->props.find("__index");
|
|
|
|
if (indexProp == metatable->props.end())
|
2023-03-03 15:45:38 +02:00
|
|
|
return {{}, result};
|
2023-02-24 10:24:22 -08:00
|
|
|
|
|
|
|
// TODO: __index can be an overloaded function.
|
|
|
|
|
2023-04-28 14:55:55 +03:00
|
|
|
TypeId indexType = follow(indexProp->second.type());
|
2023-02-24 10:24:22 -08:00
|
|
|
|
|
|
|
if (auto ft = get<FunctionType>(indexType))
|
2023-03-17 16:59:30 +02:00
|
|
|
{
|
|
|
|
TypePack rets = extendTypePack(*arena, builtinTypes, ft->retTypes, 1);
|
|
|
|
if (1 == rets.head.size())
|
|
|
|
return {{}, rets.head[0]};
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This should probably be an error: We need the first result of the MT.__index method,
|
|
|
|
// but it returns 0 values. See CLI-68672
|
|
|
|
return {{}, builtinTypes->nilType};
|
|
|
|
}
|
|
|
|
}
|
2023-03-03 15:45:38 +02:00
|
|
|
else
|
2024-02-23 10:40:00 -08:00
|
|
|
return lookupTableProp(indexType, propName, context, suppressSimplification, seen);
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
2024-03-01 15:58:44 +02:00
|
|
|
else if (get<MetatableType>(mtt))
|
|
|
|
return lookupTableProp(mtt, propName, context, suppressSimplification, seen);
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
else if (auto ct = get<ClassType>(subjectType))
|
|
|
|
{
|
2023-03-03 15:45:38 +02:00
|
|
|
if (auto p = lookupClassProp(ct, propName))
|
2024-02-23 10:40:00 -08:00
|
|
|
return {{}, context == ValueContext::RValue ? p->readTy : p->writeTy};
|
2023-05-19 11:59:59 -07:00
|
|
|
if (ct->indexer)
|
|
|
|
{
|
|
|
|
return {{}, ct->indexer->indexResultType};
|
|
|
|
}
|
2023-02-24 10:24:22 -08:00
|
|
|
}
|
|
|
|
else if (auto pt = get<PrimitiveType>(subjectType); pt && pt->metatable)
|
|
|
|
{
|
|
|
|
const TableType* metatable = get<TableType>(follow(*pt->metatable));
|
|
|
|
LUAU_ASSERT(metatable);
|
|
|
|
|
|
|
|
auto indexProp = metatable->props.find("__index");
|
|
|
|
if (indexProp == metatable->props.end())
|
2023-03-03 15:45:38 +02:00
|
|
|
return {{}, std::nullopt};
|
2023-02-24 10:24:22 -08:00
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
return lookupTableProp(indexProp->second.type(), propName, context, suppressSimplification, seen);
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
2023-03-03 15:45:38 +02:00
|
|
|
else if (auto ft = get<FreeType>(subjectType))
|
|
|
|
{
|
2023-11-17 10:15:31 -08:00
|
|
|
const TypeId upperBound = follow(ft->upperBound);
|
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
2024-02-23 10:40:00 -08:00
|
|
|
return lookupTableProp(upperBound, propName, context, suppressSimplification, seen);
|
2023-11-17 10:15:31 -08:00
|
|
|
|
|
|
|
// TODO: The upper bound could be an intersection that contains suitable tables or classes.
|
2023-03-03 15:45:38 +02:00
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
NotNull<Scope> scope{ft->scope};
|
|
|
|
|
|
|
|
const TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, scope});
|
|
|
|
TableType* tt = getMutable<TableType>(newUpperBound);
|
|
|
|
LUAU_ASSERT(tt);
|
2023-08-04 10:01:35 -07:00
|
|
|
TypeId propType = freshType(arena, builtinTypes, scope);
|
2024-02-23 10:40:00 -08:00
|
|
|
|
|
|
|
switch (context)
|
|
|
|
{
|
|
|
|
case ValueContext::RValue:
|
|
|
|
tt->props[propName] = Property::readonly(propType);
|
|
|
|
break;
|
|
|
|
case ValueContext::LValue:
|
|
|
|
tt->props[propName] = Property::rw(propType);
|
|
|
|
break;
|
|
|
|
}
|
2023-03-03 15:45:38 +02:00
|
|
|
|
2023-11-17 10:15:31 -08:00
|
|
|
unify(scope, Location{}, subjectType, newUpperBound);
|
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
return {{}, propType};
|
|
|
|
}
|
2023-01-03 19:33:19 +02:00
|
|
|
else if (auto utv = get<UnionType>(subjectType))
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
2023-03-03 15:45:38 +02:00
|
|
|
std::vector<TypeId> blocked;
|
2023-03-31 15:21:14 +03:00
|
|
|
std::set<TypeId> options;
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
for (TypeId ty : utv)
|
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
auto [innerBlocked, innerResult] = lookupTableProp(ty, propName, context, suppressSimplification, seen);
|
2023-03-03 15:45:38 +02:00
|
|
|
blocked.insert(blocked.end(), innerBlocked.begin(), innerBlocked.end());
|
|
|
|
if (innerResult)
|
2023-03-31 15:21:14 +03:00
|
|
|
options.insert(*innerResult);
|
2023-03-03 15:45:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!blocked.empty())
|
|
|
|
return {blocked, std::nullopt};
|
2023-01-27 13:28:45 -08:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
if (options.empty())
|
|
|
|
return {{}, std::nullopt};
|
|
|
|
else if (options.size() == 1)
|
2023-03-31 15:21:14 +03:00
|
|
|
return {{}, *begin(options)};
|
2023-05-19 11:59:59 -07:00
|
|
|
else if (options.size() == 2 && !suppressSimplification)
|
|
|
|
{
|
|
|
|
TypeId one = *begin(options);
|
|
|
|
TypeId two = *(++begin(options));
|
|
|
|
return {{}, simplifyUnion(builtinTypes, arena, one, two).result};
|
|
|
|
}
|
2023-03-03 15:45:38 +02:00
|
|
|
else
|
2023-03-31 15:21:14 +03:00
|
|
|
return {{}, arena->addType(UnionType{std::vector<TypeId>(begin(options), end(options))})};
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
2023-01-03 19:33:19 +02:00
|
|
|
else if (auto itv = get<IntersectionType>(subjectType))
|
2022-11-18 10:45:14 -08:00
|
|
|
{
|
2023-03-03 15:45:38 +02:00
|
|
|
std::vector<TypeId> blocked;
|
2023-03-31 15:21:14 +03:00
|
|
|
std::set<TypeId> options;
|
2022-11-18 10:45:14 -08:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
for (TypeId ty : itv)
|
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
auto [innerBlocked, innerResult] = lookupTableProp(ty, propName, context, suppressSimplification, seen);
|
2023-03-03 15:45:38 +02:00
|
|
|
blocked.insert(blocked.end(), innerBlocked.begin(), innerBlocked.end());
|
|
|
|
if (innerResult)
|
2023-03-31 15:21:14 +03:00
|
|
|
options.insert(*innerResult);
|
2023-03-03 15:45:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!blocked.empty())
|
|
|
|
return {blocked, std::nullopt};
|
2023-01-27 13:28:45 -08:00
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
if (options.empty())
|
|
|
|
return {{}, std::nullopt};
|
|
|
|
else if (options.size() == 1)
|
2023-03-31 15:21:14 +03:00
|
|
|
return {{}, *begin(options)};
|
2023-05-19 11:59:59 -07:00
|
|
|
else if (options.size() == 2 && !suppressSimplification)
|
|
|
|
{
|
|
|
|
TypeId one = *begin(options);
|
|
|
|
TypeId two = *(++begin(options));
|
|
|
|
return {{}, simplifyIntersection(builtinTypes, arena, one, two).result};
|
|
|
|
}
|
2023-03-03 15:45:38 +02:00
|
|
|
else
|
2023-03-31 15:21:14 +03:00
|
|
|
return {{}, arena->addType(IntersectionType{std::vector<TypeId>(begin(options), end(options))})};
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
|
|
|
|
2023-03-03 15:45:38 +02:00
|
|
|
return {{}, std::nullopt};
|
2022-11-18 10:45:14 -08:00
|
|
|
}
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
template <typename TID>
|
|
|
|
bool ConstraintSolver::unify(NotNull<Scope> scope, Location location, TID subType, TID superType)
|
2023-03-31 15:21:14 +03:00
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
Unifier2 u2{NotNull{arena}, builtinTypes, scope, NotNull{&iceReporter}};
|
2023-03-31 15:21:14 +03:00
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
const bool ok = u2.unify(subType, superType);
|
2023-03-31 15:21:14 +03:00
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
if (ok)
|
2023-11-10 10:05:48 -08:00
|
|
|
{
|
|
|
|
for (const auto& [expanded, additions] : u2.expandedFreeTypes)
|
|
|
|
{
|
|
|
|
for (TypeId addition : additions)
|
2024-02-23 10:40:00 -08:00
|
|
|
upperBoundContributors[expanded].push_back(std::make_pair(location, addition));
|
2023-11-10 10:05:48 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2023-03-31 15:21:14 +03:00
|
|
|
{
|
2024-02-23 10:40:00 -08:00
|
|
|
reportError(OccursCheckFailed{}, location);
|
|
|
|
return false;
|
2023-03-31 15:21:14 +03:00
|
|
|
}
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
unblock(subType, location);
|
|
|
|
unblock(superType, location);
|
2023-03-31 15:21:14 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
template<typename TID>
|
|
|
|
bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TID subTy, TID superTy)
|
|
|
|
{
|
|
|
|
return unify(constraint->scope, constraint->location, subTy, superTy);
|
|
|
|
}
|
|
|
|
|
2023-06-24 08:33:44 +03:00
|
|
|
void ConstraintSolver::bindBlockedType(TypeId blockedTy, TypeId resultTy, TypeId rootTy, Location location)
|
|
|
|
{
|
|
|
|
resultTy = follow(resultTy);
|
|
|
|
|
|
|
|
LUAU_ASSERT(get<BlockedType>(blockedTy));
|
|
|
|
|
|
|
|
if (blockedTy == resultTy)
|
|
|
|
{
|
|
|
|
rootTy = follow(rootTy);
|
|
|
|
Scope* freeScope = nullptr;
|
|
|
|
if (auto ft = get<FreeType>(rootTy))
|
|
|
|
freeScope = ft->scope;
|
|
|
|
else if (auto tt = get<TableType>(rootTy); tt && tt->state == TableState::Free)
|
|
|
|
freeScope = tt->scope;
|
|
|
|
else
|
|
|
|
iceReporter.ice("bindBlockedType couldn't find an appropriate scope for a fresh type!", location);
|
|
|
|
|
|
|
|
LUAU_ASSERT(freeScope);
|
|
|
|
|
|
|
|
asMutable(blockedTy)->ty.emplace<BoundType>(arena->freshType(freeScope));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
asMutable(blockedTy)->ty.emplace<BoundType>(resultTy);
|
|
|
|
}
|
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
bool ConstraintSolver::block_(BlockedConstraintId target, NotNull<const Constraint> constraint)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2024-01-26 18:30:40 -08:00
|
|
|
// If a set is not present for the target, construct a new DenseHashSet for it,
|
|
|
|
// else grab the address of the existing set.
|
|
|
|
NotNull<DenseHashSet<const Constraint*>> blockVec{&blocked.try_emplace(target, nullptr).first->second};
|
|
|
|
|
|
|
|
if (blockVec->find(constraint))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
blockVec->insert(constraint);
|
2022-06-03 13:32:20 -07:00
|
|
|
|
|
|
|
auto& count = blockedConstraints[constraint];
|
|
|
|
count += 1;
|
2024-01-26 18:30:40 -08:00
|
|
|
|
|
|
|
return true;
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
void ConstraintSolver::block(NotNull<const Constraint> target, NotNull<const Constraint> constraint)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2024-01-26 18:30:40 -08:00
|
|
|
const bool newBlock = block_(target.get(), constraint);
|
|
|
|
if (newBlock)
|
|
|
|
{
|
|
|
|
if (logger)
|
|
|
|
logger->pushBlock(constraint, target);
|
2022-09-08 14:44:50 -07:00
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
if (FFlag::DebugLuauLogSolver)
|
|
|
|
printf("block Constraint %s on\t%s\n", toString(*target, opts).c_str(), toString(*constraint, opts).c_str());
|
|
|
|
}
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::block(TypeId target, NotNull<const Constraint> constraint)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2024-01-26 18:30:40 -08:00
|
|
|
const bool newBlock = block_(follow(target), constraint);
|
|
|
|
if (newBlock)
|
|
|
|
{
|
|
|
|
if (logger)
|
|
|
|
logger->pushBlock(constraint, target);
|
2022-09-08 14:44:50 -07:00
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
if (FFlag::DebugLuauLogSolver)
|
|
|
|
printf("block TypeId %s on\t%s\n", toString(target, opts).c_str(), toString(*constraint, opts).c_str());
|
|
|
|
}
|
2022-09-08 14:44:50 -07:00
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
return false;
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::block(TypePackId target, NotNull<const Constraint> constraint)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2024-01-26 18:30:40 -08:00
|
|
|
const bool newBlock = block_(target, constraint);
|
|
|
|
if (newBlock)
|
|
|
|
{
|
|
|
|
if (logger)
|
|
|
|
logger->pushBlock(constraint, target);
|
2022-09-08 14:44:50 -07:00
|
|
|
|
2024-01-26 18:30:40 -08:00
|
|
|
if (FFlag::DebugLuauLogSolver)
|
|
|
|
printf("block TypeId %s on\t%s\n", toString(target, opts).c_str(), toString(*constraint, opts).c_str());
|
|
|
|
}
|
2022-09-08 14:44:50 -07:00
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
return false;
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2023-03-17 16:59:30 +02:00
|
|
|
void ConstraintSolver::inheritBlocks(NotNull<const Constraint> source, NotNull<const Constraint> addition)
|
|
|
|
{
|
|
|
|
// Anything that is blocked on this constraint must also be blocked on our
|
|
|
|
// synthesized constraints.
|
|
|
|
auto blockedIt = blocked.find(source.get());
|
|
|
|
if (blockedIt != blocked.end())
|
|
|
|
{
|
2024-01-26 18:30:40 -08:00
|
|
|
for (const Constraint* blockedConstraint : blockedIt->second)
|
2023-03-17 16:59:30 +02:00
|
|
|
{
|
2024-01-26 18:30:40 -08:00
|
|
|
block(addition, NotNull{blockedConstraint});
|
2023-03-17 16:59:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-03 19:33:19 +02:00
|
|
|
struct Blocker : TypeOnceVisitor
|
2022-09-23 11:32:10 -07:00
|
|
|
{
|
|
|
|
NotNull<ConstraintSolver> solver;
|
|
|
|
NotNull<const Constraint> constraint;
|
|
|
|
|
|
|
|
bool blocked = false;
|
|
|
|
|
|
|
|
explicit Blocker(NotNull<ConstraintSolver> solver, NotNull<const Constraint> constraint)
|
|
|
|
: solver(solver)
|
|
|
|
, constraint(constraint)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-05-25 23:46:51 +03:00
|
|
|
bool visit(TypeId ty, const PendingExpansionType&) override
|
2022-09-23 11:32:10 -07:00
|
|
|
{
|
|
|
|
blocked = true;
|
|
|
|
solver->block(ty, constraint);
|
|
|
|
return false;
|
|
|
|
}
|
2023-05-25 23:46:51 +03:00
|
|
|
|
|
|
|
bool visit(TypeId ty, const ClassType&) override
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2022-09-23 11:32:10 -07:00
|
|
|
};
|
|
|
|
|
2023-05-19 11:59:59 -07:00
|
|
|
bool ConstraintSolver::blockOnPendingTypes(TypeId target, NotNull<const Constraint> constraint)
|
2022-09-23 11:32:10 -07:00
|
|
|
{
|
|
|
|
Blocker blocker{NotNull{this}, constraint};
|
|
|
|
blocker.traverse(target);
|
|
|
|
return !blocker.blocked;
|
|
|
|
}
|
|
|
|
|
2023-05-19 11:59:59 -07:00
|
|
|
bool ConstraintSolver::blockOnPendingTypes(TypePackId pack, NotNull<const Constraint> constraint)
|
2022-09-23 11:32:10 -07:00
|
|
|
{
|
|
|
|
Blocker blocker{NotNull{this}, constraint};
|
|
|
|
blocker.traverse(pack);
|
|
|
|
return !blocker.blocked;
|
|
|
|
}
|
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
void ConstraintSolver::unblock_(BlockedConstraintId progressed)
|
|
|
|
{
|
|
|
|
auto it = blocked.find(progressed);
|
|
|
|
if (it == blocked.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// unblocked should contain a value always, because of the above check
|
2024-01-26 18:30:40 -08:00
|
|
|
for (const Constraint* unblockedConstraint : it->second)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2024-01-26 18:30:40 -08:00
|
|
|
auto& count = blockedConstraints[NotNull{unblockedConstraint}];
|
2022-08-25 13:55:08 -07:00
|
|
|
if (FFlag::DebugLuauLogSolver)
|
2022-09-01 16:00:14 -07:00
|
|
|
printf("Unblocking count=%d\t%s\n", int(count), toString(*unblockedConstraint, opts).c_str());
|
2022-08-25 13:55:08 -07:00
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
// This assertion being hit indicates that `blocked` and
|
|
|
|
// `blockedConstraints` desynchronized at some point. This is problematic
|
|
|
|
// because we rely on this count being correct to skip over blocked
|
|
|
|
// constraints.
|
|
|
|
LUAU_ASSERT(count > 0);
|
|
|
|
count -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
blocked.erase(it);
|
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
void ConstraintSolver::unblock(NotNull<const Constraint> progressed)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2023-03-03 15:45:38 +02:00
|
|
|
if (logger)
|
2022-09-08 14:44:50 -07:00
|
|
|
logger->popBlock(progressed);
|
|
|
|
|
2023-02-24 10:24:22 -08:00
|
|
|
return unblock_(progressed.get());
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2023-06-16 10:01:18 -07:00
|
|
|
void ConstraintSolver::unblock(TypeId ty, Location location)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2023-05-05 12:57:12 -07:00
|
|
|
DenseHashSet<TypeId> seen{nullptr};
|
2022-09-08 14:44:50 -07:00
|
|
|
|
2023-05-12 15:15:01 +03:00
|
|
|
TypeId progressed = ty;
|
2023-05-05 12:57:12 -07:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (seen.find(progressed))
|
2023-06-16 10:01:18 -07:00
|
|
|
iceReporter.ice("ConstraintSolver::unblock encountered a self-bound type!", location);
|
2023-05-05 12:57:12 -07:00
|
|
|
seen.insert(progressed);
|
2023-02-24 10:24:22 -08:00
|
|
|
|
2023-05-05 12:57:12 -07:00
|
|
|
if (logger)
|
|
|
|
logger->popBlock(progressed);
|
|
|
|
|
|
|
|
unblock_(progressed);
|
|
|
|
|
|
|
|
if (auto bt = get<BoundType>(progressed))
|
|
|
|
progressed = bt->boundTo;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2023-06-16 10:01:18 -07:00
|
|
|
void ConstraintSolver::unblock(TypePackId progressed, Location)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2023-03-03 15:45:38 +02:00
|
|
|
if (logger)
|
2022-09-08 14:44:50 -07:00
|
|
|
logger->popBlock(progressed);
|
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
return unblock_(progressed);
|
|
|
|
}
|
|
|
|
|
2023-06-16 10:01:18 -07:00
|
|
|
void ConstraintSolver::unblock(const std::vector<TypeId>& types, Location location)
|
2022-08-25 13:55:08 -07:00
|
|
|
{
|
|
|
|
for (TypeId t : types)
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(t, location);
|
2022-08-25 13:55:08 -07:00
|
|
|
}
|
|
|
|
|
2023-06-16 10:01:18 -07:00
|
|
|
void ConstraintSolver::unblock(const std::vector<TypePackId>& packs, Location location)
|
2022-08-25 13:55:08 -07:00
|
|
|
{
|
|
|
|
for (TypePackId t : packs)
|
2023-06-16 10:01:18 -07:00
|
|
|
unblock(t, location);
|
2022-08-25 13:55:08 -07:00
|
|
|
}
|
|
|
|
|
2024-02-23 10:40:00 -08:00
|
|
|
void ConstraintSolver::reproduceConstraints(NotNull<Scope> scope, const Location& location, const Substitution& subst)
|
|
|
|
{
|
|
|
|
for (auto [_, newTy] : subst.newTypes)
|
|
|
|
{
|
|
|
|
if (get<TypeFamilyInstanceType>(newTy))
|
|
|
|
pushConstraint(scope, location, ReduceConstraint{newTy});
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto [_, newPack] : subst.newPacks)
|
|
|
|
{
|
|
|
|
if (get<TypeFamilyInstanceTypePack>(newPack))
|
|
|
|
pushConstraint(scope, location, ReducePackConstraint{newPack});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::isBlocked(TypeId ty)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2023-11-17 10:15:31 -08:00
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
if (auto lt = get<LocalType>(ty))
|
|
|
|
return lt->blockCount > 0;
|
|
|
|
|
|
|
|
return nullptr != get<BlockedType>(ty) || nullptr != get<PendingExpansionType>(ty);
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
bool ConstraintSolver::isBlocked(TypePackId tp)
|
|
|
|
{
|
|
|
|
return nullptr != get<BlockedTypePack>(follow(tp));
|
|
|
|
}
|
|
|
|
|
2022-06-16 17:54:42 -07:00
|
|
|
bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint)
|
2022-06-03 13:32:20 -07:00
|
|
|
{
|
2022-06-16 17:54:42 -07:00
|
|
|
auto blockedIt = blockedConstraints.find(constraint);
|
|
|
|
return blockedIt != blockedConstraints.end() && blockedIt->second > 0;
|
2022-06-03 13:32:20 -07:00
|
|
|
}
|
|
|
|
|
2023-02-17 16:53:37 +02:00
|
|
|
NotNull<Constraint> ConstraintSolver::pushConstraint(NotNull<Scope> scope, const Location& location, ConstraintV cv)
|
2022-08-04 14:27:28 -07:00
|
|
|
{
|
2022-09-01 16:00:14 -07:00
|
|
|
std::unique_ptr<Constraint> c = std::make_unique<Constraint>(scope, location, std::move(cv));
|
2022-08-04 14:27:28 -07:00
|
|
|
NotNull<Constraint> borrow = NotNull(c.get());
|
|
|
|
solverConstraints.push_back(std::move(c));
|
|
|
|
unsolvedConstraints.push_back(borrow);
|
2023-02-17 16:53:37 +02:00
|
|
|
|
|
|
|
return borrow;
|
2022-08-04 14:27:28 -07:00
|
|
|
}
|
|
|
|
|
2022-09-01 16:00:14 -07:00
|
|
|
TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& location)
|
|
|
|
{
|
|
|
|
if (info.name.empty())
|
|
|
|
{
|
|
|
|
reportError(UnknownRequire{}, location);
|
2022-09-15 15:13:58 -07:00
|
|
|
return errorRecoveryType();
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& [location, path] : requireCycles)
|
|
|
|
{
|
2023-05-25 23:46:51 +03:00
|
|
|
if (!path.empty() && path.front() == info.name)
|
2023-01-03 19:33:19 +02:00
|
|
|
return builtinTypes->anyType;
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ModulePtr module = moduleResolver->getModule(info.name);
|
|
|
|
if (!module)
|
|
|
|
{
|
|
|
|
if (!moduleResolver->moduleExists(info.name) && !info.optional)
|
2023-04-21 14:41:03 -07:00
|
|
|
reportError(UnknownRequire{moduleResolver->getHumanReadableModuleName(info.name)}, location);
|
2022-09-01 16:00:14 -07:00
|
|
|
|
2022-09-15 15:13:58 -07:00
|
|
|
return errorRecoveryType();
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (module->type != SourceCode::Type::Module)
|
|
|
|
{
|
2023-04-21 14:41:03 -07:00
|
|
|
reportError(IllegalRequire{module->humanReadableName, "Module is not a ModuleScript. It cannot be required."}, location);
|
2022-09-15 15:13:58 -07:00
|
|
|
return errorRecoveryType();
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
|
|
|
|
2023-02-10 10:50:54 -08:00
|
|
|
TypePackId modulePack = module->returnType;
|
2022-09-01 16:00:14 -07:00
|
|
|
if (get<Unifiable::Error>(modulePack))
|
2022-09-15 15:13:58 -07:00
|
|
|
return errorRecoveryType();
|
2022-09-01 16:00:14 -07:00
|
|
|
|
|
|
|
std::optional<TypeId> moduleType = first(modulePack);
|
|
|
|
if (!moduleType)
|
|
|
|
{
|
2023-04-21 14:41:03 -07:00
|
|
|
reportError(IllegalRequire{module->humanReadableName, "Module does not return exactly 1 value. It cannot be required."}, location);
|
2022-09-15 15:13:58 -07:00
|
|
|
return errorRecoveryType();
|
2022-09-01 16:00:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return *moduleType;
|
|
|
|
}
|
|
|
|
|
2022-08-18 14:04:33 -07:00
|
|
|
void ConstraintSolver::reportError(TypeErrorData&& data, const Location& location)
|
|
|
|
{
|
|
|
|
errors.emplace_back(location, std::move(data));
|
2022-09-01 16:00:14 -07:00
|
|
|
errors.back().moduleName = currentModuleName;
|
2022-08-18 14:04:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void ConstraintSolver::reportError(TypeError e)
|
|
|
|
{
|
|
|
|
errors.emplace_back(std::move(e));
|
2022-09-01 16:00:14 -07:00
|
|
|
errors.back().moduleName = currentModuleName;
|
2022-08-18 14:04:33 -07:00
|
|
|
}
|
|
|
|
|
2023-09-22 11:10:49 -07:00
|
|
|
bool ConstraintSolver::hasUnresolvedConstraints(TypeId ty)
|
|
|
|
{
|
2023-10-27 12:33:36 -07:00
|
|
|
if (auto refCount = unresolvedConstraints.find(ty))
|
|
|
|
return *refCount > 0;
|
2023-09-22 11:10:49 -07:00
|
|
|
|
2023-10-27 12:33:36 -07:00
|
|
|
return false;
|
2023-09-22 11:10:49 -07:00
|
|
|
}
|
|
|
|
|
2022-09-15 15:13:58 -07:00
|
|
|
TypeId ConstraintSolver::errorRecoveryType() const
|
|
|
|
{
|
2023-01-03 19:33:19 +02:00
|
|
|
return builtinTypes->errorRecoveryType();
|
2022-09-15 15:13:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TypePackId ConstraintSolver::errorRecoveryTypePack() const
|
|
|
|
{
|
2023-01-03 19:33:19 +02:00
|
|
|
return builtinTypes->errorRecoveryTypePack();
|
2022-09-15 15:13:58 -07:00
|
|
|
}
|
|
|
|
|
2023-05-19 11:59:59 -07:00
|
|
|
TypePackId ConstraintSolver::anyifyModuleReturnTypePackGenerics(TypePackId tp)
|
|
|
|
{
|
|
|
|
tp = follow(tp);
|
|
|
|
|
|
|
|
if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
|
|
|
|
{
|
|
|
|
TypeId ty = follow(vtp->ty);
|
|
|
|
return get<GenericType>(ty) ? builtinTypes->anyTypePack : tp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!get<TypePack>(follow(tp)))
|
|
|
|
return tp;
|
|
|
|
|
|
|
|
std::vector<TypeId> resultTypes;
|
|
|
|
std::optional<TypePackId> resultTail;
|
|
|
|
|
|
|
|
TypePackIterator it = begin(tp);
|
|
|
|
|
|
|
|
for (TypePackIterator e = end(tp); it != e; ++it)
|
|
|
|
{
|
|
|
|
TypeId ty = follow(*it);
|
|
|
|
resultTypes.push_back(get<GenericType>(ty) ? builtinTypes->anyType : ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::optional<TypePackId> tail = it.tail())
|
|
|
|
resultTail = anyifyModuleReturnTypePackGenerics(*tail);
|
|
|
|
|
|
|
|
return arena->addTypePack(resultTypes, resultTail);
|
|
|
|
}
|
|
|
|
|
2023-07-28 14:37:00 +03:00
|
|
|
LUAU_NOINLINE void ConstraintSolver::throwTimeLimitError()
|
|
|
|
{
|
|
|
|
throw TimeLimitError(currentModuleName);
|
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_NOINLINE void ConstraintSolver::throwUserCancelError()
|
|
|
|
{
|
|
|
|
throw UserCancelError(currentModuleName);
|
|
|
|
}
|
|
|
|
|
2022-06-03 13:32:20 -07:00
|
|
|
} // namespace Luau
|