2022-06-03 23:15:45 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
#include "Luau/Constraint.h"
|
2023-11-10 21:10:07 +00:00
|
|
|
#include "Luau/DenseHash.h"
|
2022-12-09 19:57:01 +00:00
|
|
|
#include "Luau/Error.h"
|
2023-11-10 21:10:07 +00:00
|
|
|
#include "Luau/Location.h"
|
2022-12-09 19:57:01 +00:00
|
|
|
#include "Luau/Module.h"
|
2022-10-07 01:23:29 +01:00
|
|
|
#include "Luau/Normalize.h"
|
2024-02-23 20:08:34 +00:00
|
|
|
#include "Luau/Substitution.h"
|
2022-12-09 19:57:01 +00:00
|
|
|
#include "Luau/ToString.h"
|
2023-01-04 20:53:17 +00:00
|
|
|
#include "Luau/Type.h"
|
2023-07-28 16:13:53 +01:00
|
|
|
#include "Luau/TypeCheckLimits.h"
|
2023-11-10 21:10:07 +00:00
|
|
|
#include "Luau/TypeFwd.h"
|
2022-12-09 19:57:01 +00:00
|
|
|
#include "Luau/Variant.h"
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-11-10 21:10:07 +00:00
|
|
|
#include <utility>
|
2022-06-03 23:15:45 +01:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2024-02-23 20:08:34 +00:00
|
|
|
enum class ValueContext;
|
|
|
|
|
2022-09-08 23:14:25 +01:00
|
|
|
struct DcrLogger;
|
|
|
|
|
2024-03-30 23:14:44 +00:00
|
|
|
class AstExpr;
|
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
// TypeId, TypePackId, or Constraint*. It is impossible to know which, but we
|
|
|
|
// never dereference this pointer.
|
2023-02-24 21:49:38 +00:00
|
|
|
using BlockedConstraintId = Variant<TypeId, TypePackId, const Constraint*>;
|
|
|
|
|
|
|
|
struct HashBlockedConstraintId
|
|
|
|
{
|
|
|
|
size_t operator()(const BlockedConstraintId& bci) const;
|
|
|
|
};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
struct ModuleResolver;
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
struct InstantiationSignature
|
|
|
|
{
|
|
|
|
TypeFun fn;
|
|
|
|
std::vector<TypeId> arguments;
|
|
|
|
std::vector<TypePackId> packArguments;
|
|
|
|
|
|
|
|
bool operator==(const InstantiationSignature& rhs) const;
|
|
|
|
bool operator!=(const InstantiationSignature& rhs) const
|
|
|
|
{
|
|
|
|
return !((*this) == rhs);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct HashInstantiationSignature
|
|
|
|
{
|
|
|
|
size_t operator()(const InstantiationSignature& signature) const;
|
|
|
|
};
|
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
struct ConstraintSolver
|
|
|
|
{
|
2023-04-21 23:14:26 +01:00
|
|
|
NotNull<TypeArena> arena;
|
2023-01-04 20:53:17 +00:00
|
|
|
NotNull<BuiltinTypes> builtinTypes;
|
2022-06-03 23:15:45 +01:00
|
|
|
InternalErrorReporter iceReporter;
|
2022-10-07 01:23:29 +01:00
|
|
|
NotNull<Normalizer> normalizer;
|
2022-08-04 23:35:33 +01:00
|
|
|
// The entire set of constraints that the solver is trying to resolve.
|
|
|
|
std::vector<NotNull<Constraint>> constraints;
|
2022-07-29 05:24:07 +01:00
|
|
|
NotNull<Scope> rootScope;
|
2022-09-02 00:14:03 +01:00
|
|
|
ModuleName currentModuleName;
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
// Constraints that the solver has generated, rather than sourcing from the
|
|
|
|
// scope tree.
|
|
|
|
std::vector<std::unique_ptr<Constraint>> solverConstraints;
|
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
// This includes every constraint that has not been fully solved.
|
|
|
|
// A constraint can be both blocked and unsolved, for instance.
|
2022-06-17 02:05:14 +01:00
|
|
|
std::vector<NotNull<const Constraint>> unsolvedConstraints;
|
2022-06-03 23:15:45 +01:00
|
|
|
|
|
|
|
// A mapping of constraint pointer to how many things the constraint is
|
|
|
|
// blocked on. Can be empty or 0 for constraints that are not blocked on
|
|
|
|
// anything.
|
2022-06-17 02:05:14 +01:00
|
|
|
std::unordered_map<NotNull<const Constraint>, size_t> blockedConstraints;
|
2022-06-03 23:15:45 +01:00
|
|
|
// A mapping of type/pack pointers to the constraints they block.
|
2024-01-27 03:20:56 +00:00
|
|
|
std::unordered_map<BlockedConstraintId, DenseHashSet<const Constraint*>, HashBlockedConstraintId> blocked;
|
2022-08-04 23:35:33 +01:00
|
|
|
// Memoized instantiations of type aliases.
|
|
|
|
DenseHashMap<InstantiationSignature, TypeId, HashInstantiationSignature> instantiatedAliases{{}};
|
2023-11-10 21:10:07 +00:00
|
|
|
// Breadcrumbs for where a free type's upper bound was expanded. We use
|
|
|
|
// these to provide more helpful error messages when a free type is solved
|
|
|
|
// as never unexpectedly.
|
|
|
|
DenseHashMap<TypeId, std::vector<std::pair<Location, TypeId>>> upperBoundContributors{nullptr};
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2023-10-27 22:18:41 +01:00
|
|
|
// A mapping from free types to the number of unresolved constraints that mention them.
|
|
|
|
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
|
|
|
|
|
2024-04-19 22:48:02 +01:00
|
|
|
// Irreducible/uninhabited type families or type pack families.
|
|
|
|
DenseHashSet<const void*> uninhabitedTypeFamilies{{}};
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
// Recorded errors that take place within the solver.
|
|
|
|
ErrorVec errors;
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
NotNull<ModuleResolver> moduleResolver;
|
|
|
|
std::vector<RequireCycle> requireCycles;
|
|
|
|
|
2022-09-08 23:14:25 +01:00
|
|
|
DcrLogger* logger;
|
2023-07-28 16:13:53 +01:00
|
|
|
TypeCheckLimits limits;
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-11-10 22:53:13 +00:00
|
|
|
explicit ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, std::vector<NotNull<Constraint>> constraints,
|
2023-07-28 16:13:53 +01:00
|
|
|
ModuleName moduleName, NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger,
|
|
|
|
TypeCheckLimits limits);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
// Randomize the order in which to dispatch constraints
|
|
|
|
void randomize(unsigned seed);
|
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
/**
|
|
|
|
* Attempts to dispatch all pending constraints and reach a type solution
|
2022-06-17 02:05:14 +01:00
|
|
|
* that satisfies all of the constraints.
|
2022-06-03 23:15:45 +01:00
|
|
|
**/
|
|
|
|
void run();
|
|
|
|
|
2022-10-14 20:48:41 +01:00
|
|
|
bool isDone();
|
|
|
|
|
2022-10-07 01:23:29 +01:00
|
|
|
/** Attempt to dispatch a constraint. Returns true if it was successful. If
|
|
|
|
* tryDispatch() returns false, the constraint remains in the unsolved set
|
|
|
|
* and will be retried later.
|
2022-07-01 00:52:43 +01:00
|
|
|
*/
|
2022-06-17 02:05:14 +01:00
|
|
|
bool tryDispatch(NotNull<const Constraint> c, bool force);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
bool tryDispatch(const SubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
|
|
|
|
bool tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
|
|
|
|
bool tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint, bool force);
|
2022-09-02 00:14:03 +01:00
|
|
|
bool tryDispatch(const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
|
2022-06-24 02:56:00 +01:00
|
|
|
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
|
2022-08-04 23:35:33 +01:00
|
|
|
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint);
|
2022-09-02 00:14:03 +01:00
|
|
|
bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint);
|
2024-01-27 03:20:56 +00:00
|
|
|
bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint);
|
2022-09-23 20:17:25 +01:00
|
|
|
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);
|
|
|
|
bool tryDispatch(const HasPropConstraint& c, NotNull<const Constraint> constraint);
|
2024-03-30 23:14:44 +00:00
|
|
|
bool tryDispatch(const SetPropConstraint& c, NotNull<const Constraint> constraint);
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2024-04-19 22:48:02 +01:00
|
|
|
bool tryDispatchHasIndexer(
|
|
|
|
int& recursionDepth, NotNull<const Constraint> constraint, TypeId subjectType, TypeId indexType, TypeId resultType, Set<TypeId>& seen);
|
2024-03-15 23:37:39 +00:00
|
|
|
bool tryDispatch(const HasIndexerConstraint& c, NotNull<const Constraint> constraint);
|
|
|
|
|
2024-04-19 22:48:02 +01:00
|
|
|
std::pair<bool, std::optional<TypeId>> tryDispatchSetIndexer(
|
|
|
|
NotNull<const Constraint> constraint, TypeId subjectType, TypeId indexType, TypeId propType, bool expandFreeTypeBounds);
|
2023-02-24 21:49:38 +00:00
|
|
|
bool tryDispatch(const SetIndexerConstraint& c, NotNull<const Constraint> constraint, bool force);
|
2024-03-30 23:14:44 +00:00
|
|
|
|
|
|
|
bool tryDispatchUnpack1(NotNull<const Constraint> constraint, TypeId resultType, TypeId sourceType, bool resultIsLValue);
|
2023-02-24 21:49:38 +00:00
|
|
|
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
|
2024-03-30 23:14:44 +00:00
|
|
|
bool tryDispatch(const Unpack1Constraint& c, NotNull<const Constraint> constraint);
|
|
|
|
|
2023-05-12 18:50:47 +01:00
|
|
|
bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
|
|
|
|
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
|
2024-02-23 20:08:34 +00:00
|
|
|
bool tryDispatch(const EqualityConstraint& c, NotNull<const Constraint> constraint, bool force);
|
2022-09-02 00:14:03 +01:00
|
|
|
|
|
|
|
// for a, ... in some_table do
|
2022-09-29 23:23:10 +01:00
|
|
|
// also handles __iter metamethod
|
2022-09-02 00:14:03 +01:00
|
|
|
bool tryDispatchIterableTable(TypeId iteratorTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
|
|
|
|
|
|
|
|
// for a, ... in next_function, t, ... do
|
|
|
|
bool tryDispatchIterableFunction(
|
|
|
|
TypeId nextTy, TypeId tableTy, TypeId firstIndexTy, const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2024-03-30 23:14:44 +00:00
|
|
|
std::pair<std::vector<TypeId>, std::optional<TypeId>> lookupTableProp(NotNull<const Constraint> constraint, TypeId subjectType,
|
|
|
|
const std::string& propName, ValueContext context, bool inConditional = false, bool suppressSimplification = false);
|
|
|
|
std::pair<std::vector<TypeId>, std::optional<TypeId>> lookupTableProp(NotNull<const Constraint> constraint, TypeId subjectType,
|
|
|
|
const std::string& propName, ValueContext context, bool inConditional, bool suppressSimplification, DenseHashSet<TypeId>& seen);
|
2022-11-18 19:47:21 +00:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
|
2022-06-03 23:15:45 +01:00
|
|
|
/**
|
2023-01-04 20:53:17 +00:00
|
|
|
* Block a constraint on the resolution of a Type.
|
2022-06-17 02:05:14 +01:00
|
|
|
* @returns false always. This is just to allow tryDispatch to return the result of block()
|
|
|
|
*/
|
|
|
|
bool block(TypeId target, NotNull<const Constraint> constraint);
|
|
|
|
bool block(TypePackId target, NotNull<const Constraint> constraint);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
// Block on every target
|
|
|
|
template<typename T>
|
|
|
|
bool block(const T& targets, NotNull<const Constraint> constraint)
|
|
|
|
{
|
|
|
|
for (TypeId target : targets)
|
|
|
|
block(target, constraint);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
/**
|
|
|
|
* For all constraints that are blocked on one constraint, make them block
|
|
|
|
* on a new constraint.
|
|
|
|
* @param source the constraint to copy blocks from.
|
|
|
|
* @param addition the constraint that other constraints should now block on.
|
|
|
|
*/
|
|
|
|
void inheritBlocks(NotNull<const Constraint> source, NotNull<const Constraint> addition);
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
// Traverse the type. If any pending types are found, block the constraint
|
|
|
|
// on them.
|
2022-09-23 20:17:25 +01:00
|
|
|
//
|
|
|
|
// Returns false if a type blocks the constraint.
|
|
|
|
//
|
|
|
|
// FIXME: This use of a boolean for the return result is an appalling
|
|
|
|
// interface.
|
2023-05-19 20:37:30 +01:00
|
|
|
bool blockOnPendingTypes(TypeId target, NotNull<const Constraint> constraint);
|
|
|
|
bool blockOnPendingTypes(TypePackId target, NotNull<const Constraint> constraint);
|
2022-09-23 20:17:25 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
void unblock(NotNull<const Constraint> progressed);
|
2023-06-16 18:35:18 +01:00
|
|
|
void unblock(TypeId progressed, Location location);
|
|
|
|
void unblock(TypePackId progressed, Location location);
|
|
|
|
void unblock(const std::vector<TypeId>& types, Location location);
|
|
|
|
void unblock(const std::vector<TypePackId>& packs, Location location);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
/**
|
|
|
|
* @returns true if the TypeId is in a blocked state.
|
|
|
|
*/
|
|
|
|
bool isBlocked(TypeId ty);
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
/**
|
|
|
|
* @returns true if the TypePackId is in a blocked state.
|
|
|
|
*/
|
|
|
|
bool isBlocked(TypePackId tp);
|
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
/**
|
|
|
|
* Returns whether the constraint is blocked on anything.
|
|
|
|
* @param constraint the constraint to check.
|
|
|
|
*/
|
2022-06-17 02:05:14 +01:00
|
|
|
bool isBlocked(NotNull<const Constraint> constraint);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
/** Pushes a new solver constraint to the solver.
|
|
|
|
* @param cv the body of the constraint.
|
|
|
|
**/
|
2023-02-17 23:41:51 +00:00
|
|
|
NotNull<Constraint> pushConstraint(NotNull<Scope> scope, const Location& location, ConstraintV cv);
|
2022-09-02 00:14:03 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to resolve a module from its module information. Returns the
|
|
|
|
* module-level return type of the module, or the error type if one cannot
|
|
|
|
* be found. Reports errors to the solver if the module cannot be found or
|
|
|
|
* the require is illegal.
|
|
|
|
* @param module the module information to look up.
|
|
|
|
* @param location the location where the require is taking place; used for
|
|
|
|
* error locations.
|
|
|
|
**/
|
|
|
|
TypeId resolveModule(const ModuleInfo& module, const Location& location);
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
void reportError(TypeErrorData&& data, const Location& location);
|
|
|
|
void reportError(TypeError e);
|
2022-09-02 00:14:03 +01:00
|
|
|
|
2023-09-22 20:12:15 +01:00
|
|
|
/**
|
|
|
|
* Checks the existing set of constraints to see if there exist any that contain
|
|
|
|
* the provided free type, indicating that it is not yet ready to be replaced by
|
|
|
|
* one of its bounds.
|
|
|
|
* @param ty the free type that to check for related constraints
|
|
|
|
* @returns whether or not it is unsafe to replace the free type by one of its bounds
|
|
|
|
*/
|
|
|
|
bool hasUnresolvedConstraints(TypeId ty);
|
|
|
|
|
2024-02-23 20:08:34 +00:00
|
|
|
/** Attempts to unify subTy with superTy. If doing so would require unifying
|
2023-03-31 19:42:49 +01:00
|
|
|
* BlockedTypes, fail and block the constraint on those BlockedTypes.
|
|
|
|
*
|
2024-02-23 20:08:34 +00:00
|
|
|
* Note: TID can only be TypeId or TypePackId.
|
|
|
|
*
|
2023-03-31 19:42:49 +01:00
|
|
|
* If unification fails, replace all free types with errorType.
|
|
|
|
*
|
|
|
|
* If unification succeeds, unblock every type changed by the unification.
|
2024-02-23 20:08:34 +00:00
|
|
|
*
|
|
|
|
* @returns true if the unification succeeded. False if the unification was
|
|
|
|
* too complex.
|
2023-03-31 19:42:49 +01:00
|
|
|
*/
|
2024-02-02 21:32:42 +00:00
|
|
|
template<typename TID>
|
2024-02-23 20:08:34 +00:00
|
|
|
bool unify(NotNull<const Constraint> constraint, TID subTy, TID superTy);
|
2023-03-31 19:42:49 +01:00
|
|
|
|
2024-02-23 20:08:34 +00:00
|
|
|
private:
|
2023-06-24 07:19:39 +01:00
|
|
|
/**
|
|
|
|
* Bind a BlockedType to another type while taking care not to bind it to
|
|
|
|
* itself in the case that resultTy == blockedTy. This can happen if we
|
|
|
|
* have a tautological constraint. When it does, we must instead bind
|
|
|
|
* blockedTy to a fresh type belonging to an appropriate scope.
|
|
|
|
*
|
|
|
|
* To determine which scope is appropriate, we also accept rootTy, which is
|
|
|
|
* to be the type that contains blockedTy.
|
2024-03-22 17:47:10 +00:00
|
|
|
*
|
|
|
|
* A constraint is required and will validate that blockedTy is owned by this
|
|
|
|
* constraint. This prevents one constraint from interfering with another's
|
|
|
|
* blocked types.
|
2023-06-24 07:19:39 +01:00
|
|
|
*/
|
2024-03-22 17:47:10 +00:00
|
|
|
void bindBlockedType(TypeId blockedTy, TypeId resultTy, TypeId rootTy, NotNull<const Constraint> constraint);
|
2023-06-24 07:19:39 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
/**
|
|
|
|
* Marks a constraint as being blocked on a type or type pack. The constraint
|
|
|
|
* solver will not attempt to dispatch blocked constraints until their
|
|
|
|
* dependencies have made progress.
|
|
|
|
* @param target the type or type pack pointer that the constraint is blocked on.
|
|
|
|
* @param constraint the constraint to block.
|
|
|
|
**/
|
2024-01-27 03:20:56 +00:00
|
|
|
bool block_(BlockedConstraintId target, NotNull<const Constraint> constraint);
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Informs the solver that progress has been made on a type or type pack. The
|
|
|
|
* solver will wake up all constraints that are blocked on the type or type pack,
|
|
|
|
* and will resume attempting to dispatch them.
|
|
|
|
* @param progressed the type or type pack pointer that has progressed.
|
|
|
|
**/
|
|
|
|
void unblock_(BlockedConstraintId progressed);
|
2022-09-02 00:14:03 +01:00
|
|
|
|
2024-02-23 20:08:34 +00:00
|
|
|
/**
|
|
|
|
* Reproduces any constraints necessary for new types that are copied when applying a substitution.
|
|
|
|
* At the time of writing, this pertains only to type families.
|
|
|
|
* @param subst the substitution that was applied
|
|
|
|
**/
|
|
|
|
void reproduceConstraints(NotNull<Scope> scope, const Location& location, const Substitution& subst);
|
|
|
|
|
2022-09-15 23:38:17 +01:00
|
|
|
TypeId errorRecoveryType() const;
|
|
|
|
TypePackId errorRecoveryTypePack() const;
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
TypePackId anyifyModuleReturnTypePackGenerics(TypePackId tp);
|
|
|
|
|
2023-07-28 16:13:53 +01:00
|
|
|
void throwTimeLimitError();
|
|
|
|
void throwUserCancelError();
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
ToStringOptions opts;
|
2022-06-03 23:15:45 +01:00
|
|
|
};
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
|
|
|
} // namespace Luau
|