luau/Analysis/include/Luau/Constraint.h

316 lines
8.6 KiB
C
Raw Normal View History

2022-06-17 01:54:42 +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-07-01 00:29:02 +01:00
#include "Luau/Ast.h" // Used for some of the enumerations
2022-12-09 18:07:25 +00:00
#include "Luau/DenseHash.h"
2022-06-17 01:54:42 +01:00
#include "Luau/NotNull.h"
2022-10-21 18:33:43 +01:00
#include "Luau/Variant.h"
2023-10-20 21:36:26 +01:00
#include "Luau/TypeFwd.h"
2022-06-17 01:54:42 +01:00
2022-06-24 02:44:07 +01:00
#include <string>
2022-06-17 01:54:42 +01:00
#include <memory>
#include <vector>
namespace Luau
{
2024-02-23 18:40:00 +00:00
enum class ValueContext;
2022-07-29 04:41:13 +01:00
struct Scope;
2024-02-23 18:40:00 +00:00
// if resultType is a freeType, assignmentType <: freeType <: resultType bounds
struct EqualityConstraint
{
TypeId resultType;
TypeId assignmentType;
};
2022-06-17 01:54:42 +01:00
// subType <: superType
struct SubtypeConstraint
{
TypeId subType;
TypeId superType;
};
// subPack <: superPack
struct PackSubtypeConstraint
{
TypePackId subPack;
TypePackId superPack;
2023-07-07 18:14:35 +01:00
// HACK!! TODO clip.
// We need to know which of `PackSubtypeConstraint` are emitted from `AstStatReturn` vs any others.
// Then we force these specific `PackSubtypeConstraint` to only dispatch in the order of the `return`s.
bool returns = false;
2022-06-17 01:54:42 +01:00
};
2022-08-25 21:55:08 +01:00
// generalizedType ~ gen sourceType
2022-06-17 01:54:42 +01:00
struct GeneralizationConstraint
{
TypeId generalizedType;
TypeId sourceType;
2024-02-23 18:40:00 +00:00
std::vector<TypeId> interiorTypes;
2022-06-17 01:54:42 +01:00
};
// variables ~ iterate iterator
// Unpack the iterator, figure out what types it iterates over, and bind those types to variables.
2022-09-02 00:00:14 +01:00
struct IterableConstraint
{
TypePackId iterator;
2024-06-07 18:09:03 +01:00
std::vector<TypeId> variables;
2023-05-25 21:46:51 +01:00
const AstNode* nextAstFragment;
2023-06-24 06:33:44 +01:00
DenseHashMap<const AstNode*, TypeId>* astForInNextTypes;
2022-09-02 00:00:14 +01:00
};
2022-06-24 02:44:07 +01:00
// name(namedType) = name
struct NameConstraint
{
TypeId namedType;
std::string name;
2023-01-27 21:28:45 +00:00
bool synthetic = false;
2023-02-17 14:53:37 +00:00
std::vector<TypeId> typeParameters;
std::vector<TypePackId> typePackParameters;
2022-06-24 02:44:07 +01:00
};
2022-08-04 22:27:28 +01:00
// target ~ inst target
struct TypeAliasExpansionConstraint
{
2023-01-03 17:33:19 +00:00
// Must be a PendingExpansionType.
2022-08-04 22:27:28 +01:00
TypeId target;
};
2022-09-02 00:00:14 +01:00
struct FunctionCallConstraint
{
TypeId fn;
2022-09-29 23:11:54 +01:00
TypePackId argsPack;
2022-09-02 00:00:14 +01:00
TypePackId result;
2023-07-07 18:14:35 +01:00
class AstExprCall* callSite = nullptr;
2023-02-10 18:50:54 +00:00
std::vector<std::optional<TypeId>> discriminantTypes;
2023-05-05 20:57:12 +01:00
// When we dispatch this constraint, we update the key at this map to record
// the overload that we selected.
2023-07-07 18:14:35 +01:00
DenseHashMap<const AstNode*, TypeId>* astOverloadResolvedTypes = nullptr;
2022-09-02 00:00:14 +01:00
};
2024-01-27 02:30:40 +00:00
// function_check fn argsPack
2022-09-23 19:32:10 +01:00
//
2024-01-27 02:30:40 +00:00
// If fn is a function type and argsPack is a partially solved
// pack of arguments to be supplied to the function, propagate the argument
// types of fn into the types of argsPack. This is used to implement
// bidirectional inference of lambda arguments.
struct FunctionCheckConstraint
{
TypeId fn;
TypePackId argsPack;
class AstExprCall* callSite = nullptr;
2024-03-22 17:21:27 +00:00
NotNull<DenseHashMap<const AstExpr*, TypeId>> astTypes;
2024-02-09 17:32:52 +00:00
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
2024-01-27 02:30:40 +00:00
};
// prim FreeType ExpectedType PrimitiveType
2022-09-23 19:32:10 +01:00
//
2024-01-27 02:30:40 +00:00
// FreeType is bounded below by the singleton type and above by PrimitiveType
// initially. When this constraint is resolved, it will check that the bounds
// of the free type are well-formed by subtyping.
//
// If they are not well-formed, then FreeType is replaced by its lower bound
//
// If they are well-formed and ExpectedType is potentially a singleton (an
// actual singleton or a union that contains a singleton),
// then FreeType is replaced by its lower bound
//
// else FreeType is replaced by PrimitiveType
2022-09-23 19:32:10 +01:00
struct PrimitiveTypeConstraint
{
2024-01-27 02:30:40 +00:00
TypeId freeType;
// potentially gets used to force the lower bound?
std::optional<TypeId> expectedType;
// the primitive type to check against
TypeId primitiveType;
2022-09-23 19:32:10 +01:00
};
// result ~ hasProp type "prop_name"
//
// If the subject is a table, bind the result to the named prop. If the table
// has an indexer, bind it to the index result type. If the subject is a union,
// bind the result to the union of its constituents' properties.
//
// It would be nice to get rid of this constraint and someday replace it with
//
// T <: {p: X}
//
// Where {} describes an inexact shape type.
struct HasPropConstraint
{
TypeId resultType;
TypeId subjectType;
std::string prop;
2024-02-23 18:40:00 +00:00
ValueContext context;
2023-05-19 19:59:59 +01:00
2024-03-15 21:01:00 +00:00
// We want to track if this `HasPropConstraint` comes from a conditional.
// If it does, we're going to change the behavior of property look-up a bit.
// In particular, we're going to return `unknownType` for property lookups
// on `table` or inexact table types where the property is not present.
//
// This allows us to refine table types to have additional properties
// without reporting errors in typechecking on the property tests.
bool inConditional = false;
2023-05-19 19:59:59 +01:00
// HACK: We presently need types like true|false or string|"hello" when
// deciding whether a particular literal expression should have a singleton
// type. This boolean is set to true when extracting the property type of a
// value that may be a union of tables.
//
// For example, in the following code fragment, we want the lookup of the
// success property to yield true|false when extracting an expectedType in
// this expression:
//
// type Result<T, E> = {success:true, result: T} | {success:false, error: E}
//
// local r: Result<number, string> = {success=true, result=9}
//
// If we naively simplify the expectedType to boolean, we will erroneously
// compute the type boolean for the success property of the table literal.
// This causes type checking to fail.
bool suppressSimplification = false;
2022-09-23 19:32:10 +01:00
};
2024-03-15 21:01:00 +00:00
// resultType ~ hasIndexer subjectType indexType
//
// If the subject type is a table or table-like thing that supports indexing,
// populate the type result with the result type of such an index operation.
//
// If the subject is not indexable, resultType is bound to errorType.
struct HasIndexerConstraint
{
TypeId resultType;
TypeId subjectType;
TypeId indexType;
};
2024-06-07 18:09:03 +01:00
// assignProp lhsType propName rhsType
2023-02-24 18:24:22 +00:00
//
2024-05-31 18:46:33 +01:00
// Assign a value of type rhsType into the named property of lhsType.
struct AssignPropConstraint
2023-02-24 18:24:22 +00:00
{
2024-05-31 18:46:33 +01:00
TypeId lhsType;
std::string propName;
TypeId rhsType;
2024-07-08 21:22:11 +01:00
/// If a new property is to be inserted into a table type, it will be
/// ascribed this location.
std::optional<Location> propLocation;
2024-05-31 18:46:33 +01:00
/// The canonical write type of the property. It is _solely_ used to
/// populate astTypes during constraint resolution. Nothing should ever
/// block on it.
TypeId propType;
2024-06-07 18:09:03 +01:00
// When we generate constraints, we increment the remaining prop count on
// the table if we are able. This flag informs the solver as to whether or
// not it should in turn decrement the prop count when this constraint is
// dispatched.
bool decrementPropCount = false;
2024-05-31 18:46:33 +01:00
};
struct AssignIndexConstraint
{
TypeId lhsType;
2023-02-24 18:24:22 +00:00
TypeId indexType;
2024-05-31 18:46:33 +01:00
TypeId rhsType;
/// The canonical write type of the property. It is _solely_ used to
/// populate astTypes during constraint resolution. Nothing should ever
/// block on it.
2023-02-24 18:24:22 +00:00
TypeId propType;
};
2024-06-07 18:09:03 +01:00
// resultTypes ~ unpack sourceTypePack
2023-02-24 18:24:22 +00:00
//
// Similar to PackSubtypeConstraint, but with one important difference: If the
// sourcePack is blocked, this constraint blocks.
struct UnpackConstraint
{
2024-06-07 18:09:03 +01:00
std::vector<TypeId> resultPack;
2023-02-24 18:24:22 +00:00
TypePackId sourcePack;
2024-03-30 22:49:03 +00:00
};
2023-05-12 13:15:01 +01:00
// ty ~ reduce ty
//
2024-07-11 23:13:45 +01:00
// Try to reduce ty, if it is a TypeFunctionInstanceType. Otherwise, do nothing.
2023-05-12 13:15:01 +01:00
struct ReduceConstraint
{
TypeId ty;
};
// tp ~ reduce tp
//
// Analogous to ReduceConstraint, but for type packs.
struct ReducePackConstraint
{
TypePackId tp;
};
2024-08-02 00:25:12 +01:00
using ConstraintV = Variant<
SubtypeConstraint,
PackSubtypeConstraint,
GeneralizationConstraint,
IterableConstraint,
NameConstraint,
TypeAliasExpansionConstraint,
FunctionCallConstraint,
FunctionCheckConstraint,
PrimitiveTypeConstraint,
HasPropConstraint,
HasIndexerConstraint,
AssignPropConstraint,
AssignIndexConstraint,
UnpackConstraint,
ReduceConstraint,
ReducePackConstraint,
EqualityConstraint>;
2022-09-02 00:00:14 +01:00
2022-06-17 01:54:42 +01:00
struct Constraint
{
2022-09-02 00:00:14 +01:00
Constraint(NotNull<Scope> scope, const Location& location, ConstraintV&& c);
2022-06-17 01:54:42 +01:00
Constraint(const Constraint&) = delete;
Constraint& operator=(const Constraint&) = delete;
2022-09-02 00:00:14 +01:00
NotNull<Scope> scope;
2023-02-10 18:50:54 +00:00
Location location;
2022-06-17 01:54:42 +01:00
ConstraintV c;
2022-09-02 00:00:14 +01:00
2022-06-17 01:54:42 +01:00
std::vector<NotNull<Constraint>> dependencies;
2023-10-27 20:33:36 +01:00
2024-05-10 17:17:09 +01:00
DenseHashSet<TypeId> getMaybeMutatedFreeTypes() const;
2022-06-17 01:54:42 +01:00
};
2022-09-23 19:32:10 +01:00
using ConstraintPtr = std::unique_ptr<Constraint>;
2024-05-10 17:17:09 +01:00
bool isReferenceCountedType(const TypeId typ);
2022-06-17 01:54:42 +01:00
inline Constraint& asMutable(const Constraint& c)
{
return const_cast<Constraint&>(c);
}
template<typename T>
T* getMutable(Constraint& c)
{
return ::Luau::get_if<T>(&c.c);
}
template<typename T>
const T* get(const Constraint& c)
{
return getMutable<T>(asMutable(c));
}
} // namespace Luau