Skip to content

Commit

Permalink
Sync to upstream/release/576 (#928)
Browse files Browse the repository at this point in the history
* `ClassType` can now have an indexer defined on it. This allows custom
types to be used in `t[x]` expressions.
* Fixed search for closest executable breakpoint line. Previously,
breakpoints might have been skipped in `else` blocks at the end of a
function
* Fixed how unification is performed for two optional types `a? <: b?`,
previously it might have unified either 'a' or 'b' with 'nil'. Note that
this fix is not enabled by default yet (see the list in
`ExperimentalFlags.h`)

In the new type solver, a concept of 'Type Families' has been
introduced.
Type families can be thought of as type aliases with custom type
inference/reduction logic included with them.
For example, we can have an `Add<T, U>` type family that will resolve
the type that is the result of adding two values together.
This will help type inference to figure out what 'T' and 'U' might be
when explicit type annotations are not provided.
In this update we don't define any type families, but they will be added
in the near future.
It is also possible for Luau embedders to define their own type families
in the global/environment scope.

Other changes include:
* Fixed scope used to find out which generic types should be included in
the function generic type list
* Fixed a crash after cyclic bound types were created during unification

And in native code generation (jit):
* Use of arm64 target on M1 now requires macOS 13
* Entry into native code has been optimized. This is especially
important for coroutine call/pcall performance as they involve going
through a C call frame
* LOP_LOADK(X) translation into IR has been improved to enable type
tag/constant propagation
* arm64 can use integer immediate values to synthesize floating-point
values
* x64 assembler removes duplicate 64bit numbers from the data section to
save space
* Linux `perf` can now be used to profile native Luau code (when running
with --codegen-perf CLI argument)
  • Loading branch information
vegorov-rbx authored May 12, 2023
1 parent 8d8c797 commit 97965c7
Show file tree
Hide file tree
Showing 62 changed files with 2,869 additions and 276 deletions.
23 changes: 20 additions & 3 deletions Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,26 @@ struct UnpackConstraint
TypePackId sourcePack;
};

using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, UnaryConstraint,
BinaryConstraint, IterableConstraint, NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, PrimitiveTypeConstraint,
HasPropConstraint, SetPropConstraint, SetIndexerConstraint, SingletonOrTopTypeConstraint, UnpackConstraint>;
// ty ~ reduce ty
//
// Try to reduce ty, if it is a TypeFamilyInstanceType. 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, InstantiationConstraint, UnaryConstraint, BinaryConstraint,
IterableConstraint, NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, PrimitiveTypeConstraint, HasPropConstraint,
SetPropConstraint, SetIndexerConstraint, SingletonOrTopTypeConstraint, UnpackConstraint, ReduceConstraint, ReducePackConstraint>;

struct Constraint
{
Expand Down
2 changes: 2 additions & 0 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ struct ConstraintSolver
bool tryDispatch(const SetIndexerConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const SingletonOrTopTypeConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);

// for a, ... in some_table do
// also handles __iter metamethod
Expand Down
27 changes: 21 additions & 6 deletions Analysis/include/Luau/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,12 +329,27 @@ struct DynamicPropertyLookupOnClassesUnsafe
bool operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const;
};

using TypeErrorData = Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods,
DuplicateTypeDefinition, CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire,
IncorrectGenericParameterCount, SyntaxError, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError,
CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning,
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty,
TypesAreUnrelated, NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe>;
struct UninhabitedTypeFamily
{
TypeId ty;

bool operator==(const UninhabitedTypeFamily& rhs) const;
};

struct UninhabitedTypePackFamily
{
TypePackId tp;

bool operator==(const UninhabitedTypePackFamily& rhs) const;
};

using TypeErrorData =
Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods, DuplicateTypeDefinition,
CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire, IncorrectGenericParameterCount, SyntaxError,
CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError, CannotCallNonFunction, ExtraInformation,
DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning, DuplicateGenericParameter,
CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty, TypesAreUnrelated,
NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFamily, UninhabitedTypePackFamily>;

