// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once #include "Luau/Ast.h" // Used for some of the enumerations #include "Luau/DenseHash.h" #include "Luau/NotNull.h" #include "Luau/Variant.h" #include "Luau/TypeFwd.h" #include #include #include namespace Luau { enum class ValueContext; struct Scope; // if resultType is a freeType, assignmentType <: freeType <: resultType bounds struct EqualityConstraint { TypeId resultType; TypeId assignmentType; }; // subType <: superType struct SubtypeConstraint { TypeId subType; TypeId superType; }; // subPack <: superPack struct PackSubtypeConstraint { TypePackId subPack; TypePackId superPack; // 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; }; // generalizedType ~ gen sourceType struct GeneralizationConstraint { TypeId generalizedType; TypeId sourceType; std::vector interiorTypes; }; // variables ~ iterate iterator // Unpack the iterator, figure out what types it iterates over, and bind those types to variables. struct IterableConstraint { TypePackId iterator; std::vector variables; const AstNode* nextAstFragment; DenseHashMap* astForInNextTypes; }; // name(namedType) = name struct NameConstraint { TypeId namedType; std::string name; bool synthetic = false; std::vector typeParameters; std::vector typePackParameters; }; // target ~ inst target struct TypeAliasExpansionConstraint { // Must be a PendingExpansionType. TypeId target; }; struct FunctionCallConstraint { TypeId fn; TypePackId argsPack; TypePackId result; class AstExprCall* callSite = nullptr; std::vector> discriminantTypes; // When we dispatch this constraint, we update the key at this map to record // the overload that we selected. DenseHashMap* astOverloadResolvedTypes = nullptr; }; // function_check fn argsPack // // 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; NotNull> astTypes; NotNull> astExpectedTypes; }; // prim FreeType ExpectedType PrimitiveType // // 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 struct PrimitiveTypeConstraint { TypeId freeType; // potentially gets used to force the lower bound? std::optional expectedType; // the primitive type to check against TypeId primitiveType; }; // 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; ValueContext context; // 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; // 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 = {success:true, result: T} | {success:false, error: E} // // local r: Result = {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; }; // 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; }; // assignProp lhsType propName rhsType // // Assign a value of type rhsType into the named property of lhsType. struct AssignPropConstraint { TypeId lhsType; std::string propName; TypeId rhsType; /// If a new property is to be inserted into a table type, it will be /// ascribed this location. std::optional propLocation; /// 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; // 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; }; struct AssignIndexConstraint { TypeId lhsType; TypeId indexType; 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. TypeId propType; }; // resultTypes ~ unpack sourceTypePack // // Similar to PackSubtypeConstraint, but with one important difference: If the // sourcePack is blocked, this constraint blocks. struct UnpackConstraint { std::vector resultPack; TypePackId sourcePack; }; // ty ~ reduce ty // // Try to reduce ty, if it is a TypeFunctionInstanceType. Otherwise, do nothing. struct ReduceConstraint { TypeId ty; }; // tp ~ reduce tp // // Analogous to ReduceConstraint, but for type packs. struct ReducePackConstraint { TypePackId tp; }; using ConstraintV = Variant< SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, IterableConstraint, NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, FunctionCheckConstraint, PrimitiveTypeConstraint, HasPropConstraint, HasIndexerConstraint, AssignPropConstraint, AssignIndexConstraint, UnpackConstraint, ReduceConstraint, ReducePackConstraint, EqualityConstraint>; struct Constraint { Constraint(NotNull scope, const Location& location, ConstraintV&& c); Constraint(const Constraint&) = delete; Constraint& operator=(const Constraint&) = delete; NotNull scope; Location location; ConstraintV c; std::vector> dependencies; DenseHashSet getMaybeMutatedFreeTypes() const; }; using ConstraintPtr = std::unique_ptr; bool isReferenceCountedType(const TypeId typ); inline Constraint& asMutable(const Constraint& c) { return const_cast(c); } template T* getMutable(Constraint& c) { return ::Luau::get_if(&c.c); } template const T* get(const Constraint& c) { return getMutable(asMutable(c)); } } // namespace Luau