struct TypeErrorSummary
{
Expand Down
28 changes: 27 additions & 1 deletion Analysis/include/Luau/Quantify.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#pragma once

#include "Luau/Type.h"
#include "Luau/DenseHash.h"

#include <vector>

namespace Luau
{
Expand All @@ -10,6 +13,29 @@ struct TypeArena;
struct Scope;

void quantify(TypeId ty, TypeLevel level);
std::optional<TypeId> quantify(TypeArena* arena, TypeId ty, Scope* scope);

// TODO: This is eerily similar to the pattern that NormalizedClassType
// implements. We could, and perhaps should, merge them together.
template<typename K, typename V>
struct OrderedMap
{
std::vector<K> keys;
DenseHashMap<K, V> pairings{nullptr};

void push(K k, V v)
{
keys.push_back(k);
pairings[k] = v;
}
};

struct QuantifierResult
{
TypeId result;
OrderedMap<TypeId, TypeId> insertedGenerics;
OrderedMap<TypePackId, TypePackId> insertedGenericPacks;
};

std::optional<QuantifierResult> quantify(TypeArena* arena, TypeId ty, Scope* scope);

} // namespace Luau
13 changes: 12 additions & 1 deletion Analysis/include/Luau/TxnLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ struct PendingType
// The pending Type state.
Type pending;

// On very rare occasions, we need to delete an entry from the TxnLog.
// DenseHashMap does not afford that so we note its deadness here.
bool dead = false;

explicit PendingType(Type state)
: pending(std::move(state))
{
Expand Down Expand Up @@ -61,10 +65,11 @@ T* getMutable(PendingTypePack* pending)
// Log of what TypeIds we are rebinding, to be committed later.
struct TxnLog
{
TxnLog()
explicit TxnLog(bool useScopes = false)
: typeVarChanges(nullptr)
, typePackChanges(nullptr)
, ownedSeen()
, useScopes(useScopes)
, sharedSeen(&ownedSeen)
{
}
Expand Down Expand Up @@ -297,6 +302,12 @@ struct TxnLog
void popSeen(TypeOrPackId lhs, TypeOrPackId rhs);

public:
// There is one spot in the code where TxnLog has to reconcile collisions
// between parallel logs. In that codepath, we have to work out which of two
// FreeTypes subsumes the other. If useScopes is false, the TypeLevel is
// used. Else we use the embedded Scope*.
bool useScopes = false;

// Used to avoid infinite recursion when types are cyclic.
// Shared with all the descendent TxnLogs.
std::vector<std::pair<TypeOrPackId, TypeOrPackId>>* sharedSeen;
Expand Down
38 changes: 36 additions & 2 deletions Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

LUAU_FASTINT(LuauTableTypeMaximumStringifierLength)
LUAU_FASTINT(LuauTypeMaximumStringifierLength)
LUAU_FASTFLAG(LuauTypecheckClassTypeIndexers)

namespace Luau
{
Expand All @@ -31,6 +32,8 @@ struct TypeArena;
struct Scope;
using ScopePtr = std::shared_ptr<Scope>;

struct TypeFamily;

/**
* There are three kinds of type variables:
* - `Free` variables are metavariables, which stand for unconstrained types.
Expand Down Expand Up @@ -489,6 +492,7 @@ struct ClassType
Tags tags;
std::shared_ptr<ClassUserData> userData;
ModuleName definitionModuleName;
std::optional<TableIndexer> indexer;

ClassType(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName)
Expand All @@ -501,6 +505,35 @@ struct ClassType
, definitionModuleName(definitionModuleName)
{
}

ClassType(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName, std::optional<TableIndexer> indexer)
: name(name)
, props(props)
, parent(parent)
, metatable(metatable)
, tags(tags)
, userData(userData)
, definitionModuleName(definitionModuleName)
, indexer(indexer)
{
LUAU_ASSERT(FFlag::LuauTypecheckClassTypeIndexers);
}
};

/**
* An instance of a type family that has not yet been reduced to a more concrete
* type. The constraint solver receives a constraint to reduce each
* TypeFamilyInstanceType to a concrete type. A design detail is important to
* note here: the parameters for this instantiation of the type family are
* contained within this type, so that they can be substituted.
*/
struct TypeFamilyInstanceType
{
NotNull<TypeFamily> family;

std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments;
};

struct TypeFun
Expand Down Expand Up @@ -640,8 +673,9 @@ struct NegationType

using ErrorType = Unifiable::Error;

using TypeVariant = Unifiable::Variant<TypeId, FreeType, GenericType, PrimitiveType, BlockedType, PendingExpansionType, SingletonType, FunctionType,
TableType, MetatableType, ClassType, AnyType, UnionType, IntersectionType, LazyType, UnknownType, NeverType, NegationType>;
using TypeVariant =
Unifiable::Variant<TypeId, FreeType, GenericType, PrimitiveType, BlockedType, PendingExpansionType, SingletonType, FunctionType, TableType,
MetatableType, ClassType, AnyType, UnionType, IntersectionType, LazyType, UnknownType, NeverType, NegationType, TypeFamilyInstanceType>;

struct Type final
{
Expand Down
115 changes: 115 additions & 0 deletions Analysis/include/Luau/TypeFamily.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include "Luau/Error.h"
#include "Luau/NotNull.h"
#include "Luau/Variant.h"

#include <functional>
#include <string>
#include <optional>

namespace Luau
{

struct Type;
using TypeId = const Type*;

struct TypePackVar;
using TypePackId = const TypePackVar*;

struct TypeArena;
struct BuiltinTypes;
struct TxnLog;

/// Represents a reduction result, which may have successfully reduced the type,
/// may have concretely failed to reduce the type, or may simply be stuck
/// without more information.
template<typename Ty>
struct TypeFamilyReductionResult
{
/// The result of the reduction, if any. If this is nullopt, the family
/// could not be reduced.
std::optional<Ty> result;
/// Whether the result is uninhabited: whether we know, unambiguously and
/// permanently, whether this type family reduction results in an
/// uninhabitable type. This will trigger an error to be reported.
bool uninhabited;
/// Any types that need to be progressed or mutated before the reduction may
/// proceed.
std::vector<TypeId> blockedTypes;
/// Any type packs that need to be progressed or mutated before the
/// reduction may proceed.
std::vector<TypePackId> blockedPacks;
};

/// Represents a type function that may be applied to map a series of types and
/// type packs to a single output type.
struct TypeFamily
{
/// The human-readable name of the type family. Used to stringify instance
/// types.
std::string name;

/// The reducer function for the type family.
std::function<TypeFamilyReductionResult<TypeId>(
std::vector<TypeId>, std::vector<TypePackId>, NotNull<TypeArena>, NotNull<BuiltinTypes>, NotNull<const TxnLog> log)>
reducer;
};

/// Represents a type function that may be applied to map a series of types and
/// type packs to a single output type pack.
struct TypePackFamily
{
/// The human-readable name of the type pack family. Used to stringify
/// instance packs.
std::string name;

/// The reducer function for the type pack family.
std::function<TypeFamilyReductionResult<TypePackId>(
std::vector<TypeId>, std::vector<TypePackId>, NotNull<TypeArena>, NotNull<BuiltinTypes>, NotNull<const TxnLog> log)>
reducer;
};

struct FamilyGraphReductionResult
{
ErrorVec errors;
DenseHashSet<TypeId> blockedTypes{nullptr};
DenseHashSet<TypePackId> blockedPacks{nullptr};
DenseHashSet<TypeId> reducedTypes{nullptr};
DenseHashSet<TypePackId> reducedPacks{nullptr};
};

/**
* Attempt to reduce all instances of any type or type pack family in the type
* graph provided.
*
* @param entrypoint the entry point to the type graph.
* @param location the location the reduction is occurring at; used to populate
* type errors.
* @param arena an arena to allocate types into.
* @param builtins the built-in types.
* @param log a TxnLog to use. If one is provided, substitution will take place
* against the TxnLog, otherwise substitutions will directly mutate the type
* graph. Do not provide the empty TxnLog, as a result.
*/
FamilyGraphReductionResult reduceFamilies(
TypeId entrypoint, Location location, NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, TxnLog* log = nullptr, bool force = false);

/**
* Attempt to reduce all instances of any type or type pack family in the type
* graph provided.
*
* @param entrypoint the entry point to the type graph.
* @param location the location the reduction is occurring at; used to populate
* type errors.
* @param arena an arena to allocate types into.
* @param builtins the built-in types.
* @param log a TxnLog to use. If one is provided, substitution will take place
* against the TxnLog, otherwise substitutions will directly mutate the type
* graph. Do not provide the empty TxnLog, as a result.
*/
FamilyGraphReductionResult reduceFamilies(
TypePackId entrypoint, Location location, NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, TxnLog* log = nullptr, bool force = false);

} // namespace Luau
17 changes: 15 additions & 2 deletions Analysis/include/Luau/TypePack.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ namespace Luau
{

struct TypeArena;
struct TypePackFamily;
struct TxnLog;

struct TypePack;
struct VariadicTypePack;
struct BlockedTypePack;
struct TypeFamilyInstanceTypePack;

struct TypePackVar;
using TypePackId = const TypePackVar*;
Expand Down Expand Up @@ -50,10 +52,10 @@ struct GenericTypePack
};

using BoundTypePack = Unifiable::Bound<TypePackId>;

using ErrorTypePack = Unifiable::Error;

using TypePackVariant = Unifiable::Variant<TypePackId, FreeTypePack, GenericTypePack, TypePack, VariadicTypePack, BlockedTypePack>;
using TypePackVariant =
Unifiable::Variant<TypePackId, FreeTypePack, GenericTypePack, TypePack, VariadicTypePack, BlockedTypePack, TypeFamilyInstanceTypePack>;

/* A TypePack is a rope-like string of TypeIds. We use this structure to encode
* notions like packs of unknown length and packs of any length, as well as more
Expand Down Expand Up @@ -83,6 +85,17 @@ struct BlockedTypePack
static size_t nextIndex;
};

/**
* Analogous to a TypeFamilyInstanceType.
*/
struct TypeFamilyInstanceTypePack
{
NotNull<TypePackFamily> family;

std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments;
};

struct TypePackVar
{
explicit TypePackVar(const TypePackVariant& ty);
Expand Down
Loading

0 comments on commit 97965c7

Please sign in to comment.