Sync to upstream/release/634 (#1325)

# What's Changed?

- Performance improvement in the old solver
- Bugfixes in the new solver

## Old Solver

- Mark types that do not need instantiation when being exported to
prevent unnecessary work from being done

## New Solver

- Refactored instances of "type family" with "type function"
- Index-out-of-bounds bug fix in the resolution resolver
- Subtyping reasonings are merged only if all failed

---
### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
Junseo Yoo 2024-07-12 10:03:36 -07:00 committed by GitHub
parent 6bfc38e61a
commit b6b74b4425
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
51 changed files with 812 additions and 771 deletions

View File

@ -242,7 +242,7 @@ struct UnpackConstraint
// ty ~ reduce ty // ty ~ reduce ty
// //
// Try to reduce ty, if it is a TypeFamilyInstanceType. Otherwise, do nothing. // Try to reduce ty, if it is a TypeFunctionInstanceType. Otherwise, do nothing.
struct ReduceConstraint struct ReduceConstraint
{ {
TypeId ty; TypeId ty;

View File

@ -343,9 +343,9 @@ private:
void reportError(Location location, TypeErrorData err); void reportError(Location location, TypeErrorData err);
void reportCodeTooComplex(Location location); void reportCodeTooComplex(Location location);
// make a union type family of these two types // make a union type function of these two types
TypeId makeUnion(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs); TypeId makeUnion(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs);
// make an intersect type family of these two types // make an intersect type function of these two types
TypeId makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs); TypeId makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs);
/** Scan the program for global definitions. /** Scan the program for global definitions.
@ -370,8 +370,8 @@ private:
*/ */
std::vector<std::optional<TypeId>> getExpectedCallTypesForFunctionOverloads(const TypeId fnType); std::vector<std::optional<TypeId>> getExpectedCallTypesForFunctionOverloads(const TypeId fnType);
TypeId createTypeFamilyInstance( TypeId createTypeFunctionInstance(
const TypeFamily& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments, const ScopePtr& scope, Location location); const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments, const ScopePtr& scope, Location location);
}; };
/** Borrow a vector of pointers from a vector of owning pointers to constraints. /** Borrow a vector of pointers from a vector of owning pointers to constraints.

View File

@ -334,11 +334,11 @@ struct DynamicPropertyLookupOnClassesUnsafe
bool operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const; bool operator==(const DynamicPropertyLookupOnClassesUnsafe& rhs) const;
}; };
struct UninhabitedTypeFamily struct UninhabitedTypeFunction
{ {
TypeId ty; TypeId ty;
bool operator==(const UninhabitedTypeFamily& rhs) const; bool operator==(const UninhabitedTypeFunction& rhs) const;
}; };
struct ExplicitFunctionAnnotationRecommended struct ExplicitFunctionAnnotationRecommended
@ -348,11 +348,11 @@ struct ExplicitFunctionAnnotationRecommended
bool operator==(const ExplicitFunctionAnnotationRecommended& rhs) const; bool operator==(const ExplicitFunctionAnnotationRecommended& rhs) const;
}; };
struct UninhabitedTypePackFamily struct UninhabitedTypePackFunction
{ {
TypePackId tp; TypePackId tp;
bool operator==(const UninhabitedTypePackFamily& rhs) const; bool operator==(const UninhabitedTypePackFunction& rhs) const;
}; };
struct WhereClauseNeeded struct WhereClauseNeeded
@ -449,7 +449,7 @@ using TypeErrorData =
CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError, CannotCallNonFunction, ExtraInformation, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError, CannotCallNonFunction, ExtraInformation,
DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning, DuplicateGenericParameter, CannotAssignToNever, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning, DuplicateGenericParameter, CannotAssignToNever,
CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty, TypesAreUnrelated, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty, TypesAreUnrelated,
NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFamily, UninhabitedTypePackFamily, NormalizationTooComplex, TypePackMismatch, DynamicPropertyLookupOnClassesUnsafe, UninhabitedTypeFunction, UninhabitedTypePackFunction,
WhereClauseNeeded, PackWhereClauseNeeded, CheckedFunctionCallError, NonStrictFunctionDefinitionError, PropertyAccessViolation, WhereClauseNeeded, PackWhereClauseNeeded, CheckedFunctionCallError, NonStrictFunctionDefinitionError, PropertyAccessViolation,
CheckedFunctionIncorrectArgs, UnexpectedTypeInSubtyping, UnexpectedTypePackInSubtyping, ExplicitFunctionAnnotationRecommended>; CheckedFunctionIncorrectArgs, UnexpectedTypeInSubtyping, UnexpectedTypePackInSubtyping, ExplicitFunctionAnnotationRecommended>;

View File

@ -5,6 +5,7 @@
#include "Luau/Substitution.h" #include "Luau/Substitution.h"
#include "Luau/TypeFwd.h" #include "Luau/TypeFwd.h"
#include "Luau/Unifiable.h" #include "Luau/Unifiable.h"
#include "Luau/VisitType.h"
namespace Luau namespace Luau
{ {
@ -72,6 +73,59 @@ struct Instantiation : Substitution
TypePackId clean(TypePackId tp) override; TypePackId clean(TypePackId tp) override;
}; };
// Used to find if a FunctionType requires generic type cleanup during instantiation
struct GenericTypeFinder : TypeOnceVisitor
{
bool found = false;
bool visit(TypeId ty) override
{
return !found;
}
bool visit(TypePackId ty) override
{
return !found;
}
bool visit(TypeId ty, const Luau::FunctionType& ftv) override
{
if (ftv.hasNoFreeOrGenericTypes)
return false;
if (!ftv.generics.empty() || !ftv.genericPacks.empty())
found = true;
return !found;
}
bool visit(TypeId ty, const Luau::TableType& ttv) override
{
if (ttv.state == Luau::TableState::Generic)
found = true;
return !found;
}
bool visit(TypeId ty, const Luau::GenericType&) override
{
found = true;
return false;
}
bool visit(TypePackId ty, const Luau::GenericTypePack&) override
{
found = true;
return false;
}
bool visit(TypeId ty, const Luau::ClassType&) override
{
// During function instantiation, classes are not traversed even if they have generics
return false;
}
};
/** Attempt to instantiate a type. Only used under local type inference. /** Attempt to instantiate a type. Only used under local type inference.
* *
* When given a generic function type, instantiate() will return a copy with the * When given a generic function type, instantiate() will return a copy with the

View File

@ -5,7 +5,7 @@
#include "Luau/TypeFwd.h" #include "Luau/TypeFwd.h"
#include "Luau/TypePairHash.h" #include "Luau/TypePairHash.h"
#include "Luau/TypePath.h" #include "Luau/TypePath.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypeCheckLimits.h" #include "Luau/TypeCheckLimits.h"
#include "Luau/DenseHash.h" #include "Luau/DenseHash.h"
@ -114,7 +114,7 @@ struct SubtypingEnvironment
DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash> ephemeralCache{{}}; DenseHashMap<std::pair<TypeId, TypeId>, SubtypingResult, TypePairHash> ephemeralCache{{}};
/// Applies `mappedGenerics` to the given type. /// Applies `mappedGenerics` to the given type.
/// This is used specifically to substitute for generics in type family instances. /// This is used specifically to substitute for generics in type function instances.
std::optional<TypeId> applyMappedGenerics(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId ty); std::optional<TypeId> applyMappedGenerics(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, TypeId ty);
}; };
@ -219,8 +219,8 @@ private:
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes); SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const VariadicTypePack* subVariadic, const VariadicTypePack* superVariadic); SubtypingResult isCovariantWith(SubtypingEnvironment& env, const VariadicTypePack* subVariadic, const VariadicTypePack* superVariadic);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeFamilyInstanceType* subFamilyInstance, const TypeId superTy); SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFamilyInstance, const TypeId superTy);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFamilyInstanceType* superFamilyInstance); SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFamilyInstance);
bool bindGeneric(SubtypingEnvironment& env, TypeId subTp, TypeId superTp); bool bindGeneric(SubtypingEnvironment& env, TypeId subTp, TypeId superTp);
bool bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp); bool bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp);
@ -228,7 +228,7 @@ private:
template<typename T, typename Container> template<typename T, typename Container>
TypeId makeAggregateType(const Container& container, TypeId orElse); TypeId makeAggregateType(const Container& container, TypeId orElse);
std::pair<TypeId, ErrorVec> handleTypeFamilyReductionResult(const TypeFamilyInstanceType* familyInstance); std::pair<TypeId, ErrorVec> handleTypeFunctionReductionResult(const TypeFunctionInstanceType* familyInstance);
[[noreturn]] void unexpected(TypeId ty); [[noreturn]] void unexpected(TypeId ty);
[[noreturn]] void unexpected(TypePackId tp); [[noreturn]] void unexpected(TypePackId tp);

View File

@ -32,7 +32,7 @@ struct TypeArena;
struct Scope; struct Scope;
using ScopePtr = std::shared_ptr<Scope>; using ScopePtr = std::shared_ptr<Scope>;
struct TypeFamily; struct TypeFunction;
struct Constraint; struct Constraint;
/** /**
@ -528,34 +528,34 @@ struct ClassType
}; };
/** /**
* An instance of a type family that has not yet been reduced to a more concrete * An instance of a type function that has not yet been reduced to a more concrete
* type. The constraint solver receives a constraint to reduce each * type. The constraint solver receives a constraint to reduce each
* TypeFamilyInstanceType to a concrete type. A design detail is important to * TypeFunctionInstanceType to a concrete type. A design detail is important to
* note here: the parameters for this instantiation of the type family are * note here: the parameters for this instantiation of the type function are
* contained within this type, so that they can be substituted. * contained within this type, so that they can be substituted.
*/ */
struct TypeFamilyInstanceType struct TypeFunctionInstanceType
{ {
NotNull<const TypeFamily> family; NotNull<const TypeFunction> family;
std::vector<TypeId> typeArguments; std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments; std::vector<TypePackId> packArguments;
TypeFamilyInstanceType(NotNull<const TypeFamily> family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments) TypeFunctionInstanceType(NotNull<const TypeFunction> family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
: family(family) : family(family)
, typeArguments(typeArguments) , typeArguments(typeArguments)
, packArguments(packArguments) , packArguments(packArguments)
{ {
} }
TypeFamilyInstanceType(const TypeFamily& family, std::vector<TypeId> typeArguments) TypeFunctionInstanceType(const TypeFunction& family, std::vector<TypeId> typeArguments)
: family{&family} : family{&family}
, typeArguments(typeArguments) , typeArguments(typeArguments)
, packArguments{} , packArguments{}
{ {
} }
TypeFamilyInstanceType(const TypeFamily& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments) TypeFunctionInstanceType(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
: family{&family} : family{&family}
, typeArguments(typeArguments) , typeArguments(typeArguments)
, packArguments(packArguments) , packArguments(packArguments)
@ -659,7 +659,7 @@ using ErrorType = Unifiable::Error;
using TypeVariant = using TypeVariant =
Unifiable::Variant<TypeId, FreeType, GenericType, PrimitiveType, SingletonType, BlockedType, PendingExpansionType, FunctionType, TableType, Unifiable::Variant<TypeId, FreeType, GenericType, PrimitiveType, SingletonType, BlockedType, PendingExpansionType, FunctionType, TableType,
MetatableType, ClassType, AnyType, UnionType, IntersectionType, LazyType, UnknownType, NeverType, NegationType, TypeFamilyInstanceType>; MetatableType, ClassType, AnyType, UnionType, IntersectionType, LazyType, UnknownType, NeverType, NegationType, TypeFunctionInstanceType>;
struct Type final struct Type final
{ {

View File

@ -49,10 +49,10 @@ struct TypeArena
return addTypePack(TypePackVar(std::move(tp))); return addTypePack(TypePackVar(std::move(tp)));
} }
TypeId addTypeFamily(const TypeFamily& family, std::initializer_list<TypeId> types); TypeId addTypeFunction(const TypeFunction& family, std::initializer_list<TypeId> types);
TypeId addTypeFamily(const TypeFamily& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {}); TypeId addTypeFunction(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
TypePackId addTypePackFamily(const TypePackFamily& family, std::initializer_list<TypeId> types); TypePackId addTypePackFunction(const TypePackFunction& family, std::initializer_list<TypeId> types);
TypePackId addTypePackFamily(const TypePackFamily& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {}); TypePackId addTypePackFunction(const TypePackFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
}; };
void freeze(TypeArena& arena); void freeze(TypeArena& arena);

View File

@ -1,85 +0,0 @@
// 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"
#include "Luau/VecDeque.h"
#include "Luau/DenseHash.h"
#include "Luau/TypeFamily.h"
#include "Luau/Type.h"
#include "Luau/TypePack.h"
#include "Luau/TypeUtils.h"
#include "Luau/Normalize.h"
#include "Luau/TypeFwd.h"
#include "Luau/VisitType.h"
#include "Luau/NotNull.h"
#include "TypeArena.h"
namespace Luau
{
struct TypeFamilyReductionGuessResult
{
std::vector<std::pair<std::string, TypeId>> guessedFunctionAnnotations;
TypeId guessedReturnType;
bool shouldRecommendAnnotation = true;
};
// An Inference result for a type family is a list of types corresponding to the guessed argument types, followed by a type for the result
struct TypeFamilyInferenceResult
{
std::vector<TypeId> operandInference;
TypeId familyResultInference;
};
struct TypeFamilyReductionGuesser
{
// Tracks our hypothesis about what a type family reduces to
DenseHashMap<TypeId, TypeId> familyReducesTo{nullptr};
// Tracks our constraints on type family operands
DenseHashMap<TypeId, TypeId> substitutable{nullptr};
// List of instances to try progress
VecDeque<TypeId> toInfer;
DenseHashSet<TypeId> cyclicInstances{nullptr};
// Utilities
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtins;
NotNull<Normalizer> normalizer;
TypeFamilyReductionGuesser(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, NotNull<Normalizer> normalizer);
std::optional<TypeId> guess(TypeId typ);
std::optional<TypePackId> guess(TypePackId typ);
TypeFamilyReductionGuessResult guessTypeFamilyReductionForFunction(const AstExprFunction& expr, const FunctionType* ftv, TypeId retTy);
private:
std::optional<TypeId> guessType(TypeId arg);
void dumpGuesses();
bool isNumericBinopFamily(const TypeFamilyInstanceType& instance);
bool isComparisonFamily(const TypeFamilyInstanceType& instance);
bool isOrAndFamily(const TypeFamilyInstanceType& instance);
bool isNotFamily(const TypeFamilyInstanceType& instance);
bool isLenFamily(const TypeFamilyInstanceType& instance);
bool isUnaryMinus(const TypeFamilyInstanceType& instance);
// Operand is assignable if it looks like a cyclic family instance, or a generic type
bool operandIsAssignable(TypeId ty);
std::optional<TypeId> tryAssignOperandType(TypeId ty);
std::shared_ptr<const NormalizedType> normalize(TypeId ty);
void step();
void infer();
bool done();
bool isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& instanceArgs);
void inferTypeFamilySubstitutions(TypeId ty, const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferNumericBinopFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferComparisonFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferOrAndFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferNotFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferLenFamily(const TypeFamilyInstanceType* instance);
TypeFamilyInferenceResult inferUnaryMinusFamily(const TypeFamilyInstanceType* instance);
};
} // namespace Luau

View File

@ -18,7 +18,7 @@ struct TypeArena;
struct TxnLog; struct TxnLog;
class Normalizer; class Normalizer;
struct TypeFamilyContext struct TypeFunctionContext
{ {
NotNull<TypeArena> arena; NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtins; NotNull<BuiltinTypes> builtins;
@ -27,12 +27,12 @@ struct TypeFamilyContext
NotNull<InternalErrorReporter> ice; NotNull<InternalErrorReporter> ice;
NotNull<TypeCheckLimits> limits; NotNull<TypeCheckLimits> limits;
// nullptr if the type family is being reduced outside of the constraint solver. // nullptr if the type function is being reduced outside of the constraint solver.
ConstraintSolver* solver; ConstraintSolver* solver;
// The constraint being reduced in this run of the reduction // The constraint being reduced in this run of the reduction
const Constraint* constraint; const Constraint* constraint;
TypeFamilyContext(NotNull<ConstraintSolver> cs, NotNull<Scope> scope, NotNull<const Constraint> constraint) TypeFunctionContext(NotNull<ConstraintSolver> cs, NotNull<Scope> scope, NotNull<const Constraint> constraint)
: arena(cs->arena) : arena(cs->arena)
, builtins(cs->builtinTypes) , builtins(cs->builtinTypes)
, scope(scope) , scope(scope)
@ -44,7 +44,7 @@ struct TypeFamilyContext
{ {
} }
TypeFamilyContext(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, NotNull<Scope> scope, NotNull<Normalizer> normalizer, TypeFunctionContext(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, NotNull<Scope> scope, NotNull<Normalizer> normalizer,
NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits) NotNull<InternalErrorReporter> ice, NotNull<TypeCheckLimits> limits)
: arena(arena) : arena(arena)
, builtins(builtins) , builtins(builtins)
@ -64,13 +64,13 @@ struct TypeFamilyContext
/// may have concretely failed to reduce the type, or may simply be stuck /// may have concretely failed to reduce the type, or may simply be stuck
/// without more information. /// without more information.
template<typename Ty> template<typename Ty>
struct TypeFamilyReductionResult struct TypeFunctionReductionResult
{ {
/// The result of the reduction, if any. If this is nullopt, the family /// The result of the reduction, if any. If this is nullopt, the type function
/// could not be reduced. /// could not be reduced.
std::optional<Ty> result; std::optional<Ty> result;
/// Whether the result is uninhabited: whether we know, unambiguously and /// Whether the result is uninhabited: whether we know, unambiguously and
/// permanently, whether this type family reduction results in an /// permanently, whether this type function reduction results in an
/// uninhabitable type. This will trigger an error to be reported. /// uninhabitable type. This will trigger an error to be reported.
bool uninhabited; bool uninhabited;
/// Any types that need to be progressed or mutated before the reduction may /// Any types that need to be progressed or mutated before the reduction may
@ -83,33 +83,33 @@ struct TypeFamilyReductionResult
template<typename T> template<typename T>
using ReducerFunction = using ReducerFunction =
std::function<TypeFamilyReductionResult<T>(T, const std::vector<TypeId>&, const std::vector<TypePackId>&, NotNull<TypeFamilyContext>)>; std::function<TypeFunctionReductionResult<T>(T, const std::vector<TypeId>&, const std::vector<TypePackId>&, NotNull<TypeFunctionContext>)>;
/// Represents a type function that may be applied to map a series of types and /// Represents a type function that may be applied to map a series of types and
/// type packs to a single output type. /// type packs to a single output type.
struct TypeFamily struct TypeFunction
{ {
/// The human-readable name of the type family. Used to stringify instance /// The human-readable name of the type function. Used to stringify instance
/// types. /// types.
std::string name; std::string name;
/// The reducer function for the type family. /// The reducer function for the type function.
ReducerFunction<TypeId> reducer; ReducerFunction<TypeId> reducer;
}; };
/// Represents a type function that may be applied to map a series of types and /// Represents a type function that may be applied to map a series of types and
/// type packs to a single output type pack. /// type packs to a single output type pack.
struct TypePackFamily struct TypePackFunction
{ {
/// The human-readable name of the type pack family. Used to stringify /// The human-readable name of the type pack function. Used to stringify
/// instance packs. /// instance packs.
std::string name; std::string name;
/// The reducer function for the type pack family. /// The reducer function for the type pack function.
ReducerFunction<TypePackId> reducer; ReducerFunction<TypePackId> reducer;
}; };
struct FamilyGraphReductionResult struct FunctionGraphReductionResult
{ {
ErrorVec errors; ErrorVec errors;
DenseHashSet<TypeId> blockedTypes{nullptr}; DenseHashSet<TypeId> blockedTypes{nullptr};
@ -119,7 +119,7 @@ struct FamilyGraphReductionResult
}; };
/** /**
* Attempt to reduce all instances of any type or type pack family in the type * Attempt to reduce all instances of any type or type pack functions in the type
* graph provided. * graph provided.
* *
* @param entrypoint the entry point to the type graph. * @param entrypoint the entry point to the type graph.
@ -130,10 +130,10 @@ struct FamilyGraphReductionResult
* @param normalizer the normalizer to use when normalizing types * @param normalizer the normalizer to use when normalizing types
* @param ice the internal error reporter to use for ICEs * @param ice the internal error reporter to use for ICEs
*/ */
FamilyGraphReductionResult reduceFamilies(TypeId entrypoint, Location location, TypeFamilyContext, bool force = false); FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext, bool force = false);
/** /**
* Attempt to reduce all instances of any type or type pack family in the type * Attempt to reduce all instances of any type or type pack functions in the type
* graph provided. * graph provided.
* *
* @param entrypoint the entry point to the type graph. * @param entrypoint the entry point to the type graph.
@ -144,47 +144,46 @@ FamilyGraphReductionResult reduceFamilies(TypeId entrypoint, Location location,
* @param normalizer the normalizer to use when normalizing types * @param normalizer the normalizer to use when normalizing types
* @param ice the internal error reporter to use for ICEs * @param ice the internal error reporter to use for ICEs
*/ */
FamilyGraphReductionResult reduceFamilies(TypePackId entrypoint, Location location, TypeFamilyContext, bool force = false); FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, TypeFunctionContext, bool force = false);
struct BuiltinTypeFamilies struct BuiltinTypeFunctions
{ {
BuiltinTypeFamilies(); BuiltinTypeFunctions();
TypeFamily notFamily; TypeFunction notFunc;
TypeFamily lenFamily; TypeFunction lenFunc;
TypeFamily unmFamily; TypeFunction unmFunc;
TypeFamily addFamily; TypeFunction addFunc;
TypeFamily subFamily; TypeFunction subFunc;
TypeFamily mulFamily; TypeFunction mulFunc;
TypeFamily divFamily; TypeFunction divFunc;
TypeFamily idivFamily; TypeFunction idivFunc;
TypeFamily powFamily; TypeFunction powFunc;
TypeFamily modFamily; TypeFunction modFunc;
TypeFamily concatFamily; TypeFunction concatFunc;
TypeFamily andFamily; TypeFunction andFunc;
TypeFamily orFamily; TypeFunction orFunc;
TypeFamily ltFamily; TypeFunction ltFunc;
TypeFamily leFamily; TypeFunction leFunc;
TypeFamily eqFamily; TypeFunction eqFunc;
TypeFamily refineFamily; TypeFunction refineFunc;
TypeFamily singletonFamily; TypeFunction singletonFunc;
TypeFamily unionFamily; TypeFunction unionFunc;
TypeFamily intersectFamily; TypeFunction intersectFunc;
TypeFamily keyofFamily; TypeFunction keyofFunc;
TypeFamily rawkeyofFamily; TypeFunction rawkeyofFunc;
TypeFunction indexFunc;
TypeFamily indexFamily; TypeFunction rawgetFunc;
TypeFamily rawgetFamily;
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const; void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
}; };
const BuiltinTypeFamilies& builtinTypeFunctions(); const BuiltinTypeFunctions& builtinTypeFunctions();
} // namespace Luau } // namespace Luau

View File

@ -0,0 +1,85 @@
// 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"
#include "Luau/VecDeque.h"
#include "Luau/DenseHash.h"
#include "Luau/TypeFunction.h"
#include "Luau/Type.h"
#include "Luau/TypePack.h"
#include "Luau/TypeUtils.h"
#include "Luau/Normalize.h"
#include "Luau/TypeFwd.h"
#include "Luau/VisitType.h"
#include "Luau/NotNull.h"
#include "TypeArena.h"
namespace Luau
{
struct TypeFunctionReductionGuessResult
{
std::vector<std::pair<std::string, TypeId>> guessedFunctionAnnotations;
TypeId guessedReturnType;
bool shouldRecommendAnnotation = true;
};
// An Inference result for a type function is a list of types corresponding to the guessed argument types, followed by a type for the result
struct TypeFunctionInferenceResult
{
std::vector<TypeId> operandInference;
TypeId familyResultInference;
};
struct TypeFunctionReductionGuesser
{
// Tracks our hypothesis about what a type function reduces to
DenseHashMap<TypeId, TypeId> familyReducesTo{nullptr};
// Tracks our constraints on type function operands
DenseHashMap<TypeId, TypeId> substitutable{nullptr};
// List of instances to try progress
VecDeque<TypeId> toInfer;
DenseHashSet<TypeId> cyclicInstances{nullptr};
// Utilities
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtins;
NotNull<Normalizer> normalizer;
TypeFunctionReductionGuesser(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, NotNull<Normalizer> normalizer);
std::optional<TypeId> guess(TypeId typ);
std::optional<TypePackId> guess(TypePackId typ);
TypeFunctionReductionGuessResult guessTypeFunctionReductionForFunctionExpr(const AstExprFunction& expr, const FunctionType* ftv, TypeId retTy);
private:
std::optional<TypeId> guessType(TypeId arg);
void dumpGuesses();
bool isNumericBinopFamily(const TypeFunctionInstanceType& instance);
bool isComparisonFamily(const TypeFunctionInstanceType& instance);
bool isOrAndFamily(const TypeFunctionInstanceType& instance);
bool isNotFamily(const TypeFunctionInstanceType& instance);
bool isLenFamily(const TypeFunctionInstanceType& instance);
bool isUnaryMinus(const TypeFunctionInstanceType& instance);
// Operand is assignable if it looks like a cyclic family instance, or a generic type
bool operandIsAssignable(TypeId ty);
std::optional<TypeId> tryAssignOperandType(TypeId ty);
std::shared_ptr<const NormalizedType> normalize(TypeId ty);
void step();
void infer();
bool done();
bool isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& instanceArgs);
void inferTypeFunctionSubstitutions(TypeId ty, const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferNumericBinopFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferComparisonFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferOrAndFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferNotFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferLenFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferUnaryMinusFamily(const TypeFunctionInstanceType* instance);
};
} // namespace Luau

View File

@ -37,7 +37,7 @@ struct LazyType;
struct UnknownType; struct UnknownType;
struct NeverType; struct NeverType;
struct NegationType; struct NegationType;
struct TypeFamilyInstanceType; struct TypeFunctionInstanceType;
struct TypePackVar; struct TypePackVar;
using TypePackId = const TypePackVar*; using TypePackId = const TypePackVar*;
@ -47,7 +47,7 @@ struct GenericTypePack;
struct TypePack; struct TypePack;
struct VariadicTypePack; struct VariadicTypePack;
struct BlockedTypePack; struct BlockedTypePack;
struct TypeFamilyInstanceTypePack; struct TypeFunctionInstanceTypePack;
using Name = std::string; using Name = std::string;
using ModuleName = std::string; using ModuleName = std::string;

View File

@ -15,13 +15,13 @@ namespace Luau
{ {
struct TypeArena; struct TypeArena;
struct TypePackFamily; struct TypePackFunction;
struct TxnLog; struct TxnLog;
struct TypePack; struct TypePack;
struct VariadicTypePack; struct VariadicTypePack;
struct BlockedTypePack; struct BlockedTypePack;
struct TypeFamilyInstanceTypePack; struct TypeFunctionInstanceTypePack;
struct FreeTypePack struct FreeTypePack
{ {
@ -55,7 +55,7 @@ using BoundTypePack = Unifiable::Bound<TypePackId>;
using ErrorTypePack = Unifiable::Error; using ErrorTypePack = Unifiable::Error;
using TypePackVariant = using TypePackVariant =
Unifiable::Variant<TypePackId, FreeTypePack, GenericTypePack, TypePack, VariadicTypePack, BlockedTypePack, TypeFamilyInstanceTypePack>; Unifiable::Variant<TypePackId, FreeTypePack, GenericTypePack, TypePack, VariadicTypePack, BlockedTypePack, TypeFunctionInstanceTypePack>;
/* A TypePack is a rope-like string of TypeIds. We use this structure to encode /* 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 * notions like packs of unknown length and packs of any length, as well as more
@ -88,11 +88,11 @@ struct BlockedTypePack
}; };
/** /**
* Analogous to a TypeFamilyInstanceType. * Analogous to a TypeFunctionInstanceType.
*/ */
struct TypeFamilyInstanceTypePack struct TypeFunctionInstanceTypePack
{ {
NotNull<const TypePackFamily> family; NotNull<const TypePackFunction> family;
std::vector<TypeId> typeArguments; std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments; std::vector<TypePackId> packArguments;

View File

@ -7,6 +7,7 @@
#include "Luau/RecursionCounter.h" #include "Luau/RecursionCounter.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Type.h"
LUAU_FASTINT(LuauVisitRecursionLimit) LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauBoundLazyTypes2) LUAU_FASTFLAG(LuauBoundLazyTypes2)
@ -65,7 +66,7 @@ inline void unsee(DenseHashSet<void*>& seen, const void* tv)
} // namespace visit_detail } // namespace visit_detail
// recursion counter is equivalent here, but we'd like a better name to express the intent. // recursion counter is equivalent here, but we'd like a better name to express the intent.
using TypeFamilyDepthCounter = RecursionCounter; using TypeFunctionDepthCounter = RecursionCounter;
template<typename S> template<typename S>
struct GenericTypeVisitor struct GenericTypeVisitor
@ -75,7 +76,7 @@ struct GenericTypeVisitor
Set seen; Set seen;
bool skipBoundTypes = false; bool skipBoundTypes = false;
int recursionCounter = 0; int recursionCounter = 0;
int typeFamilyDepth = 0; int typeFunctionDepth = 0;
GenericTypeVisitor() = default; GenericTypeVisitor() = default;
@ -164,7 +165,7 @@ struct GenericTypeVisitor
{ {
return visit(ty); return visit(ty);
} }
virtual bool visit(TypeId ty, const TypeFamilyInstanceType& tfit) virtual bool visit(TypeId ty, const TypeFunctionInstanceType& tfit)
{ {
return visit(ty); return visit(ty);
} }
@ -201,7 +202,7 @@ struct GenericTypeVisitor
{ {
return visit(tp); return visit(tp);
} }
virtual bool visit(TypePackId tp, const TypeFamilyInstanceTypePack& tfitp) virtual bool visit(TypePackId tp, const TypeFunctionInstanceTypePack& tfitp)
{ {
return visit(tp); return visit(tp);
} }
@ -415,9 +416,9 @@ struct GenericTypeVisitor
if (visit(ty, *ntv)) if (visit(ty, *ntv))
traverse(ntv->ty); traverse(ntv->ty);
} }
else if (auto tfit = get<TypeFamilyInstanceType>(ty)) else if (auto tfit = get<TypeFunctionInstanceType>(ty))
{ {
TypeFamilyDepthCounter tfdc{&typeFamilyDepth}; TypeFunctionDepthCounter tfdc{&typeFunctionDepth};
if (visit(ty, *tfit)) if (visit(ty, *tfit))
{ {
@ -477,9 +478,9 @@ struct GenericTypeVisitor
} }
else if (auto btp = get<BlockedTypePack>(tp)) else if (auto btp = get<BlockedTypePack>(tp))
visit(tp, *btp); visit(tp, *btp);
else if (auto tfitp = get<TypeFamilyInstanceTypePack>(tp)) else if (auto tfitp = get<TypeFunctionInstanceTypePack>(tp))
{ {
TypeFamilyDepthCounter tfdc{&typeFamilyDepth}; TypeFunctionDepthCounter tfdc{&typeFunctionDepth};
if (visit(tp, *tfitp)) if (visit(tp, *tfitp))
{ {

View File

@ -10,7 +10,7 @@
#include "Luau/ConstraintGenerator.h" #include "Luau/ConstraintGenerator.h"
#include "Luau/NotNull.h" #include "Luau/NotNull.h"
#include "Luau/TypeInfer.h" #include "Luau/TypeInfer.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
@ -312,8 +312,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
{ {
// declare function assert<T>(value: T, errorMessage: string?): intersect<T, ~(false?)> // declare function assert<T>(value: T, errorMessage: string?): intersect<T, ~(false?)>
TypeId genericT = arena.addType(GenericType{"T"}); TypeId genericT = arena.addType(GenericType{"T"});
TypeId refinedTy = arena.addType(TypeFamilyInstanceType{ TypeId refinedTy = arena.addType(TypeFunctionInstanceType{
NotNull{&builtinTypeFunctions().intersectFamily}, {genericT, arena.addType(NegationType{builtinTypes->falsyType})}, {}}); NotNull{&builtinTypeFunctions().intersectFunc}, {genericT, arena.addType(NegationType{builtinTypes->falsyType})}, {}});
TypeId assertTy = arena.addType(FunctionType{ TypeId assertTy = arena.addType(FunctionType{
{genericT}, {}, arena.addTypePack(TypePack{{genericT, builtinTypes->optionalStringType}}), arena.addTypePack(TypePack{{refinedTy}})}); {genericT}, {}, arena.addTypePack(TypePack{{genericT, builtinTypes->optionalStringType}}), arena.addTypePack(TypePack{{refinedTy}})});

View File

@ -388,7 +388,7 @@ private:
t->ty = shallowClone(t->ty); t->ty = shallowClone(t->ty);
} }
void cloneChildren(TypeFamilyInstanceType* t) void cloneChildren(TypeFunctionInstanceType* t)
{ {
for (TypeId& ty : t->typeArguments) for (TypeId& ty : t->typeArguments)
ty = shallowClone(ty); ty = shallowClone(ty);
@ -432,7 +432,7 @@ private:
t->tail = shallowClone(*t->tail); t->tail = shallowClone(*t->tail);
} }
void cloneChildren(TypeFamilyInstanceTypePack* t) void cloneChildren(TypeFunctionInstanceTypePack* t)
{ {
for (TypeId& ty : t->typeArguments) for (TypeId& ty : t->typeArguments)
ty = shallowClone(ty); ty = shallowClone(ty);

View File

@ -16,7 +16,7 @@
#include "Luau/StringUtils.h" #include "Luau/StringUtils.h"
#include "Luau/TableLiteralInference.h" #include "Luau/TableLiteralInference.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
#include "Luau/Unifier2.h" #include "Luau/Unifier2.h"
@ -432,7 +432,7 @@ void ConstraintGenerator::computeRefinement(const ScopePtr& scope, Location loca
discriminantTy = arena->addType(NegationType{discriminantTy}); discriminantTy = arena->addType(NegationType{discriminantTy});
if (eq) if (eq)
discriminantTy = createTypeFamilyInstance(builtinTypeFunctions().singletonFamily, {discriminantTy}, {}, scope, location); discriminantTy = createTypeFunctionInstance(builtinTypeFunctions().singletonFunc, {discriminantTy}, {}, scope, location);
for (const RefinementKey* key = proposition->key; key; key = key->parent) for (const RefinementKey* key = proposition->key; key; key = key->parent)
{ {
@ -544,7 +544,7 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
{ {
if (mustDeferIntersection(ty) || mustDeferIntersection(dt)) if (mustDeferIntersection(ty) || mustDeferIntersection(dt))
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().refineFamily, {ty, dt}, {}, scope, location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().refineFunc, {ty, dt}, {}, scope, location);
ty = resultType; ty = resultType;
} }
@ -2090,17 +2090,17 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprUnary* unary)
{ {
case AstExprUnary::Op::Not: case AstExprUnary::Op::Not:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().notFamily, {operandType}, {}, scope, unary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().notFunc, {operandType}, {}, scope, unary->location);
return Inference{resultType, refinementArena.negation(refinement)}; return Inference{resultType, refinementArena.negation(refinement)};
} }
case AstExprUnary::Op::Len: case AstExprUnary::Op::Len:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().lenFamily, {operandType}, {}, scope, unary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().lenFunc, {operandType}, {}, scope, unary->location);
return Inference{resultType, refinementArena.negation(refinement)}; return Inference{resultType, refinementArena.negation(refinement)};
} }
case AstExprUnary::Op::Minus: case AstExprUnary::Op::Minus:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().unmFamily, {operandType}, {}, scope, unary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().unmFunc, {operandType}, {}, scope, unary->location);
return Inference{resultType, refinementArena.negation(refinement)}; return Inference{resultType, refinementArena.negation(refinement)};
} }
default: // msvc can't prove that this is exhaustive. default: // msvc can't prove that this is exhaustive.
@ -2116,75 +2116,75 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar
{ {
case AstExprBinary::Op::Add: case AstExprBinary::Op::Add:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().addFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().addFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Sub: case AstExprBinary::Op::Sub:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().subFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().subFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Mul: case AstExprBinary::Op::Mul:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().mulFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().mulFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Div: case AstExprBinary::Op::Div:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().divFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().divFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::FloorDiv: case AstExprBinary::Op::FloorDiv:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().idivFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().idivFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Pow: case AstExprBinary::Op::Pow:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().powFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().powFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Mod: case AstExprBinary::Op::Mod:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().modFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().modFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Concat: case AstExprBinary::Op::Concat:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().concatFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().concatFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::And: case AstExprBinary::Op::And:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().andFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().andFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Or: case AstExprBinary::Op::Or:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().orFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().orFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::CompareLt: case AstExprBinary::Op::CompareLt:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().ltFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().ltFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::CompareGe: case AstExprBinary::Op::CompareGe:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().ltFamily, TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().ltFunc,
{rightType, leftType}, // lua decided that `__ge(a, b)` is instead just `__lt(b, a)` {rightType, leftType}, // lua decided that `__ge(a, b)` is instead just `__lt(b, a)`
{}, scope, binary->location); {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::CompareLe: case AstExprBinary::Op::CompareLe:
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().leFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().leFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::CompareGt: case AstExprBinary::Op::CompareGt:
{ {
TypeId resultType = createTypeFamilyInstance( TypeId resultType = createTypeFunctionInstance(
builtinTypeFunctions().leFamily, builtinTypeFunctions().leFunc,
{rightType, leftType}, // lua decided that `__gt(a, b)` is instead just `__le(b, a)` {rightType, leftType}, // lua decided that `__gt(a, b)` is instead just `__le(b, a)`
{}, scope, binary->location); {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
@ -2206,7 +2206,7 @@ builtinTypeFunctions().leFamily,
else if (rightSubscripted) else if (rightSubscripted)
rightType = makeUnion(scope, binary->location, rightType, builtinTypes->nilType); rightType = makeUnion(scope, binary->location, rightType, builtinTypes->nilType);
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().eqFamily, {leftType, rightType}, {}, scope, binary->location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().eqFunc, {leftType, rightType}, {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Op__Count: case AstExprBinary::Op::Op__Count:
@ -3160,14 +3160,14 @@ TypeId ConstraintGenerator::makeUnion(const ScopePtr& scope, Location location,
if (get<NeverType>(follow(rhs))) if (get<NeverType>(follow(rhs)))
return lhs; return lhs;
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().unionFamily, {lhs, rhs}, {}, scope, location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().unionFunc, {lhs, rhs}, {}, scope, location);
return resultType; return resultType;
} }
TypeId ConstraintGenerator::makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs) TypeId ConstraintGenerator::makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs)
{ {
TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().intersectFamily, {lhs, rhs}, {}, scope, location); TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().intersectFunc, {lhs, rhs}, {}, scope, location);
return resultType; return resultType;
} }
@ -3285,7 +3285,7 @@ void ConstraintGenerator::fillInInferredBindings(const ScopePtr& globalScope, As
scope->bindings[symbol] = Binding{tys.front(), location}; scope->bindings[symbol] = Binding{tys.front(), location};
else else
{ {
TypeId ty = createTypeFamilyInstance(builtinTypeFunctions().unionFamily, std::move(tys), {}, globalScope, location); TypeId ty = createTypeFunctionInstance(builtinTypeFunctions().unionFunc, std::move(tys), {}, globalScope, location);
scope->bindings[symbol] = Binding{ty, location}; scope->bindings[symbol] = Binding{ty, location};
} }
@ -3355,10 +3355,10 @@ std::vector<std::optional<TypeId>> ConstraintGenerator::getExpectedCallTypesForF
return expectedTypes; return expectedTypes;
} }
TypeId ConstraintGenerator::createTypeFamilyInstance( TypeId ConstraintGenerator::createTypeFunctionInstance(
const TypeFamily& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments, const ScopePtr& scope, Location location) const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments, const ScopePtr& scope, Location location)
{ {
TypeId result = arena->addTypeFamily(family, typeArguments, packArguments); TypeId result = arena->addTypeFunction(family, typeArguments, packArguments);
addConstraint(scope, location, ReduceConstraint{result}); addConstraint(scope, location, ReduceConstraint{result});
return result; return result;
} }

View File

@ -18,7 +18,7 @@
#include "Luau/TimeTrace.h" #include "Luau/TimeTrace.h"
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypeFwd.h" #include "Luau/TypeFwd.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
#include "Luau/Unifier2.h" #include "Luau/Unifier2.h"
@ -298,7 +298,7 @@ struct InstantiationQueuer : TypeOnceVisitor
return false; return false;
} }
bool visit(TypeId ty, const TypeFamilyInstanceType&) override bool visit(TypeId ty, const TypeFunctionInstanceType&) override
{ {
solver->pushConstraint(scope, location, ReduceConstraint{ty}); solver->pushConstraint(scope, location, ReduceConstraint{ty});
return true; return true;
@ -510,10 +510,10 @@ void ConstraintSolver::finalizeTypeFamilies()
for (auto [t, constraint] : typeFamiliesToFinalize) for (auto [t, constraint] : typeFamiliesToFinalize)
{ {
TypeId ty = follow(t); TypeId ty = follow(t);
if (get<TypeFamilyInstanceType>(ty)) if (get<TypeFunctionInstanceType>(ty))
{ {
FamilyGraphReductionResult result = FunctionGraphReductionResult result =
reduceFamilies(t, constraint->location, TypeFamilyContext{NotNull{this}, constraint->scope, NotNull{constraint}}, true); reduceTypeFunctions(t, constraint->location, TypeFunctionContext{NotNull{this}, constraint->scope, NotNull{constraint}}, true);
for (TypeId r : result.reducedTypes) for (TypeId r : result.reducedTypes)
unblock(r, constraint->location); unblock(r, constraint->location);
@ -1394,7 +1394,7 @@ bool ConstraintSolver::tryDispatch(const HasPropConstraint& c, NotNull<const Con
LUAU_ASSERT(get<BlockedType>(resultType)); LUAU_ASSERT(get<BlockedType>(resultType));
LUAU_ASSERT(canMutate(resultType, constraint)); LUAU_ASSERT(canMutate(resultType, constraint));
if (isBlocked(subjectType) || get<PendingExpansionType>(subjectType) || get<TypeFamilyInstanceType>(subjectType)) if (isBlocked(subjectType) || get<PendingExpansionType>(subjectType) || get<TypeFunctionInstanceType>(subjectType))
return block(subjectType, constraint); return block(subjectType, constraint);
if (const TableType* subjectTable = getTableType(subjectType)) if (const TableType* subjectTable = getTableType(subjectType))
@ -1433,6 +1433,12 @@ bool ConstraintSolver::tryDispatchHasIndexer(
LUAU_ASSERT(get<BlockedType>(resultType)); LUAU_ASSERT(get<BlockedType>(resultType));
LUAU_ASSERT(canMutate(resultType, constraint)); LUAU_ASSERT(canMutate(resultType, constraint));
if (get<AnyType>(subjectType))
{
bind(constraint, resultType, builtinTypes->anyType);
return true;
}
if (auto ft = get<FreeType>(subjectType)) if (auto ft = get<FreeType>(subjectType))
{ {
if (auto tbl = get<TableType>(follow(ft->upperBound)); tbl && tbl->indexer) if (auto tbl = get<TableType>(follow(ft->upperBound)); tbl && tbl->indexer)
@ -1955,8 +1961,8 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force) bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force)
{ {
TypeId ty = follow(c.ty); TypeId ty = follow(c.ty);
FamilyGraphReductionResult result = FunctionGraphReductionResult result =
reduceFamilies(ty, constraint->location, TypeFamilyContext{NotNull{this}, constraint->scope, constraint}, force); reduceTypeFunctions(ty, constraint->location, TypeFunctionContext{NotNull{this}, constraint->scope, constraint}, force);
for (TypeId r : result.reducedTypes) for (TypeId r : result.reducedTypes)
unblock(r, constraint->location); unblock(r, constraint->location);
@ -1967,8 +1973,8 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
bool reductionFinished = result.blockedTypes.empty() && result.blockedPacks.empty(); bool reductionFinished = result.blockedTypes.empty() && result.blockedPacks.empty();
ty = follow(ty); ty = follow(ty);
// If we couldn't reduce this type family, stick it in the set! // If we couldn't reduce this type function, stick it in the set!
if (get<TypeFamilyInstanceType>(ty)) if (get<TypeFunctionInstanceType>(ty))
typeFamiliesToFinalize[ty] = constraint; typeFamiliesToFinalize[ty] = constraint;
if (force || reductionFinished) if (force || reductionFinished)
@ -1976,9 +1982,9 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
// if we're completely dispatching this constraint, we want to record any uninhabited type families to unblock. // if we're completely dispatching this constraint, we want to record any uninhabited type families to unblock.
for (auto error : result.errors) for (auto error : result.errors)
{ {
if (auto utf = get<UninhabitedTypeFamily>(error)) if (auto utf = get<UninhabitedTypeFunction>(error))
uninhabitedTypeFamilies.insert(utf->ty); uninhabitedTypeFamilies.insert(utf->ty);
else if (auto utpf = get<UninhabitedTypePackFamily>(error)) else if (auto utpf = get<UninhabitedTypePackFunction>(error))
uninhabitedTypeFamilies.insert(utpf->tp); uninhabitedTypeFamilies.insert(utpf->tp);
} }
} }
@ -1998,8 +2004,8 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
bool ConstraintSolver::tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force) bool ConstraintSolver::tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force)
{ {
TypePackId tp = follow(c.tp); TypePackId tp = follow(c.tp);
FamilyGraphReductionResult result = FunctionGraphReductionResult result =
reduceFamilies(tp, constraint->location, TypeFamilyContext{NotNull{this}, constraint->scope, constraint}, force); reduceTypeFunctions(tp, constraint->location, TypeFunctionContext{NotNull{this}, constraint->scope, constraint}, force);
for (TypeId r : result.reducedTypes) for (TypeId r : result.reducedTypes)
unblock(r, constraint->location); unblock(r, constraint->location);
@ -2014,9 +2020,9 @@ bool ConstraintSolver::tryDispatch(const ReducePackConstraint& c, NotNull<const
// if we're completely dispatching this constraint, we want to record any uninhabited type families to unblock. // if we're completely dispatching this constraint, we want to record any uninhabited type families to unblock.
for (auto error : result.errors) for (auto error : result.errors)
{ {
if (auto utf = get<UninhabitedTypeFamily>(error)) if (auto utf = get<UninhabitedTypeFunction>(error))
uninhabitedTypeFamilies.insert(utf->ty); uninhabitedTypeFamilies.insert(utf->ty);
else if (auto utpf = get<UninhabitedTypePackFamily>(error)) else if (auto utpf = get<UninhabitedTypePackFunction>(error))
uninhabitedTypeFamilies.insert(utpf->tp); uninhabitedTypeFamilies.insert(utpf->tp);
} }
} }
@ -2103,7 +2109,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
* it's possible that there are other constraints on the table that will * it's possible that there are other constraints on the table that will
* clarify what we should do. * clarify what we should do.
* *
* We should eventually introduce a type family to talk about iteration. * We should eventually introduce a type function to talk about iteration.
*/ */
if (iteratorTable->state == TableState::Free && !force) if (iteratorTable->state == TableState::Free && !force)
return block(iteratorTy, constraint); return block(iteratorTy, constraint);
@ -2713,13 +2719,13 @@ void ConstraintSolver::reproduceConstraints(NotNull<Scope> scope, const Location
{ {
for (auto [_, newTy] : subst.newTypes) for (auto [_, newTy] : subst.newTypes)
{ {
if (get<TypeFamilyInstanceType>(newTy)) if (get<TypeFunctionInstanceType>(newTy))
pushConstraint(scope, location, ReduceConstraint{newTy}); pushConstraint(scope, location, ReduceConstraint{newTy});
} }
for (auto [_, newPack] : subst.newPacks) for (auto [_, newPack] : subst.newPacks)
{ {
if (get<TypeFamilyInstanceTypePack>(newPack)) if (get<TypeFunctionInstanceTypePack>(newPack))
pushConstraint(scope, location, ReducePackConstraint{newPack}); pushConstraint(scope, location, ReducePackConstraint{newPack});
} }
} }
@ -2728,7 +2734,7 @@ bool ConstraintSolver::isBlocked(TypeId ty)
{ {
ty = follow(ty); ty = follow(ty);
if (auto tfit = get<TypeFamilyInstanceType>(ty)) if (auto tfit = get<TypeFunctionInstanceType>(ty))
return uninhabitedTypeFamilies.contains(ty) == false; return uninhabitedTypeFamilies.contains(ty) == false;
return nullptr != get<BlockedType>(ty) || nullptr != get<PendingExpansionType>(ty); return nullptr != get<BlockedType>(ty) || nullptr != get<PendingExpansionType>(ty);
@ -2738,7 +2744,7 @@ bool ConstraintSolver::isBlocked(TypePackId tp)
{ {
tp = follow(tp); tp = follow(tp);
if (auto tfitp = get<TypeFamilyInstanceTypePack>(tp)) if (auto tfitp = get<TypeFunctionInstanceTypePack>(tp))
return uninhabitedTypeFamilies.contains(tp) == false; return uninhabitedTypeFamilies.contains(tp) == false;
return nullptr != get<BlockedTypePack>(tp); return nullptr != get<BlockedTypePack>(tp);

View File

@ -8,7 +8,7 @@
#include "Luau/StringUtils.h" #include "Luau/StringUtils.h"
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>
@ -64,16 +64,16 @@ static std::string wrongNumberOfArgsString(
namespace Luau namespace Luau
{ {
// this list of binary operator type families is used for better stringification of type families errors // this list of binary operator type functions is used for better stringification of type functions errors
static const std::unordered_map<std::string, const char*> kBinaryOps{{"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"idiv", "//"}, static const std::unordered_map<std::string, const char*> kBinaryOps{{"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"idiv", "//"},
{"pow", "^"}, {"mod", "%"}, {"concat", ".."}, {"and", "and"}, {"or", "or"}, {"lt", "< or >="}, {"le", "<= or >"}, {"eq", "== or ~="}}; {"pow", "^"}, {"mod", "%"}, {"concat", ".."}, {"and", "and"}, {"or", "or"}, {"lt", "< or >="}, {"le", "<= or >"}, {"eq", "== or ~="}};
// this list of unary operator type families is used for better stringification of type families errors // this list of unary operator type functions is used for better stringification of type functions errors
static const std::unordered_map<std::string, const char*> kUnaryOps{{"unm", "-"}, {"len", "#"}, {"not", "not"}}; static const std::unordered_map<std::string, const char*> kUnaryOps{{"unm", "-"}, {"len", "#"}, {"not", "not"}};
// this list of type families will receive a special error indicating that the user should file a bug on the GitHub repository // this list of type functions will receive a special error indicating that the user should file a bug on the GitHub repository
// putting a type family in this list indicates that it is expected to _always_ reduce // putting a type function in this list indicates that it is expected to _always_ reduce
static const std::unordered_set<std::string> kUnreachableTypeFamilies{"refine", "singleton", "union", "intersect"}; static const std::unordered_set<std::string> kUnreachableTypeFunctions{"refine", "singleton", "union", "intersect"};
struct ErrorConverter struct ErrorConverter
{ {
@ -577,12 +577,12 @@ struct ErrorConverter
return "Attempting a dynamic property access on type '" + Luau::toString(e.ty) + "' is unsafe and may cause exceptions at runtime"; return "Attempting a dynamic property access on type '" + Luau::toString(e.ty) + "' is unsafe and may cause exceptions at runtime";
} }
std::string operator()(const UninhabitedTypeFamily& e) const std::string operator()(const UninhabitedTypeFunction& e) const
{ {
auto tfit = get<TypeFamilyInstanceType>(e.ty); auto tfit = get<TypeFunctionInstanceType>(e.ty);
LUAU_ASSERT(tfit); // Luau analysis has actually done something wrong if this type is not a type family. LUAU_ASSERT(tfit); // Luau analysis has actually done something wrong if this type is not a type function.
if (!tfit) if (!tfit)
return "Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type family."; return "Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type function.";
// unary operators // unary operators
if (auto unaryString = kUnaryOps.find(tfit->family->name); unaryString != kUnaryOps.end()) if (auto unaryString = kUnaryOps.find(tfit->family->name); unaryString != kUnaryOps.end())
@ -658,13 +658,13 @@ struct ErrorConverter
if (tfit->typeArguments.size() == 1 && tfit->packArguments.empty()) if (tfit->typeArguments.size() == 1 && tfit->packArguments.empty())
return "Type '" + toString(tfit->typeArguments[0]) + "' does not have keys, so '" + Luau::toString(e.ty) + "' is invalid"; return "Type '" + toString(tfit->typeArguments[0]) + "' does not have keys, so '" + Luau::toString(e.ty) + "' is invalid";
else else
return "Type family instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid"; return "Type function instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid";
} }
if ("index" == tfit->family->name || "rawget" == tfit->family->name) if ("index" == tfit->family->name || "rawget" == tfit->family->name)
{ {
if (tfit->typeArguments.size() != 2) if (tfit->typeArguments.size() != 2)
return "Type family instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid"; return "Type function instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid";
if (auto errType = get<ErrorType>(tfit->typeArguments[1])) // Second argument to (index | rawget)<_,_> is not a type if (auto errType = get<ErrorType>(tfit->typeArguments[1])) // Second argument to (index | rawget)<_,_> is not a type
return "Second argument to " + tfit->family->name + "<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type"; return "Second argument to " + tfit->family->name + "<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type";
@ -673,15 +673,15 @@ struct ErrorConverter
"'"; "'";
} }
if (kUnreachableTypeFamilies.count(tfit->family->name)) if (kUnreachableTypeFunctions.count(tfit->family->name))
{ {
return "Type family instance " + Luau::toString(e.ty) + " is uninhabited\n" + return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" +
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues"; "This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
} }
// Everything should be specialized above to report a more descriptive error that hopefully does not mention "type families" explicitly. // Everything should be specialized above to report a more descriptive error that hopefully does not mention "type functions" explicitly.
// If we produce this message, it's an indication that we've missed a specialization and it should be fixed! // If we produce this message, it's an indication that we've missed a specialization and it should be fixed!
return "Type family instance " + Luau::toString(e.ty) + " is uninhabited"; return "Type function instance " + Luau::toString(e.ty) + " is uninhabited";
} }
std::string operator()(const ExplicitFunctionAnnotationRecommended& r) const std::string operator()(const ExplicitFunctionAnnotationRecommended& r) const
@ -704,14 +704,14 @@ struct ErrorConverter
return "Consider placing the following annotations on the arguments: " + argAnnotations + " or instead annotating the return as " + toReturn; return "Consider placing the following annotations on the arguments: " + argAnnotations + " or instead annotating the return as " + toReturn;
} }
std::string operator()(const UninhabitedTypePackFamily& e) const std::string operator()(const UninhabitedTypePackFunction& e) const
{ {
return "Type pack family instance " + Luau::toString(e.tp) + " is uninhabited"; return "Type pack family instance " + Luau::toString(e.tp) + " is uninhabited";
} }
std::string operator()(const WhereClauseNeeded& e) const std::string operator()(const WhereClauseNeeded& e) const
{ {
return "Type family instance " + Luau::toString(e.ty) + return "Type function instance " + Luau::toString(e.ty) +
" depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this " " depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this "
"time"; "time";
} }
@ -1092,7 +1092,7 @@ bool DynamicPropertyLookupOnClassesUnsafe::operator==(const DynamicPropertyLooku
return ty == rhs.ty; return ty == rhs.ty;
} }
bool UninhabitedTypeFamily::operator==(const UninhabitedTypeFamily& rhs) const bool UninhabitedTypeFunction::operator==(const UninhabitedTypeFunction& rhs) const
{ {
return ty == rhs.ty; return ty == rhs.ty;
} }
@ -1103,7 +1103,7 @@ bool ExplicitFunctionAnnotationRecommended::operator==(const ExplicitFunctionAnn
return recommendedReturn == rhs.recommendedReturn && recommendedArgs == rhs.recommendedArgs; return recommendedReturn == rhs.recommendedReturn && recommendedArgs == rhs.recommendedArgs;
} }
bool UninhabitedTypePackFamily::operator==(const UninhabitedTypePackFamily& rhs) const bool UninhabitedTypePackFunction::operator==(const UninhabitedTypePackFunction& rhs) const
{ {
return tp == rhs.tp; return tp == rhs.tp;
} }
@ -1316,7 +1316,7 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
} }
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>) else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>)
e.ty = clone(e.ty); e.ty = clone(e.ty);
else if constexpr (std::is_same_v<T, UninhabitedTypeFamily>) else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
e.ty = clone(e.ty); e.ty = clone(e.ty);
else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>) else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>)
{ {
@ -1324,7 +1324,7 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState)
for (auto& [_, t] : e.recommendedArgs) for (auto& [_, t] : e.recommendedArgs)
t = clone(t); t = clone(t);
} }
else if constexpr (std::is_same_v<T, UninhabitedTypePackFamily>) else if constexpr (std::is_same_v<T, UninhabitedTypePackFunction>)
e.tp = clone(e.tp); e.tp = clone(e.tp);
else if constexpr (std::is_same_v<T, WhereClauseNeeded>) else if constexpr (std::is_same_v<T, WhereClauseNeeded>)
e.ty = clone(e.ty); e.ty = clone(e.ty);

View File

@ -131,9 +131,10 @@ static ParseResult parseSourceForModule(std::string_view source, Luau::SourceMod
if (FFlag::LuauStoreCommentsForDefinitionFiles && options.captureComments) if (FFlag::LuauStoreCommentsForDefinitionFiles && options.captureComments)
{ {
sourceModule.hotcomments = std::move(parseResult.hotcomments); sourceModule.hotcomments = parseResult.hotcomments;
sourceModule.commentLocations = std::move(parseResult.commentLocations); sourceModule.commentLocations = parseResult.commentLocations;
} }
return parseResult; return parseResult;
} }
@ -1236,7 +1237,7 @@ struct InternalTypeFinder : TypeOnceVisitor
return false; return false;
} }
bool visit(TypePackId, const TypeFamilyInstanceTypePack&) override bool visit(TypePackId, const TypeFunctionInstanceTypePack&) override
{ {
LUAU_ASSERT(false); LUAU_ASSERT(false);
return false; return false;

View File

@ -804,7 +804,7 @@ struct TypeCacher : TypeOnceVisitor
return false; return false;
} }
bool visit(TypeId ty, const TypeFamilyInstanceType& tfit) override bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
{ {
if (isCached(ty) || isUncacheable(ty)) if (isCached(ty) || isUncacheable(ty))
return false; return false;
@ -860,7 +860,7 @@ struct TypeCacher : TypeOnceVisitor
return false; return false;
} }
bool visit(TypePackId tp, const TypeFamilyInstanceTypePack&) override bool visit(TypePackId tp, const TypeFunctionInstanceTypePack&) override
{ {
markUncacheable(tp); markUncacheable(tp);
return false; return false;

View File

@ -193,8 +193,8 @@ static void errorToString(std::ostream& stream, const T& err)
stream << "TypePackMismatch { wanted = '" + toString(err.wantedTp) + "', given = '" + toString(err.givenTp) + "' }"; stream << "TypePackMismatch { wanted = '" + toString(err.wantedTp) + "', given = '" + toString(err.givenTp) + "' }";
else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>) else if constexpr (std::is_same_v<T, DynamicPropertyLookupOnClassesUnsafe>)
stream << "DynamicPropertyLookupOnClassesUnsafe { " << toString(err.ty) << " }"; stream << "DynamicPropertyLookupOnClassesUnsafe { " << toString(err.ty) << " }";
else if constexpr (std::is_same_v<T, UninhabitedTypeFamily>) else if constexpr (std::is_same_v<T, UninhabitedTypeFunction>)
stream << "UninhabitedTypeFamily { " << toString(err.ty) << " }"; stream << "UninhabitedTypeFunction { " << toString(err.ty) << " }";
else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>) else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>)
{ {
std::string recArgs = "["; std::string recArgs = "[";
@ -204,8 +204,8 @@ static void errorToString(std::ostream& stream, const T& err)
stream << "ExplicitFunctionAnnotationRecommended { recommmendedReturn = '" + toString(err.recommendedReturn) + stream << "ExplicitFunctionAnnotationRecommended { recommmendedReturn = '" + toString(err.recommendedReturn) +
"', recommmendedArgs = " + recArgs + "}"; "', recommmendedArgs = " + recArgs + "}";
} }
else if constexpr (std::is_same_v<T, UninhabitedTypePackFamily>) else if constexpr (std::is_same_v<T, UninhabitedTypePackFunction>)
stream << "UninhabitedTypePackFamily { " << toString(err.tp) << " }"; stream << "UninhabitedTypePackFunction { " << toString(err.tp) << " }";
else if constexpr (std::is_same_v<T, WhereClauseNeeded>) else if constexpr (std::is_same_v<T, WhereClauseNeeded>)
stream << "WhereClauseNeeded { " << toString(err.ty) << " }"; stream << "WhereClauseNeeded { " << toString(err.ty) << " }";
else if constexpr (std::is_same_v<T, PackWhereClauseNeeded>) else if constexpr (std::is_same_v<T, PackWhereClauseNeeded>)

View File

@ -15,6 +15,7 @@
#include <algorithm> #include <algorithm>
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauSkipEmptyInstantiations, false);
namespace Luau namespace Luau
{ {
@ -115,9 +116,22 @@ struct ClonePublicInterface : Substitution
TypeId result = clone(ty); TypeId result = clone(ty);
if (FunctionType* ftv = getMutable<FunctionType>(result)) if (FunctionType* ftv = getMutable<FunctionType>(result))
{
if (FFlag::LuauSkipEmptyInstantiations && ftv->generics.empty() && ftv->genericPacks.empty())
{
GenericTypeFinder marker;
marker.traverse(result);
if (!marker.found)
ftv->hasNoFreeOrGenericTypes = true;
}
ftv->level = TypeLevel{0, 0}; ftv->level = TypeLevel{0, 0};
}
else if (TableType* ttv = getMutable<TableType>(result)) else if (TableType* ttv = getMutable<TableType>(result))
{
ttv->level = TypeLevel{0, 0}; ttv->level = TypeLevel{0, 0};
}
return result; return result;
} }

View File

@ -10,7 +10,7 @@
#include "Luau/Normalize.h" #include "Luau/Normalize.h"
#include "Luau/Error.h" #include "Luau/Error.h"
#include "Luau/TypeArena.h" #include "Luau/TypeArena.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/Def.h" #include "Luau/Def.h"
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/TypeFwd.h" #include "Luau/TypeFwd.h"
@ -153,7 +153,7 @@ struct NonStrictTypeChecker
Normalizer normalizer; Normalizer normalizer;
Subtyping subtyping; Subtyping subtyping;
NotNull<const DataFlowGraph> dfg; NotNull<const DataFlowGraph> dfg;
DenseHashSet<TypeId> noTypeFamilyErrors{nullptr}; DenseHashSet<TypeId> noTypeFunctionErrors{nullptr};
std::vector<NotNull<Scope>> stack; std::vector<NotNull<Scope>> stack;
DenseHashMap<TypeId, TypeId> cachedNegations{nullptr}; DenseHashMap<TypeId, TypeId> cachedNegations{nullptr};
@ -208,14 +208,14 @@ struct NonStrictTypeChecker
TypeId checkForFamilyInhabitance(TypeId instance, Location location) TypeId checkForFamilyInhabitance(TypeId instance, Location location)
{ {
if (noTypeFamilyErrors.find(instance)) if (noTypeFunctionErrors.find(instance))
return instance; return instance;
ErrorVec errors = ErrorVec errors =
reduceFamilies(instance, location, TypeFamilyContext{arena, builtinTypes, stack.back(), NotNull{&normalizer}, ice, limits}, true).errors; reduceTypeFunctions(instance, location, TypeFunctionContext{arena, builtinTypes, stack.back(), NotNull{&normalizer}, ice, limits}, true).errors;
if (errors.empty()) if (errors.empty())
noTypeFamilyErrors.insert(instance); noTypeFunctionErrors.insert(instance);
// TODO?? // TODO??
// if (!isErrorSuppressing(location, instance)) // if (!isErrorSuppressing(location, instance))
// reportErrors(std::move(errors)); // reportErrors(std::move(errors));

View File

@ -816,7 +816,7 @@ static bool areNormalizedClasses(const NormalizedClassType& tys)
static bool isPlainTyvar(TypeId ty) static bool isPlainTyvar(TypeId ty)
{ {
return (get<FreeType>(ty) || get<GenericType>(ty) || get<BlockedType>(ty) || get<PendingExpansionType>(ty) || get<TypeFamilyInstanceType>(ty)); return (get<FreeType>(ty) || get<GenericType>(ty) || get<BlockedType>(ty) || get<PendingExpansionType>(ty) || get<TypeFunctionInstanceType>(ty));
} }
static bool isNormalizedTyvar(const NormalizedTyvars& tyvars) static bool isNormalizedTyvar(const NormalizedTyvars& tyvars)
@ -883,7 +883,7 @@ static bool isCacheable(TypePackId tp, Set<TypeId>& seen)
if (auto tail = it.tail()) if (auto tail = it.tail())
{ {
if (get<FreeTypePack>(*tail) || get<BlockedTypePack>(*tail) || get<TypeFamilyInstanceTypePack>(*tail)) if (get<FreeTypePack>(*tail) || get<BlockedTypePack>(*tail) || get<TypeFunctionInstanceTypePack>(*tail))
return false; return false;
} }
@ -901,7 +901,7 @@ static bool isCacheable(TypeId ty, Set<TypeId>& seen)
if (get<FreeType>(ty) || get<BlockedType>(ty) || get<PendingExpansionType>(ty)) if (get<FreeType>(ty) || get<BlockedType>(ty) || get<PendingExpansionType>(ty))
return false; return false;
if (auto tfi = get<TypeFamilyInstanceType>(ty)) if (auto tfi = get<TypeFunctionInstanceType>(ty))
{ {
for (TypeId t : tfi->typeArguments) for (TypeId t : tfi->typeArguments)
{ {
@ -1795,7 +1795,7 @@ NormalizationResult Normalizer::unionNormalWithTy(NormalizedType& here, TypeId t
else if (get<UnknownType>(here.tops)) else if (get<UnknownType>(here.tops))
return NormalizationResult::True; return NormalizationResult::True;
else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) || else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) ||
get<TypeFamilyInstanceType>(there)) get<TypeFunctionInstanceType>(there))
{ {
if (tyvarIndex(there) <= ignoreSmallerTyvars) if (tyvarIndex(there) <= ignoreSmallerTyvars)
return NormalizationResult::True; return NormalizationResult::True;
@ -1872,7 +1872,7 @@ NormalizationResult Normalizer::unionNormalWithTy(NormalizedType& here, TypeId t
if (res != NormalizationResult::True) if (res != NormalizationResult::True)
return res; return res;
} }
else if (get<PendingExpansionType>(there) || get<TypeFamilyInstanceType>(there)) else if (get<PendingExpansionType>(there) || get<TypeFunctionInstanceType>(there))
{ {
// nothing // nothing
} }
@ -3080,7 +3080,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(NormalizedType& here, Type
return NormalizationResult::True; return NormalizationResult::True;
} }
else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) || else if (get<GenericType>(there) || get<FreeType>(there) || get<BlockedType>(there) || get<PendingExpansionType>(there) ||
get<TypeFamilyInstanceType>(there)) get<TypeFunctionInstanceType>(there))
{ {
NormalizedType thereNorm{builtinTypes}; NormalizedType thereNorm{builtinTypes};
NormalizedType topNorm{builtinTypes}; NormalizedType topNorm{builtinTypes};

View File

@ -5,7 +5,7 @@
#include "Luau/Subtyping.h" #include "Luau/Subtyping.h"
#include "Luau/TxnLog.h" #include "Luau/TxnLog.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
#include "Luau/Unifier2.h" #include "Luau/Unifier2.h"
@ -175,14 +175,15 @@ bool OverloadResolver::isLiteral(AstExpr* expr)
std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_( std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_(
TypeId fnTy, const FunctionType* fn, const TypePack* args, AstExpr* fnExpr, const std::vector<AstExpr*>* argExprs) TypeId fnTy, const FunctionType* fn, const TypePack* args, AstExpr* fnExpr, const std::vector<AstExpr*>* argExprs)
{ {
FamilyGraphReductionResult result = FunctionGraphReductionResult result =
reduceFamilies(fnTy, callLoc, TypeFamilyContext{arena, builtinTypes, scope, normalizer, ice, limits}, /*force=*/true); reduceTypeFunctions(fnTy, callLoc, TypeFunctionContext{arena, builtinTypes, scope, normalizer, ice, limits}, /*force=*/true);
if (!result.errors.empty()) if (!result.errors.empty())
return {OverloadIsNonviable, result.errors}; return {OverloadIsNonviable, result.errors};
ErrorVec argumentErrors; ErrorVec argumentErrors;
TypePackId typ = arena->addTypePack(*args);
TypeId prospectiveFunction = arena->addType(FunctionType{arena->addTypePack(*args), builtinTypes->anyTypePack}); TypeId prospectiveFunction = arena->addType(FunctionType{typ, builtinTypes->anyTypePack});
SubtypingResult sr = subtyping.isSubtype(fnTy, prospectiveFunction); SubtypingResult sr = subtyping.isSubtype(fnTy, prospectiveFunction);
if (sr.isSubtype) if (sr.isSubtype)
@ -253,7 +254,14 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
if (const Luau::TypePath::Index* pathIndexComponent = get_if<Luau::TypePath::Index>(&reason.superPath.components.at(1))) if (const Luau::TypePath::Index* pathIndexComponent = get_if<Luau::TypePath::Index>(&reason.superPath.components.at(1)))
{ {
size_t nthArgument = pathIndexComponent->index; size_t nthArgument = pathIndexComponent->index;
argLocation = argExprs->at(nthArgument)->location; // if the nth type argument to the function is less than the number of ast expressions we passed to the function
// we should be able to pull out the location of the argument
// If the nth type argument to the function is out of range of the ast expressions we passed to the function
// e.g. table.pack(functionThatReturnsMultipleArguments(arg1, arg2, ....)), default to the location of the last passed expression
// If we passed no expression arguments to the call, default to the location of the function expression.
argLocation = nthArgument < argExprs->size() ? argExprs->at(nthArgument)->location
: argExprs->size() != 0 ? argExprs->back()->location
: fnExpr->location;
std::optional<TypeId> failedSubTy = traverseForType(fnTy, reason.subPath, builtinTypes); std::optional<TypeId> failedSubTy = traverseForType(fnTy, reason.subPath, builtinTypes);
std::optional<TypeId> failedSuperTy = traverseForType(prospectiveFunction, reason.superPath, builtinTypes); std::optional<TypeId> failedSuperTy = traverseForType(prospectiveFunction, reason.superPath, builtinTypes);

View File

@ -125,9 +125,9 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
} }
else if constexpr (std::is_same_v<T, NegationType>) else if constexpr (std::is_same_v<T, NegationType>)
return dest.addType(NegationType{a.ty}); return dest.addType(NegationType{a.ty});
else if constexpr (std::is_same_v<T, TypeFamilyInstanceType>) else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
{ {
TypeFamilyInstanceType clone{a.family, a.typeArguments, a.packArguments}; TypeFunctionInstanceType clone{a.family, a.typeArguments, a.packArguments};
return dest.addType(std::move(clone)); return dest.addType(std::move(clone));
} }
else else
@ -226,7 +226,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
for (TypePackId a : petv->packArguments) for (TypePackId a : petv->packArguments)
visitChild(a); visitChild(a);
} }
else if (const TypeFamilyInstanceType* tfit = get<TypeFamilyInstanceType>(ty)) else if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(ty))
{ {
for (TypeId a : tfit->typeArguments) for (TypeId a : tfit->typeArguments)
visitChild(a); visitChild(a);
@ -669,9 +669,9 @@ TypePackId Substitution::clone(TypePackId tp)
clone.hidden = vtp->hidden; clone.hidden = vtp->hidden;
return addTypePack(std::move(clone)); return addTypePack(std::move(clone));
} }
else if (const TypeFamilyInstanceTypePack* tfitp = get<TypeFamilyInstanceTypePack>(tp)) else if (const TypeFunctionInstanceTypePack* tfitp = get<TypeFunctionInstanceTypePack>(tp))
{ {
TypeFamilyInstanceTypePack clone{ TypeFunctionInstanceTypePack clone{
tfitp->family, std::vector<TypeId>(tfitp->typeArguments.size()), std::vector<TypePackId>(tfitp->packArguments.size())}; tfitp->family, std::vector<TypeId>(tfitp->typeArguments.size()), std::vector<TypePackId>(tfitp->packArguments.size())};
clone.typeArguments.assign(tfitp->typeArguments.begin(), tfitp->typeArguments.end()); clone.typeArguments.assign(tfitp->typeArguments.begin(), tfitp->typeArguments.end());
clone.packArguments.assign(tfitp->packArguments.begin(), tfitp->packArguments.end()); clone.packArguments.assign(tfitp->packArguments.begin(), tfitp->packArguments.end());
@ -798,7 +798,7 @@ void Substitution::replaceChildren(TypeId ty)
for (TypePackId& a : petv->packArguments) for (TypePackId& a : petv->packArguments)
a = replace(a); a = replace(a);
} }
else if (TypeFamilyInstanceType* tfit = getMutable<TypeFamilyInstanceType>(ty)) else if (TypeFunctionInstanceType* tfit = getMutable<TypeFunctionInstanceType>(ty))
{ {
for (TypeId& a : tfit->typeArguments) for (TypeId& a : tfit->typeArguments)
a = replace(a); a = replace(a);
@ -850,7 +850,7 @@ void Substitution::replaceChildren(TypePackId tp)
{ {
vtp->ty = replace(vtp->ty); vtp->ty = replace(vtp->ty);
} }
else if (TypeFamilyInstanceTypePack* tfitp = getMutable<TypeFamilyInstanceTypePack>(tp)) else if (TypeFunctionInstanceTypePack* tfitp = getMutable<TypeFunctionInstanceTypePack>(tp))
{ {
for (TypeId& t : tfitp->typeArguments) for (TypeId& t : tfitp->typeArguments)
t = replace(t); t = replace(t);

View File

@ -13,7 +13,7 @@
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeArena.h" #include "Luau/TypeArena.h"
#include "Luau/TypeCheckLimits.h" #include "Luau/TypeCheckLimits.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/TypePath.h" #include "Luau/TypePath.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
@ -121,9 +121,9 @@ SubtypingResult& SubtypingResult::andAlso(const SubtypingResult& other)
{ {
// If the other result is not a subtype, we want to join all of its // If the other result is not a subtype, we want to join all of its
// reasonings to this one. If this result already has reasonings of its own, // reasonings to this one. If this result already has reasonings of its own,
// those need to be attributed here. // those need to be attributed here whenever this _also_ failed.
if (!other.isSubtype) if (!other.isSubtype)
reasoning = mergeReasonings(reasoning, other.reasoning); reasoning = isSubtype ? std::move(other.reasoning) : mergeReasonings(reasoning, other.reasoning);
isSubtype &= other.isSubtype; isSubtype &= other.isSubtype;
normalizationTooComplex |= other.normalizationTooComplex; normalizationTooComplex |= other.normalizationTooComplex;
@ -579,19 +579,19 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
} }
} }
} }
else if (auto subTypeFamilyInstance = get<TypeFamilyInstanceType>(subTy)) else if (auto subTypeFunctionInstance = get<TypeFunctionInstanceType>(subTy))
{ {
if (auto substSubTy = env.applyMappedGenerics(builtinTypes, arena, subTy)) if (auto substSubTy = env.applyMappedGenerics(builtinTypes, arena, subTy))
subTypeFamilyInstance = get<TypeFamilyInstanceType>(*substSubTy); subTypeFunctionInstance = get<TypeFunctionInstanceType>(*substSubTy);
result = isCovariantWith(env, subTypeFamilyInstance, superTy); result = isCovariantWith(env, subTypeFunctionInstance, superTy);
} }
else if (auto superTypeFamilyInstance = get<TypeFamilyInstanceType>(superTy)) else if (auto superTypeFunctionInstance = get<TypeFunctionInstanceType>(superTy))
{ {
if (auto substSuperTy = env.applyMappedGenerics(builtinTypes, arena, superTy)) if (auto substSuperTy = env.applyMappedGenerics(builtinTypes, arena, superTy))
superTypeFamilyInstance = get<TypeFamilyInstanceType>(*substSuperTy); superTypeFunctionInstance = get<TypeFunctionInstanceType>(*substSuperTy);
result = isCovariantWith(env, subTy, superTypeFamilyInstance); result = isCovariantWith(env, subTy, superTypeFunctionInstance);
} }
else if (auto subGeneric = get<GenericType>(subTy); subGeneric && variance == Variance::Covariant) else if (auto subGeneric = get<GenericType>(subTy); subGeneric && variance == Variance::Covariant)
{ {
@ -1584,19 +1584,19 @@ bool Subtyping::bindGeneric(SubtypingEnvironment& env, TypeId subTy, TypeId supe
return true; return true;
} }
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeFamilyInstanceType* subFamilyInstance, const TypeId superTy) SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFamilyInstance, const TypeId superTy)
{ {
// Reduce the typefamily instance // Reduce the type function instance
auto [ty, errors] = handleTypeFamilyReductionResult(subFamilyInstance); auto [ty, errors] = handleTypeFunctionReductionResult(subFamilyInstance);
// If we return optional, that means the type family was irreducible - we can reduce that to never // If we return optional, that means the type function was irreducible - we can reduce that to never
return isCovariantWith(env, ty, superTy).withErrors(errors).withSubComponent(TypePath::Reduction{ty}); return isCovariantWith(env, ty, superTy).withErrors(errors).withSubComponent(TypePath::Reduction{ty});
} }
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFamilyInstanceType* superFamilyInstance) SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFamilyInstance)
{ {
// Reduce the typefamily instance // Reduce the type function instance
auto [ty, errors] = handleTypeFamilyReductionResult(superFamilyInstance); auto [ty, errors] = handleTypeFunctionReductionResult(superFamilyInstance);
return isCovariantWith(env, subTy, ty).withErrors(errors).withSuperComponent(TypePath::Reduction{ty}); return isCovariantWith(env, subTy, ty).withErrors(errors).withSuperComponent(TypePath::Reduction{ty});
} }
@ -1632,15 +1632,15 @@ TypeId Subtyping::makeAggregateType(const Container& container, TypeId orElse)
return arena->addType(T{std::vector<TypeId>(begin(container), end(container))}); return arena->addType(T{std::vector<TypeId>(begin(container), end(container))});
} }
std::pair<TypeId, ErrorVec> Subtyping::handleTypeFamilyReductionResult(const TypeFamilyInstanceType* familyInstance) std::pair<TypeId, ErrorVec> Subtyping::handleTypeFunctionReductionResult(const TypeFunctionInstanceType* familyInstance)
{ {
TypeFamilyContext context{arena, builtinTypes, scope, normalizer, iceReporter, NotNull{&limits}}; TypeFunctionContext context{arena, builtinTypes, scope, normalizer, iceReporter, NotNull{&limits}};
TypeId family = arena->addType(*familyInstance); TypeId family = arena->addType(*familyInstance);
FamilyGraphReductionResult result = reduceFamilies(family, {}, context, true); FunctionGraphReductionResult result = reduceTypeFunctions(family, {}, context, true);
ErrorVec errors; ErrorVec errors;
if (result.blockedTypes.size() != 0 || result.blockedPacks.size() != 0) if (result.blockedTypes.size() != 0 || result.blockedPacks.size() != 0)
{ {
errors.push_back(TypeError{{}, UninhabitedTypeFamily{family}}); errors.push_back(TypeError{{}, UninhabitedTypeFunction{family}});
return {builtinTypes->neverType, errors}; return {builtinTypes->neverType, errors};
} }
if (result.reducedTypes.contains(family)) if (result.reducedTypes.contains(family))

View File

@ -4,7 +4,7 @@
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/StringUtils.h" #include "Luau/StringUtils.h"
#include <unordered_map> #include <unordered_map>
@ -343,9 +343,9 @@ void StateDot::visitChildren(TypeId ty, int index)
visitChild(t.ty, index, "[negated]"); visitChild(t.ty, index, "[negated]");
} }
else if constexpr (std::is_same_v<T, TypeFamilyInstanceType>) else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
{ {
formatAppend(result, "TypeFamilyInstanceType %s %d", t.family->name.c_str(), index); formatAppend(result, "TypeFunctionInstanceType %s %d", t.family->name.c_str(), index);
finishNodeLabel(ty); finishNodeLabel(ty);
finishNode(); finishNode();

View File

@ -11,7 +11,7 @@
#include "Luau/TypeInfer.h" #include "Luau/TypeInfer.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/VisitType.h" #include "Luau/VisitType.h"
#include "Luau/TypeOrPack.h" #include "Luau/TypeOrPack.h"
@ -1031,7 +1031,7 @@ struct TypeStringifier
state.emit(")"); state.emit(")");
} }
void operator()(TypeId, const TypeFamilyInstanceType& tfitv) void operator()(TypeId, const TypeFunctionInstanceType& tfitv)
{ {
state.emit(tfitv.family->name); state.emit(tfitv.family->name);
state.emit("<"); state.emit("<");
@ -1232,7 +1232,7 @@ struct TypePackStringifier
state.emit("*"); state.emit("*");
} }
void operator()(TypePackId, const TypeFamilyInstanceTypePack& tfitp) void operator()(TypePackId, const TypeFunctionInstanceTypePack& tfitp)
{ {
state.emit(tfitp.family->name); state.emit(tfitp.family->name);
state.emit("<"); state.emit("<");

View File

@ -9,7 +9,7 @@
#include "Luau/RecursionCounter.h" #include "Luau/RecursionCounter.h"
#include "Luau/StringUtils.h" #include "Luau/StringUtils.h"
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypeInfer.h" #include "Luau/TypeInfer.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/VecDeque.h" #include "Luau/VecDeque.h"
@ -423,7 +423,7 @@ bool maybeSingleton(TypeId ty)
for (TypeId part : itv) for (TypeId part : itv)
if (maybeSingleton(part)) // will i regret this? if (maybeSingleton(part)) // will i regret this?
return true; return true;
if (const TypeFamilyInstanceType* tfit = get<TypeFamilyInstanceType>(ty)) if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(ty))
if (tfit->family->name == "keyof" || tfit->family->name == "rawkeyof") if (tfit->family->name == "keyof" || tfit->family->name == "rawkeyof")
return true; return true;
return false; return false;
@ -1081,7 +1081,7 @@ void persist(TypeId ty)
else if (get<GenericType>(t) || get<AnyType>(t) || get<FreeType>(t) || get<SingletonType>(t) || get<PrimitiveType>(t) || get<NegationType>(t)) else if (get<GenericType>(t) || get<AnyType>(t) || get<FreeType>(t) || get<SingletonType>(t) || get<PrimitiveType>(t) || get<NegationType>(t))
{ {
} }
else if (auto tfit = get<TypeFamilyInstanceType>(t)) else if (auto tfit = get<TypeFunctionInstanceType>(t))
{ {
for (auto ty : tfit->typeArguments) for (auto ty : tfit->typeArguments)
queue.push_back(ty); queue.push_back(ty);
@ -1117,7 +1117,7 @@ void persist(TypePackId tp)
else if (get<GenericTypePack>(tp)) else if (get<GenericTypePack>(tp))
{ {
} }
else if (auto tfitp = get<TypeFamilyInstanceTypePack>(tp)) else if (auto tfitp = get<TypeFunctionInstanceTypePack>(tp))
{ {
for (auto ty : tfitp->typeArguments) for (auto ty : tfitp->typeArguments)
persist(ty); persist(ty);

View File

@ -94,24 +94,24 @@ TypePackId TypeArena::addTypePack(TypePackVar tp)
return allocated; return allocated;
} }
TypeId TypeArena::addTypeFamily(const TypeFamily& family, std::initializer_list<TypeId> types) TypeId TypeArena::addTypeFunction(const TypeFunction& family, std::initializer_list<TypeId> types)
{ {
return addType(TypeFamilyInstanceType{family, std::move(types)}); return addType(TypeFunctionInstanceType{family, std::move(types)});
} }
TypeId TypeArena::addTypeFamily(const TypeFamily& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments) TypeId TypeArena::addTypeFunction(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
{ {
return addType(TypeFamilyInstanceType{family, std::move(typeArguments), std::move(packArguments)}); return addType(TypeFunctionInstanceType{family, std::move(typeArguments), std::move(packArguments)});
} }
TypePackId TypeArena::addTypePackFamily(const TypePackFamily& family, std::initializer_list<TypeId> types) TypePackId TypeArena::addTypePackFunction(const TypePackFunction& family, std::initializer_list<TypeId> types)
{ {
return addTypePack(TypeFamilyInstanceTypePack{NotNull{&family}, std::move(types)}); return addTypePack(TypeFunctionInstanceTypePack{NotNull{&family}, std::move(types)});
} }
TypePackId TypeArena::addTypePackFamily(const TypePackFamily& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments) TypePackId TypeArena::addTypePackFunction(const TypePackFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
{ {
return addTypePack(TypeFamilyInstanceTypePack{NotNull{&family}, std::move(typeArguments), std::move(packArguments)}); return addTypePack(TypeFunctionInstanceTypePack{NotNull{&family}, std::move(typeArguments), std::move(packArguments)});
} }
void freeze(TypeArena& arena) void freeze(TypeArena& arena)

View File

@ -9,7 +9,7 @@
#include "Luau/TypeInfer.h" #include "Luau/TypeInfer.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include <string> #include <string>
@ -380,7 +380,7 @@ public:
// FIXME: do the same thing we do with ErrorType // FIXME: do the same thing we do with ErrorType
throw InternalCompilerError("Cannot convert NegationType into AstNode"); throw InternalCompilerError("Cannot convert NegationType into AstNode");
} }
AstType* operator()(const TypeFamilyInstanceType& tfit) AstType* operator()(const TypeFunctionInstanceType& tfit)
{ {
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName{tfit.family->name.c_str()}, std::nullopt, Location()); return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName{tfit.family->name.c_str()}, std::nullopt, Location());
} }
@ -454,7 +454,7 @@ public:
return allocator->alloc<AstTypePackGeneric>(Location(), AstName("Unifiable<Error>")); return allocator->alloc<AstTypePackGeneric>(Location(), AstName("Unifiable<Error>"));
} }
AstTypePack* operator()(const TypeFamilyInstanceTypePack& tfitp) const AstTypePack* operator()(const TypeFunctionInstanceTypePack& tfitp) const
{ {
return allocator->alloc<AstTypePackGeneric>(Location(), AstName(tfitp.family->name.c_str())); return allocator->alloc<AstTypePackGeneric>(Location(), AstName(tfitp.family->name.c_str()));
} }

View File

@ -16,8 +16,8 @@
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/TxnLog.h" #include "Luau/TxnLog.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypeFamilyReductionGuesser.h" #include "Luau/TypeFunctionReductionGuesser.h"
#include "Luau/TypeFwd.h" #include "Luau/TypeFwd.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/TypePath.h" #include "Luau/TypePath.h"
@ -121,13 +121,13 @@ struct FamilyFinder : TypeOnceVisitor
DenseHashSet<TypeId> mentionedFamilies{nullptr}; DenseHashSet<TypeId> mentionedFamilies{nullptr};
DenseHashSet<TypePackId> mentionedFamilyPacks{nullptr}; DenseHashSet<TypePackId> mentionedFamilyPacks{nullptr};
bool visit(TypeId ty, const TypeFamilyInstanceType&) override bool visit(TypeId ty, const TypeFunctionInstanceType&) override
{ {
mentionedFamilies.insert(ty); mentionedFamilies.insert(ty);
return true; return true;
} }
bool visit(TypePackId tp, const TypeFamilyInstanceTypePack&) override bool visit(TypePackId tp, const TypeFunctionInstanceTypePack&) override
{ {
mentionedFamilyPacks.insert(tp); mentionedFamilyPacks.insert(tp);
return true; return true;
@ -151,7 +151,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
mentionedFamilyPacks = std::move(f.mentionedFamilyPacks); mentionedFamilyPacks = std::move(f.mentionedFamilyPacks);
} }
bool visit(TypeId ty, const TypeFamilyInstanceType& tfit) override bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
{ {
bool hasGeneric = false; bool hasGeneric = false;
@ -177,7 +177,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
{ {
for (TypeId mentioned : mentionedFamilies) for (TypeId mentioned : mentionedFamilies)
{ {
const TypeFamilyInstanceType* mentionedTfit = get<TypeFamilyInstanceType>(mentioned); const TypeFunctionInstanceType* mentionedTfit = get<TypeFunctionInstanceType>(mentioned);
LUAU_ASSERT(mentionedTfit); LUAU_ASSERT(mentionedTfit);
if (areEquivalent(tfit, *mentionedTfit)) if (areEquivalent(tfit, *mentionedTfit))
{ {
@ -191,7 +191,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
return true; return true;
} }
bool visit(TypePackId tp, const TypeFamilyInstanceTypePack& tfitp) override bool visit(TypePackId tp, const TypeFunctionInstanceTypePack& tfitp) override
{ {
bool hasGeneric = false; bool hasGeneric = false;
@ -217,7 +217,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
{ {
for (TypePackId mentioned : mentionedFamilyPacks) for (TypePackId mentioned : mentionedFamilyPacks)
{ {
const TypeFamilyInstanceTypePack* mentionedTfitp = get<TypeFamilyInstanceTypePack>(mentioned); const TypeFunctionInstanceTypePack* mentionedTfitp = get<TypeFunctionInstanceTypePack>(mentioned);
LUAU_ASSERT(mentionedTfitp); LUAU_ASSERT(mentionedTfitp);
if (areEquivalent(tfitp, *mentionedTfitp)) if (areEquivalent(tfitp, *mentionedTfitp))
{ {
@ -245,7 +245,7 @@ struct TypeChecker2
std::vector<NotNull<Scope>> stack; std::vector<NotNull<Scope>> stack;
std::vector<TypeId> functionDeclStack; std::vector<TypeId> functionDeclStack;
DenseHashSet<TypeId> seenTypeFamilyInstances{nullptr}; DenseHashSet<TypeId> seenTypeFunctionInstances{nullptr};
Normalizer normalizer; Normalizer normalizer;
Subtyping _subtyping; Subtyping _subtyping;
@ -437,12 +437,12 @@ struct TypeChecker2
TypeId checkForFamilyInhabitance(TypeId instance, Location location) TypeId checkForFamilyInhabitance(TypeId instance, Location location)
{ {
if (seenTypeFamilyInstances.find(instance)) if (seenTypeFunctionInstances.find(instance))
return instance; return instance;
seenTypeFamilyInstances.insert(instance); seenTypeFunctionInstances.insert(instance);
ErrorVec errors = reduceFamilies(instance, location, ErrorVec errors = reduceTypeFunctions(instance, location,
TypeFamilyContext{NotNull{&module->internalTypes}, builtinTypes, stack.back(), NotNull{&normalizer}, ice, limits}, true) TypeFunctionContext{NotNull{&module->internalTypes}, builtinTypes, stack.back(), NotNull{&normalizer}, ice, limits}, true)
.errors; .errors;
if (!isErrorSuppressing(location, instance)) if (!isErrorSuppressing(location, instance))
reportErrors(std::move(errors)); reportErrors(std::move(errors));
@ -1731,12 +1731,12 @@ struct TypeChecker2
const FunctionType* inferredFtv = get<FunctionType>(normalizedFnTy->functions.parts.front()); const FunctionType* inferredFtv = get<FunctionType>(normalizedFnTy->functions.parts.front());
LUAU_ASSERT(inferredFtv); LUAU_ASSERT(inferredFtv);
TypeFamilyReductionGuesser guesser{NotNull{&module->internalTypes}, builtinTypes, NotNull{&normalizer}}; TypeFunctionReductionGuesser guesser{NotNull{&module->internalTypes}, builtinTypes, NotNull{&normalizer}};
for (TypeId retTy : inferredFtv->retTypes) for (TypeId retTy : inferredFtv->retTypes)
{ {
if (get<TypeFamilyInstanceType>(follow(retTy))) if (get<TypeFunctionInstanceType>(follow(retTy)))
{ {
TypeFamilyReductionGuessResult result = guesser.guessTypeFamilyReductionForFunction(*fn, inferredFtv, retTy); TypeFunctionReductionGuessResult result = guesser.guessTypeFunctionReductionForFunctionExpr(*fn, inferredFtv, retTy);
if (result.shouldRecommendAnnotation) if (result.shouldRecommendAnnotation)
reportError(ExplicitFunctionAnnotationRecommended{std::move(result.guessedFunctionAnnotations), result.guessedReturnType}, reportError(ExplicitFunctionAnnotationRecommended{std::move(result.guessedFunctionAnnotations), result.guessedReturnType},
fn->location); fn->location);
@ -1853,7 +1853,7 @@ struct TypeChecker2
TypeId rightType = follow(lookupType(expr->right)); TypeId rightType = follow(lookupType(expr->right));
TypeId expectedResult = follow(lookupType(expr)); TypeId expectedResult = follow(lookupType(expr));
if (get<TypeFamilyInstanceType>(expectedResult)) if (get<TypeFunctionInstanceType>(expectedResult))
{ {
checkForInternalFamily(expectedResult, expr->location); checkForInternalFamily(expectedResult, expr->location);
return expectedResult; return expectedResult;
@ -1954,7 +1954,7 @@ struct TypeChecker2
if (!selectedOverloadTy) if (!selectedOverloadTy)
{ {
// reportError(CodeTooComplex{}, expr->location); // reportError(CodeTooComplex{}, expr->location);
// was handled by a type family // was handled by a type function
return expectedResult; return expectedResult;
} }

View File

@ -1,6 +1,6 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/Common.h" #include "Luau/Common.h"
#include "Luau/ConstraintSolver.h" #include "Luau/ConstraintSolver.h"
@ -15,7 +15,7 @@
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/TxnLog.h" #include "Luau/TxnLog.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeFamilyReductionGuesser.h" #include "Luau/TypeFunctionReductionGuesser.h"
#include "Luau/TypeFwd.h" #include "Luau/TypeFwd.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
#include "Luau/Unifier2.h" #include "Luau/Unifier2.h"
@ -24,10 +24,10 @@
#include <iterator> #include <iterator>
// used to control emitting CodeTooComplex warnings on type family reduction // used to control emitting CodeTooComplex warnings on type function reduction
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyGraphReductionMaximumSteps, 1'000'000); LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyGraphReductionMaximumSteps, 1'000'000);
// used to control the limits of type family application over union type arguments // used to control the limits of type function application over union type arguments
// e.g. `mul<a | b, c | d>` blows up into `mul<a, c> | mul<a, d> | mul<b, c> | mul<b, d>` // e.g. `mul<a | b, c | d>` blows up into `mul<a, c> | mul<a, d> | mul<b, c> | mul<b, d>`
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'000); LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'000);
@ -49,7 +49,7 @@ struct InstanceCollector : TypeOnceVisitor
TypeOrTypePackIdSet shouldGuess{nullptr}; TypeOrTypePackIdSet shouldGuess{nullptr};
std::vector<TypeId> cyclicInstance; std::vector<TypeId> cyclicInstance;
bool visit(TypeId ty, const TypeFamilyInstanceType&) override bool visit(TypeId ty, const TypeFunctionInstanceType&) override
{ {
// TypeOnceVisitor performs a depth-first traversal in the absence of // TypeOnceVisitor performs a depth-first traversal in the absence of
// cycles. This means that by pushing to the front of the queue, we will // cycles. This means that by pushing to the front of the queue, we will
@ -58,7 +58,7 @@ struct InstanceCollector : TypeOnceVisitor
// we want to reduce the innermost Add<number, number> instantiation // we want to reduce the innermost Add<number, number> instantiation
// first. // first.
if (DFInt::LuauTypeFamilyUseGuesserDepth >= 0 && typeFamilyDepth > DFInt::LuauTypeFamilyUseGuesserDepth) if (DFInt::LuauTypeFamilyUseGuesserDepth >= 0 && typeFunctionDepth > DFInt::LuauTypeFamilyUseGuesserDepth)
shouldGuess.insert(ty); shouldGuess.insert(ty);
tys.push_front(ty); tys.push_front(ty);
@ -70,7 +70,7 @@ struct InstanceCollector : TypeOnceVisitor
{ {
/// Detected cyclic type pack /// Detected cyclic type pack
TypeId t = follow(ty); TypeId t = follow(ty);
if (get<TypeFamilyInstanceType>(t)) if (get<TypeFunctionInstanceType>(t))
cyclicInstance.push_back(t); cyclicInstance.push_back(t);
} }
@ -79,7 +79,7 @@ struct InstanceCollector : TypeOnceVisitor
return false; return false;
} }
bool visit(TypePackId tp, const TypeFamilyInstanceTypePack&) override bool visit(TypePackId tp, const TypeFunctionInstanceTypePack&) override
{ {
// TypeOnceVisitor performs a depth-first traversal in the absence of // TypeOnceVisitor performs a depth-first traversal in the absence of
// cycles. This means that by pushing to the front of the queue, we will // cycles. This means that by pushing to the front of the queue, we will
@ -88,7 +88,7 @@ struct InstanceCollector : TypeOnceVisitor
// we want to reduce the innermost Add<number, number> instantiation // we want to reduce the innermost Add<number, number> instantiation
// first. // first.
if (DFInt::LuauTypeFamilyUseGuesserDepth >= 0 && typeFamilyDepth > DFInt::LuauTypeFamilyUseGuesserDepth) if (DFInt::LuauTypeFamilyUseGuesserDepth >= 0 && typeFunctionDepth > DFInt::LuauTypeFamilyUseGuesserDepth)
shouldGuess.insert(tp); shouldGuess.insert(tp);
tps.push_front(tp); tps.push_front(tp);
@ -97,23 +97,23 @@ struct InstanceCollector : TypeOnceVisitor
} }
}; };
struct FamilyReducer struct TypeFunctionReducer
{ {
TypeFamilyContext ctx; TypeFunctionContext ctx;
VecDeque<TypeId> queuedTys; VecDeque<TypeId> queuedTys;
VecDeque<TypePackId> queuedTps; VecDeque<TypePackId> queuedTps;
TypeOrTypePackIdSet shouldGuess; TypeOrTypePackIdSet shouldGuess;
std::vector<TypeId> cyclicTypeFamilies; std::vector<TypeId> cyclicTypeFamilies;
TypeOrTypePackIdSet irreducible{nullptr}; TypeOrTypePackIdSet irreducible{nullptr};
FamilyGraphReductionResult result; FunctionGraphReductionResult result;
bool force = false; bool force = false;
// Local to the constraint being reduced. // Local to the constraint being reduced.
Location location; Location location;
FamilyReducer(VecDeque<TypeId> queuedTys, VecDeque<TypePackId> queuedTps, TypeOrTypePackIdSet shouldGuess, std::vector<TypeId> cyclicTypes, TypeFunctionReducer(VecDeque<TypeId> queuedTys, VecDeque<TypePackId> queuedTps, TypeOrTypePackIdSet shouldGuess, std::vector<TypeId> cyclicTypes,
Location location, TypeFamilyContext ctx, bool force = false) Location location, TypeFunctionContext ctx, bool force = false)
: ctx(ctx) : ctx(ctx)
, queuedTys(std::move(queuedTys)) , queuedTys(std::move(queuedTys))
, queuedTps(std::move(queuedTps)) , queuedTps(std::move(queuedTps))
@ -126,7 +126,7 @@ struct FamilyReducer
enum class SkipTestResult enum class SkipTestResult
{ {
CyclicTypeFamily, CyclicTypeFunction,
Irreducible, Irreducible,
Defer, Defer,
Okay, Okay,
@ -136,12 +136,12 @@ struct FamilyReducer
{ {
ty = follow(ty); ty = follow(ty);
if (is<TypeFamilyInstanceType>(ty)) if (is<TypeFunctionInstanceType>(ty))
{ {
for (auto t : cyclicTypeFamilies) for (auto t : cyclicTypeFamilies)
{ {
if (ty == t) if (ty == t)
return SkipTestResult::CyclicTypeFamily; return SkipTestResult::CyclicTypeFunction;
} }
if (!irreducible.contains(ty)) if (!irreducible.contains(ty))
@ -161,7 +161,7 @@ struct FamilyReducer
{ {
ty = follow(ty); ty = follow(ty);
if (is<TypeFamilyInstanceTypePack>(ty)) if (is<TypeFunctionInstanceTypePack>(ty))
{ {
if (!irreducible.contains(ty)) if (!irreducible.contains(ty))
return SkipTestResult::Defer; return SkipTestResult::Defer;
@ -181,7 +181,7 @@ struct FamilyReducer
{ {
if (subject->owningArena != ctx.arena.get()) if (subject->owningArena != ctx.arena.get())
{ {
result.errors.emplace_back(location, InternalError{"Attempting to modify a type family instance from another arena"}); result.errors.emplace_back(location, InternalError{"Attempting to modify a type function instance from another arena"});
return; return;
} }
@ -197,7 +197,7 @@ struct FamilyReducer
} }
template<typename T> template<typename T>
void handleFamilyReduction(T subject, TypeFamilyReductionResult<T> reduction) void handleTypeFunctionReduction(T subject, TypeFunctionReductionResult<T> reduction)
{ {
if (reduction.result) if (reduction.result)
replace(subject, *reduction.result); replace(subject, *reduction.result);
@ -211,9 +211,9 @@ struct FamilyReducer
printf("%s is uninhabited\n", toString(subject, {true}).c_str()); printf("%s is uninhabited\n", toString(subject, {true}).c_str());
if constexpr (std::is_same_v<T, TypeId>) if constexpr (std::is_same_v<T, TypeId>)
result.errors.push_back(TypeError{location, UninhabitedTypeFamily{subject}}); result.errors.push_back(TypeError{location, UninhabitedTypeFunction{subject}});
else if constexpr (std::is_same_v<T, TypePackId>) else if constexpr (std::is_same_v<T, TypePackId>)
result.errors.push_back(TypeError{location, UninhabitedTypePackFamily{subject}}); result.errors.push_back(TypeError{location, UninhabitedTypePackFunction{subject}});
} }
else if (!reduction.uninhabited && !force) else if (!reduction.uninhabited && !force)
{ {
@ -301,7 +301,7 @@ struct FamilyReducer
if (FFlag::DebugLuauLogTypeFamilies) if (FFlag::DebugLuauLogTypeFamilies)
printf("Flagged %s for reduction with guesser.\n", toString(subject, {true}).c_str()); printf("Flagged %s for reduction with guesser.\n", toString(subject, {true}).c_str());
TypeFamilyReductionGuesser guesser{ctx.arena, ctx.builtins, ctx.normalizer}; TypeFunctionReductionGuesser guesser{ctx.arena, ctx.builtins, ctx.normalizer};
auto guessed = guesser.guess(subject); auto guessed = guesser.guess(subject);
if (guessed) if (guessed)
@ -332,11 +332,11 @@ struct FamilyReducer
if (FFlag::DebugLuauLogTypeFamilies) if (FFlag::DebugLuauLogTypeFamilies)
printf("Trying to reduce %s\n", toString(subject, {true}).c_str()); printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
if (const TypeFamilyInstanceType* tfit = get<TypeFamilyInstanceType>(subject)) if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(subject))
{ {
SkipTestResult testCyclic = testForSkippability(subject); SkipTestResult testCyclic = testForSkippability(subject);
if (!testParameters(subject, tfit) && testCyclic != SkipTestResult::CyclicTypeFamily) if (!testParameters(subject, tfit) && testCyclic != SkipTestResult::CyclicTypeFunction)
{ {
if (FFlag::DebugLuauLogTypeFamilies) if (FFlag::DebugLuauLogTypeFamilies)
printf("Irreducible due to irreducible/pending and a non-cyclic family\n"); printf("Irreducible due to irreducible/pending and a non-cyclic family\n");
@ -347,8 +347,8 @@ struct FamilyReducer
if (tryGuessing(subject)) if (tryGuessing(subject))
return; return;
TypeFamilyReductionResult<TypeId> result = tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); TypeFunctionReductionResult<TypeId> result = tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx});
handleFamilyReduction(subject, result); handleTypeFunctionReduction(subject, result);
} }
} }
@ -363,7 +363,7 @@ struct FamilyReducer
if (FFlag::DebugLuauLogTypeFamilies) if (FFlag::DebugLuauLogTypeFamilies)
printf("Trying to reduce %s\n", toString(subject, {true}).c_str()); printf("Trying to reduce %s\n", toString(subject, {true}).c_str());
if (const TypeFamilyInstanceTypePack* tfit = get<TypeFamilyInstanceTypePack>(subject)) if (const TypeFunctionInstanceTypePack* tfit = get<TypeFunctionInstanceTypePack>(subject))
{ {
if (!testParameters(subject, tfit)) if (!testParameters(subject, tfit))
return; return;
@ -371,8 +371,8 @@ struct FamilyReducer
if (tryGuessing(subject)) if (tryGuessing(subject))
return; return;
TypeFamilyReductionResult<TypePackId> result = tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); TypeFunctionReductionResult<TypePackId> result = tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx});
handleFamilyReduction(subject, result); handleTypeFunctionReduction(subject, result);
} }
} }
@ -385,10 +385,10 @@ struct FamilyReducer
} }
}; };
static FamilyGraphReductionResult reduceFamiliesInternal(VecDeque<TypeId> queuedTys, VecDeque<TypePackId> queuedTps, TypeOrTypePackIdSet shouldGuess, static FunctionGraphReductionResult reduceFamiliesInternal(VecDeque<TypeId> queuedTys, VecDeque<TypePackId> queuedTps, TypeOrTypePackIdSet shouldGuess,
std::vector<TypeId> cyclics, Location location, TypeFamilyContext ctx, bool force) std::vector<TypeId> cyclics, Location location, TypeFunctionContext ctx, bool force)
{ {
FamilyReducer reducer{std::move(queuedTys), std::move(queuedTps), std::move(shouldGuess), std::move(cyclics), location, ctx, force}; TypeFunctionReducer reducer{std::move(queuedTys), std::move(queuedTps), std::move(shouldGuess), std::move(cyclics), location, ctx, force};
int iterationCount = 0; int iterationCount = 0;
while (!reducer.done()) while (!reducer.done())
@ -406,7 +406,7 @@ static FamilyGraphReductionResult reduceFamiliesInternal(VecDeque<TypeId> queued
return std::move(reducer.result); return std::move(reducer.result);
} }
FamilyGraphReductionResult reduceFamilies(TypeId entrypoint, Location location, TypeFamilyContext ctx, bool force) FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location location, TypeFunctionContext ctx, bool force)
{ {
InstanceCollector collector; InstanceCollector collector;
@ -416,7 +416,7 @@ FamilyGraphReductionResult reduceFamilies(TypeId entrypoint, Location location,
} }
catch (RecursionLimitException&) catch (RecursionLimitException&)
{ {
return FamilyGraphReductionResult{}; return FunctionGraphReductionResult{};
} }
if (collector.tys.empty() && collector.tps.empty()) if (collector.tys.empty() && collector.tps.empty())
@ -426,7 +426,7 @@ FamilyGraphReductionResult reduceFamilies(TypeId entrypoint, Location location,
std::move(collector.cyclicInstance), location, ctx, force); std::move(collector.cyclicInstance), location, ctx, force);
} }
FamilyGraphReductionResult reduceFamilies(TypePackId entrypoint, Location location, TypeFamilyContext ctx, bool force) FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location location, TypeFunctionContext ctx, bool force)
{ {
InstanceCollector collector; InstanceCollector collector;
@ -436,7 +436,7 @@ FamilyGraphReductionResult reduceFamilies(TypePackId entrypoint, Location locati
} }
catch (RecursionLimitException&) catch (RecursionLimitException&)
{ {
return FamilyGraphReductionResult{}; return FunctionGraphReductionResult{};
} }
if (collector.tys.empty() && collector.tps.empty()) if (collector.tys.empty() && collector.tps.empty())
@ -448,12 +448,12 @@ FamilyGraphReductionResult reduceFamilies(TypePackId entrypoint, Location locati
bool isPending(TypeId ty, ConstraintSolver* solver) bool isPending(TypeId ty, ConstraintSolver* solver)
{ {
return is<BlockedType, PendingExpansionType, TypeFamilyInstanceType>(ty) || (solver && solver->hasUnresolvedConstraints(ty)); return is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(ty) || (solver && solver->hasUnresolvedConstraints(ty));
} }
template<typename F, typename... Args> template<typename F, typename... Args>
static std::optional<TypeFamilyReductionResult<TypeId>> tryDistributeTypeFamilyApp(F f, TypeId instance, const std::vector<TypeId>& typeParams, static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunctionApp(F f, TypeId instance, const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx, Args&&... args) const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx, Args&&... args)
{ {
// op (a | b) (c | d) ~ (op a (c | d)) | (op b (c | d)) ~ (op a c) | (op a d) | (op b c) | (op b d) // op (a | b) (c | d) ~ (op a (c | d)) | (op b (c | d)) ~ (op a c) | (op a d) | (op b c) | (op b d)
bool uninhabited = false; bool uninhabited = false;
@ -483,7 +483,7 @@ static std::optional<TypeFamilyReductionResult<TypeId>> tryDistributeTypeFamilyA
cartesianProductSize *= std::distance(begin(ut), end(ut)); cartesianProductSize *= std::distance(begin(ut), end(ut));
// TODO: We'd like to report that the type family application is too complex here. // TODO: We'd like to report that the type function application is too complex here.
if (size_t(DFInt::LuauTypeFamilyApplicationCartesianProductLimit) <= cartesianProductSize) if (size_t(DFInt::LuauTypeFamilyApplicationCartesianProductLimit) <= cartesianProductSize)
return {{std::nullopt, true, {}, {}}}; return {{std::nullopt, true, {}, {}}};
} }
@ -498,7 +498,7 @@ static std::optional<TypeFamilyReductionResult<TypeId>> tryDistributeTypeFamilyA
{ {
arguments[unionIndex] = option; arguments[unionIndex] = option;
TypeFamilyReductionResult<TypeId> result = f(instance, arguments, packParams, ctx, args...); TypeFunctionReductionResult<TypeId> result = f(instance, arguments, packParams, ctx, args...);
blockedTypes.insert(blockedTypes.end(), result.blockedTypes.begin(), result.blockedTypes.end()); blockedTypes.insert(blockedTypes.end(), result.blockedTypes.begin(), result.blockedTypes.end());
uninhabited |= result.uninhabited; uninhabited |= result.uninhabited;
@ -516,8 +516,8 @@ static std::optional<TypeFamilyReductionResult<TypeId>> tryDistributeTypeFamilyA
if (results.size() == 1) if (results.size() == 1)
return {{results[0], false, {}, {}}}; return {{results[0], false, {}, {}}};
TypeId resultTy = ctx->arena->addType(TypeFamilyInstanceType{ TypeId resultTy = ctx->arena->addType(TypeFunctionInstanceType{
NotNull{&builtinTypeFunctions().unionFamily}, NotNull{&builtinTypeFunctions().unionFunc},
std::move(results), std::move(results),
{}, {},
}); });
@ -528,12 +528,12 @@ static std::optional<TypeFamilyReductionResult<TypeId>> tryDistributeTypeFamilyA
return std::nullopt; return std::nullopt;
} }
TypeFamilyReductionResult<TypeId> notFamilyFn( TypeFunctionReductionResult<TypeId> notFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 1 || !packParams.empty()) if (typeParams.size() != 1 || !packParams.empty())
{ {
ctx->ice->ice("not type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("not type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -545,19 +545,19 @@ TypeFamilyReductionResult<TypeId> notFamilyFn(
if (isPending(ty, ctx->solver)) if (isPending(ty, ctx->solver))
return {std::nullopt, false, {ty}, {}}; return {std::nullopt, false, {ty}, {}};
if (auto result = tryDistributeTypeFamilyApp(notFamilyFn, instance, typeParams, packParams, ctx)) if (auto result = tryDistributeTypeFunctionApp(notFamilyFn, instance, typeParams, packParams, ctx))
return *result; return *result;
// `not` operates on anything and returns a `boolean` always. // `not` operates on anything and returns a `boolean` always.
return {ctx->builtins->booleanType, false, {}, {}}; return {ctx->builtins->booleanType, false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> lenFamilyFn( TypeFunctionReductionResult<TypeId> lenFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 1 || !packParams.empty()) if (typeParams.size() != 1 || !packParams.empty())
{ {
ctx->ice->ice("len type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("len type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -604,7 +604,7 @@ TypeFamilyReductionResult<TypeId> lenFamilyFn(
if (normTy->hasTopTable() || get<TableType>(normalizedOperand)) if (normTy->hasTopTable() || get<TableType>(normalizedOperand))
return {ctx->builtins->numberType, false, {}, {}}; return {ctx->builtins->numberType, false, {}, {}};
if (auto result = tryDistributeTypeFamilyApp(notFamilyFn, instance, typeParams, packParams, ctx)) if (auto result = tryDistributeTypeFunctionApp(notFamilyFn, instance, typeParams, packParams, ctx))
return *result; return *result;
// findMetatableEntry demands the ability to emit errors, so we must give it // findMetatableEntry demands the ability to emit errors, so we must give it
@ -644,12 +644,12 @@ TypeFamilyReductionResult<TypeId> lenFamilyFn(
return {ctx->builtins->numberType, false, {}, {}}; return {ctx->builtins->numberType, false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> unmFamilyFn( TypeFunctionReductionResult<TypeId> unmFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 1 || !packParams.empty()) if (typeParams.size() != 1 || !packParams.empty())
{ {
ctx->ice->ice("unm type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("unm type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -689,7 +689,7 @@ TypeFamilyReductionResult<TypeId> unmFamilyFn(
if (normTy->isExactlyNumber()) if (normTy->isExactlyNumber())
return {ctx->builtins->numberType, false, {}, {}}; return {ctx->builtins->numberType, false, {}, {}};
if (auto result = tryDistributeTypeFamilyApp(notFamilyFn, instance, typeParams, packParams, ctx)) if (auto result = tryDistributeTypeFunctionApp(notFamilyFn, instance, typeParams, packParams, ctx))
return *result; return *result;
// findMetatableEntry demands the ability to emit errors, so we must give it // findMetatableEntry demands the ability to emit errors, so we must give it
@ -731,7 +731,7 @@ TypeFamilyReductionResult<TypeId> unmFamilyFn(
return {std::nullopt, true, {}, {}}; return {std::nullopt, true, {}, {}};
} }
NotNull<Constraint> TypeFamilyContext::pushConstraint(ConstraintV&& c) NotNull<Constraint> TypeFunctionContext::pushConstraint(ConstraintV&& c)
{ {
LUAU_ASSERT(solver); LUAU_ASSERT(solver);
NotNull<Constraint> newConstraint = solver->pushConstraint(scope, constraint ? constraint->location : Location{}, std::move(c)); NotNull<Constraint> newConstraint = solver->pushConstraint(scope, constraint ? constraint->location : Location{}, std::move(c));
@ -744,12 +744,12 @@ NotNull<Constraint> TypeFamilyContext::pushConstraint(ConstraintV&& c)
return newConstraint; return newConstraint;
} }
TypeFamilyReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const std::vector<TypeId>& typeParams, TypeFunctionReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx, const std::string metamethod) const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx, const std::string metamethod)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("encountered a type family instance without the required argument structure"); ctx->ice->ice("encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -803,7 +803,7 @@ TypeFamilyReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const st
if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber()) if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber())
return {ctx->builtins->numberType, false, {}, {}}; return {ctx->builtins->numberType, false, {}, {}};
if (auto result = tryDistributeTypeFamilyApp(numericBinopFamilyFn, instance, typeParams, packParams, ctx, metamethod)) if (auto result = tryDistributeTypeFunctionApp(numericBinopFamilyFn, instance, typeParams, packParams, ctx, metamethod))
return *result; return *result;
// findMetatableEntry demands the ability to emit errors, so we must give it // findMetatableEntry demands the ability to emit errors, so we must give it
@ -847,96 +847,96 @@ TypeFamilyReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const st
return {extracted.head.front(), false, {}, {}}; return {extracted.head.front(), false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> addFamilyFn( TypeFunctionReductionResult<TypeId> addFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("add type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("add type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__add"); return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__add");
} }
TypeFamilyReductionResult<TypeId> subFamilyFn( TypeFunctionReductionResult<TypeId> subFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("sub type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("sub type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__sub"); return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__sub");
} }
TypeFamilyReductionResult<TypeId> mulFamilyFn( TypeFunctionReductionResult<TypeId> mulFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("mul type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("mul type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__mul"); return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__mul");
} }
TypeFamilyReductionResult<TypeId> divFamilyFn( TypeFunctionReductionResult<TypeId> divFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("div type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("div type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__div"); return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__div");
} }
TypeFamilyReductionResult<TypeId> idivFamilyFn( TypeFunctionReductionResult<TypeId> idivFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("integer div type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("integer div type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__idiv"); return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__idiv");
} }
TypeFamilyReductionResult<TypeId> powFamilyFn( TypeFunctionReductionResult<TypeId> powFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("pow type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("pow type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__pow"); return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__pow");
} }
TypeFamilyReductionResult<TypeId> modFamilyFn( TypeFunctionReductionResult<TypeId> modFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("modulo type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("modulo type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__mod"); return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__mod");
} }
TypeFamilyReductionResult<TypeId> concatFamilyFn( TypeFunctionReductionResult<TypeId> concatFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("concat type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("concat type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -987,7 +987,7 @@ TypeFamilyReductionResult<TypeId> concatFamilyFn(
if ((normLhsTy->isSubtypeOfString() || normLhsTy->isExactlyNumber()) && (normRhsTy->isSubtypeOfString() || normRhsTy->isExactlyNumber())) if ((normLhsTy->isSubtypeOfString() || normLhsTy->isExactlyNumber()) && (normRhsTy->isSubtypeOfString() || normRhsTy->isExactlyNumber()))
return {ctx->builtins->stringType, false, {}, {}}; return {ctx->builtins->stringType, false, {}, {}};
if (auto result = tryDistributeTypeFamilyApp(concatFamilyFn, instance, typeParams, packParams, ctx)) if (auto result = tryDistributeTypeFunctionApp(concatFamilyFn, instance, typeParams, packParams, ctx))
return *result; return *result;
// findMetatableEntry demands the ability to emit errors, so we must give it // findMetatableEntry demands the ability to emit errors, so we must give it
@ -1039,12 +1039,12 @@ TypeFamilyReductionResult<TypeId> concatFamilyFn(
return {ctx->builtins->stringType, false, {}, {}}; return {ctx->builtins->stringType, false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> andFamilyFn( TypeFunctionReductionResult<TypeId> andFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("and type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("and type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1090,12 +1090,12 @@ TypeFamilyReductionResult<TypeId> andFamilyFn(
return {overallResult.result, false, std::move(blockedTypes), {}}; return {overallResult.result, false, std::move(blockedTypes), {}};
} }
TypeFamilyReductionResult<TypeId> orFamilyFn( TypeFunctionReductionResult<TypeId> orFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("or type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("or type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1141,13 +1141,13 @@ TypeFamilyReductionResult<TypeId> orFamilyFn(
return {overallResult.result, false, std::move(blockedTypes), {}}; return {overallResult.result, false, std::move(blockedTypes), {}};
} }
static TypeFamilyReductionResult<TypeId> comparisonFamilyFn(TypeId instance, const std::vector<TypeId>& typeParams, static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx, const std::string metamethod) const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx, const std::string metamethod)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("encountered a type family instance without the required argument structure"); ctx->ice->ice("encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1238,7 +1238,7 @@ static TypeFamilyReductionResult<TypeId> comparisonFamilyFn(TypeId instance, con
if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber()) if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber())
return {ctx->builtins->booleanType, false, {}, {}}; return {ctx->builtins->booleanType, false, {}, {}};
if (auto result = tryDistributeTypeFamilyApp(comparisonFamilyFn, instance, typeParams, packParams, ctx, metamethod)) if (auto result = tryDistributeTypeFunctionApp(comparisonFamilyFn, instance, typeParams, packParams, ctx, metamethod))
return *result; return *result;
// findMetatableEntry demands the ability to emit errors, so we must give it // findMetatableEntry demands the ability to emit errors, so we must give it
@ -1280,36 +1280,36 @@ static TypeFamilyReductionResult<TypeId> comparisonFamilyFn(TypeId instance, con
return {ctx->builtins->booleanType, false, {}, {}}; return {ctx->builtins->booleanType, false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> ltFamilyFn( TypeFunctionReductionResult<TypeId> ltFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("lt type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("lt type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return comparisonFamilyFn(instance, typeParams, packParams, ctx, "__lt"); return comparisonFamilyFn(instance, typeParams, packParams, ctx, "__lt");
} }
TypeFamilyReductionResult<TypeId> leFamilyFn( TypeFunctionReductionResult<TypeId> leFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("le type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("le type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return comparisonFamilyFn(instance, typeParams, packParams, ctx, "__le"); return comparisonFamilyFn(instance, typeParams, packParams, ctx, "__le");
} }
TypeFamilyReductionResult<TypeId> eqFamilyFn( TypeFunctionReductionResult<TypeId> eqFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("eq type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("eq type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1435,12 +1435,12 @@ struct FindRefinementBlockers : TypeOnceVisitor
}; };
TypeFamilyReductionResult<TypeId> refineFamilyFn( TypeFunctionReductionResult<TypeId> refineFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("refine type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("refine type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1520,12 +1520,12 @@ TypeFamilyReductionResult<TypeId> refineFamilyFn(
return {resultTy, false, {}, {}}; return {resultTy, false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> singletonFamilyFn( TypeFunctionReductionResult<TypeId> singletonFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 1 || !packParams.empty()) if (typeParams.size() != 1 || !packParams.empty())
{ {
ctx->ice->ice("singleton type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("singleton type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1557,12 +1557,12 @@ TypeFamilyReductionResult<TypeId> singletonFamilyFn(
return {ctx->builtins->unknownType, false, {}, {}}; return {ctx->builtins->unknownType, false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> unionFamilyFn( TypeFunctionReductionResult<TypeId> unionFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (!packParams.empty()) if (!packParams.empty())
{ {
ctx->ice->ice("union type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("union type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1618,12 +1618,12 @@ TypeFamilyReductionResult<TypeId> unionFamilyFn(
} }
TypeFamilyReductionResult<TypeId> intersectFamilyFn( TypeFunctionReductionResult<TypeId> intersectFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (!packParams.empty()) if (!packParams.empty())
{ {
ctx->ice->ice("intersect type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("intersect type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1673,7 +1673,7 @@ TypeFamilyReductionResult<TypeId> intersectFamilyFn(
// computes the keys of `ty` into `result` // computes the keys of `ty` into `result`
// `isRaw` parameter indicates whether or not we should follow __index metamethods // `isRaw` parameter indicates whether or not we should follow __index metamethods
// returns `false` if `result` should be ignored because the answer is "all strings" // returns `false` if `result` should be ignored because the answer is "all strings"
bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& seen, bool isRaw, NotNull<TypeFamilyContext> ctx) bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& seen, bool isRaw, NotNull<TypeFunctionContext> ctx)
{ {
// if the type is the top table type, the answer is just "all strings" // if the type is the top table type, the answer is just "all strings"
if (get<PrimitiveType>(ty)) if (get<PrimitiveType>(ty))
@ -1725,12 +1725,12 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
return false; return false;
} }
TypeFamilyReductionResult<TypeId> keyofFamilyImpl( TypeFunctionReductionResult<TypeId> keyofFamilyImpl(
const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx, bool isRaw) const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx, bool isRaw)
{ {
if (typeParams.size() != 1 || !packParams.empty()) if (typeParams.size() != 1 || !packParams.empty())
{ {
ctx->ice->ice("keyof type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("keyof type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1842,24 +1842,24 @@ TypeFamilyReductionResult<TypeId> keyofFamilyImpl(
return {ctx->arena->addType(UnionType{singletons}), false, {}, {}}; return {ctx->arena->addType(UnionType{singletons}), false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> keyofFamilyFn( TypeFunctionReductionResult<TypeId> keyofFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 1 || !packParams.empty()) if (typeParams.size() != 1 || !packParams.empty())
{ {
ctx->ice->ice("keyof type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("keyof type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return keyofFamilyImpl(typeParams, packParams, ctx, /* isRaw */ false); return keyofFamilyImpl(typeParams, packParams, ctx, /* isRaw */ false);
} }
TypeFamilyReductionResult<TypeId> rawkeyofFamilyFn( TypeFunctionReductionResult<TypeId> rawkeyofFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 1 || !packParams.empty()) if (typeParams.size() != 1 || !packParams.empty())
{ {
ctx->ice->ice("rawkeyof type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("rawkeyof type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
@ -1870,7 +1870,7 @@ TypeFamilyReductionResult<TypeId> rawkeyofFamilyFn(
If found, appends that property to `result` and returns true If found, appends that property to `result` and returns true
Else, returns false */ Else, returns false */
bool searchPropsAndIndexer( bool searchPropsAndIndexer(
TypeId ty, TableType::Props tblProps, std::optional<TableIndexer> tblIndexer, DenseHashSet<TypeId>& result, NotNull<TypeFamilyContext> ctx) TypeId ty, TableType::Props tblProps, std::optional<TableIndexer> tblIndexer, DenseHashSet<TypeId>& result, NotNull<TypeFunctionContext> ctx)
{ {
ty = follow(ty); ty = follow(ty);
@ -1920,7 +1920,7 @@ bool searchPropsAndIndexer(
/* Handles recursion / metamethods of tables/classes /* Handles recursion / metamethods of tables/classes
`isRaw` parameter indicates whether or not we should follow __index metamethods `isRaw` parameter indicates whether or not we should follow __index metamethods
returns false if property of `ty` could not be found */ returns false if property of `ty` could not be found */
bool tblIndexInto(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result, NotNull<TypeFamilyContext> ctx, bool isRaw) bool tblIndexInto(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result, NotNull<TypeFunctionContext> ctx, bool isRaw)
{ {
indexer = follow(indexer); indexer = follow(indexer);
indexee = follow(indexee); indexee = follow(indexee);
@ -1960,8 +1960,8 @@ bool tblIndexInto(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result,
/* Vocabulary note: indexee refers to the type that contains the properties, /* Vocabulary note: indexee refers to the type that contains the properties,
indexer refers to the type that is used to access indexee indexer refers to the type that is used to access indexee
Example: index<Person, "name"> => `Person` is the indexee and `"name"` is the indexer */ Example: index<Person, "name"> => `Person` is the indexee and `"name"` is the indexer */
TypeFamilyReductionResult<TypeId> indexFamilyImpl( TypeFunctionReductionResult<TypeId> indexFamilyImpl(
const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx, bool isRaw) const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx, bool isRaw)
{ {
TypeId indexeeTy = follow(typeParams.at(0)); TypeId indexeeTy = follow(typeParams.at(0));
std::shared_ptr<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy); std::shared_ptr<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy);
@ -2064,104 +2064,104 @@ TypeFamilyReductionResult<TypeId> indexFamilyImpl(
return {ctx->arena->addType(UnionType{std::vector<TypeId>(properties.begin(), properties.end())}), false, {}, {}}; return {ctx->arena->addType(UnionType{std::vector<TypeId>(properties.begin(), properties.end())}), false, {}, {}};
} }
TypeFamilyReductionResult<TypeId> indexFamilyFn( TypeFunctionReductionResult<TypeId> indexFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("index type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("index type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ false); return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ false);
} }
TypeFamilyReductionResult<TypeId> rawgetFamilyFn( TypeFunctionReductionResult<TypeId> rawgetFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{ {
if (typeParams.size() != 2 || !packParams.empty()) if (typeParams.size() != 2 || !packParams.empty())
{ {
ctx->ice->ice("rawget type family: encountered a type family instance without the required argument structure"); ctx->ice->ice("rawget type function: encountered a type function instance without the required argument structure");
LUAU_ASSERT(false); LUAU_ASSERT(false);
} }
return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ true); return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ true);
} }
BuiltinTypeFamilies::BuiltinTypeFamilies() BuiltinTypeFunctions::BuiltinTypeFunctions()
: notFamily{"not", notFamilyFn} : notFunc{"not", notFamilyFn}
, lenFamily{"len", lenFamilyFn} , lenFunc{"len", lenFamilyFn}
, unmFamily{"unm", unmFamilyFn} , unmFunc{"unm", unmFamilyFn}
, addFamily{"add", addFamilyFn} , addFunc{"add", addFamilyFn}
, subFamily{"sub", subFamilyFn} , subFunc{"sub", subFamilyFn}
, mulFamily{"mul", mulFamilyFn} , mulFunc{"mul", mulFamilyFn}
, divFamily{"div", divFamilyFn} , divFunc{"div", divFamilyFn}
, idivFamily{"idiv", idivFamilyFn} , idivFunc{"idiv", idivFamilyFn}
, powFamily{"pow", powFamilyFn} , powFunc{"pow", powFamilyFn}
, modFamily{"mod", modFamilyFn} , modFunc{"mod", modFamilyFn}
, concatFamily{"concat", concatFamilyFn} , concatFunc{"concat", concatFamilyFn}
, andFamily{"and", andFamilyFn} , andFunc{"and", andFamilyFn}
, orFamily{"or", orFamilyFn} , orFunc{"or", orFamilyFn}
, ltFamily{"lt", ltFamilyFn} , ltFunc{"lt", ltFamilyFn}
, leFamily{"le", leFamilyFn} , leFunc{"le", leFamilyFn}
, eqFamily{"eq", eqFamilyFn} , eqFunc{"eq", eqFamilyFn}
, refineFamily{"refine", refineFamilyFn} , refineFunc{"refine", refineFamilyFn}
, singletonFamily{"singleton", singletonFamilyFn} , singletonFunc{"singleton", singletonFamilyFn}
, unionFamily{"union", unionFamilyFn} , unionFunc{"union", unionFamilyFn}
, intersectFamily{"intersect", intersectFamilyFn} , intersectFunc{"intersect", intersectFamilyFn}
, keyofFamily{"keyof", keyofFamilyFn} , keyofFunc{"keyof", keyofFamilyFn}
, rawkeyofFamily{"rawkeyof", rawkeyofFamilyFn} , rawkeyofFunc{"rawkeyof", rawkeyofFamilyFn}
, indexFamily{"index", indexFamilyFn} , indexFunc{"index", indexFamilyFn}
, rawgetFamily{"rawget", rawgetFamilyFn} , rawgetFunc{"rawget", rawgetFamilyFn}
{ {
} }
void BuiltinTypeFamilies::addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const void BuiltinTypeFunctions::addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const
{ {
// make a type function for a one-argument type family // make a type function for a one-argument type function
auto mkUnaryTypeFamily = [&](const TypeFamily* family) { auto mkUnaryTypeFunction = [&](const TypeFunction* tf) {
TypeId t = arena->addType(GenericType{"T"}); TypeId t = arena->addType(GenericType{"T"});
GenericTypeDefinition genericT{t}; GenericTypeDefinition genericT{t};
return TypeFun{{genericT}, arena->addType(TypeFamilyInstanceType{NotNull{family}, {t}, {}})}; return TypeFun{{genericT}, arena->addType(TypeFunctionInstanceType{NotNull{tf}, {t}, {}})};
}; };
// make a type function for a two-argument type family // make a type function for a two-argument type function
auto mkBinaryTypeFamily = [&](const TypeFamily* family) { auto mkBinaryTypeFunction = [&](const TypeFunction* tf) {
TypeId t = arena->addType(GenericType{"T"}); TypeId t = arena->addType(GenericType{"T"});
TypeId u = arena->addType(GenericType{"U"}); TypeId u = arena->addType(GenericType{"U"});
GenericTypeDefinition genericT{t}; GenericTypeDefinition genericT{t};
GenericTypeDefinition genericU{u, {t}}; GenericTypeDefinition genericU{u, {t}};
return TypeFun{{genericT, genericU}, arena->addType(TypeFamilyInstanceType{NotNull{family}, {t, u}, {}})}; return TypeFun{{genericT, genericU}, arena->addType(TypeFunctionInstanceType{NotNull{tf}, {t, u}, {}})};
}; };
scope->exportedTypeBindings[lenFamily.name] = mkUnaryTypeFamily(&lenFamily); scope->exportedTypeBindings[lenFunc.name] = mkUnaryTypeFunction(&lenFunc);
scope->exportedTypeBindings[unmFamily.name] = mkUnaryTypeFamily(&unmFamily); scope->exportedTypeBindings[unmFunc.name] = mkUnaryTypeFunction(&unmFunc);
scope->exportedTypeBindings[addFamily.name] = mkBinaryTypeFamily(&addFamily); scope->exportedTypeBindings[addFunc.name] = mkBinaryTypeFunction(&addFunc);
scope->exportedTypeBindings[subFamily.name] = mkBinaryTypeFamily(&subFamily); scope->exportedTypeBindings[subFunc.name] = mkBinaryTypeFunction(&subFunc);
scope->exportedTypeBindings[mulFamily.name] = mkBinaryTypeFamily(&mulFamily); scope->exportedTypeBindings[mulFunc.name] = mkBinaryTypeFunction(&mulFunc);
scope->exportedTypeBindings[divFamily.name] = mkBinaryTypeFamily(&divFamily); scope->exportedTypeBindings[divFunc.name] = mkBinaryTypeFunction(&divFunc);
scope->exportedTypeBindings[idivFamily.name] = mkBinaryTypeFamily(&idivFamily); scope->exportedTypeBindings[idivFunc.name] = mkBinaryTypeFunction(&idivFunc);
scope->exportedTypeBindings[powFamily.name] = mkBinaryTypeFamily(&powFamily); scope->exportedTypeBindings[powFunc.name] = mkBinaryTypeFunction(&powFunc);
scope->exportedTypeBindings[modFamily.name] = mkBinaryTypeFamily(&modFamily); scope->exportedTypeBindings[modFunc.name] = mkBinaryTypeFunction(&modFunc);
scope->exportedTypeBindings[concatFamily.name] = mkBinaryTypeFamily(&concatFamily); scope->exportedTypeBindings[concatFunc.name] = mkBinaryTypeFunction(&concatFunc);
scope->exportedTypeBindings[ltFamily.name] = mkBinaryTypeFamily(&ltFamily); scope->exportedTypeBindings[ltFunc.name] = mkBinaryTypeFunction(&ltFunc);
scope->exportedTypeBindings[leFamily.name] = mkBinaryTypeFamily(&leFamily); scope->exportedTypeBindings[leFunc.name] = mkBinaryTypeFunction(&leFunc);
scope->exportedTypeBindings[eqFamily.name] = mkBinaryTypeFamily(&eqFamily); scope->exportedTypeBindings[eqFunc.name] = mkBinaryTypeFunction(&eqFunc);
scope->exportedTypeBindings[keyofFamily.name] = mkUnaryTypeFamily(&keyofFamily); scope->exportedTypeBindings[keyofFunc.name] = mkUnaryTypeFunction(&keyofFunc);
scope->exportedTypeBindings[rawkeyofFamily.name] = mkUnaryTypeFamily(&rawkeyofFamily); scope->exportedTypeBindings[rawkeyofFunc.name] = mkUnaryTypeFunction(&rawkeyofFunc);
scope->exportedTypeBindings[indexFamily.name] = mkBinaryTypeFamily(&indexFamily); scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunction(&indexFunc);
scope->exportedTypeBindings[rawgetFamily.name] = mkBinaryTypeFamily(&rawgetFamily); scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunction(&rawgetFunc);
} }
const BuiltinTypeFamilies& builtinTypeFunctions() const BuiltinTypeFunctions& builtinTypeFunctions()
{ {
static std::unique_ptr<const BuiltinTypeFamilies> result = std::make_unique<BuiltinTypeFamilies>(); static std::unique_ptr<const BuiltinTypeFunctions> result = std::make_unique<BuiltinTypeFunctions>();
return *result; return *result;
} }

View File

@ -1,9 +1,9 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeFamilyReductionGuesser.h" #include "Luau/TypeFunctionReductionGuesser.h"
#include "Luau/DenseHash.h" #include "Luau/DenseHash.h"
#include "Luau/Normalize.h" #include "Luau/Normalize.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
@ -23,7 +23,7 @@ struct InstanceCollector2 : TypeOnceVisitor
DenseHashSet<TypeId> cyclicInstance{nullptr}; DenseHashSet<TypeId> cyclicInstance{nullptr};
DenseHashSet<TypeId> instanceArguments{nullptr}; DenseHashSet<TypeId> instanceArguments{nullptr};
bool visit(TypeId ty, const TypeFamilyInstanceType& it) override bool visit(TypeId ty, const TypeFunctionInstanceType& it) override
{ {
// TypeOnceVisitor performs a depth-first traversal in the absence of // TypeOnceVisitor performs a depth-first traversal in the absence of
// cycles. This means that by pushing to the front of the queue, we will // cycles. This means that by pushing to the front of the queue, we will
@ -41,7 +41,7 @@ struct InstanceCollector2 : TypeOnceVisitor
{ {
/// Detected cyclic type pack /// Detected cyclic type pack
TypeId t = follow(ty); TypeId t = follow(ty);
if (get<TypeFamilyInstanceType>(t)) if (get<TypeFunctionInstanceType>(t))
cyclicInstance.insert(t); cyclicInstance.insert(t);
} }
@ -50,7 +50,7 @@ struct InstanceCollector2 : TypeOnceVisitor
return false; return false;
} }
bool visit(TypePackId tp, const TypeFamilyInstanceTypePack&) override bool visit(TypePackId tp, const TypeFunctionInstanceTypePack&) override
{ {
// TypeOnceVisitor performs a depth-first traversal in the absence of // TypeOnceVisitor performs a depth-first traversal in the absence of
// cycles. This means that by pushing to the front of the queue, we will // cycles. This means that by pushing to the front of the queue, we will
@ -65,14 +65,14 @@ struct InstanceCollector2 : TypeOnceVisitor
TypeFamilyReductionGuesser::TypeFamilyReductionGuesser(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, NotNull<Normalizer> normalizer) TypeFunctionReductionGuesser::TypeFunctionReductionGuesser(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtins, NotNull<Normalizer> normalizer)
: arena(arena) : arena(arena)
, builtins(builtins) , builtins(builtins)
, normalizer(normalizer) , normalizer(normalizer)
{ {
} }
bool TypeFamilyReductionGuesser::isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& argsUsed) bool TypeFunctionReductionGuesser::isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& argsUsed)
{ {
bool sameSize = ftv.generics.size() == argsUsed.size(); bool sameSize = ftv.generics.size() == argsUsed.size();
bool allGenericsAppear = true; bool allGenericsAppear = true;
@ -81,7 +81,7 @@ bool TypeFamilyReductionGuesser::isFunctionGenericsSaturated(const FunctionType&
return sameSize && allGenericsAppear; return sameSize && allGenericsAppear;
} }
void TypeFamilyReductionGuesser::dumpGuesses() void TypeFunctionReductionGuesser::dumpGuesses()
{ {
for (auto [tf, t] : familyReducesTo) for (auto [tf, t] : familyReducesTo)
printf("Type family %s ~~> %s\n", toString(tf).c_str(), toString(t).c_str()); printf("Type family %s ~~> %s\n", toString(tf).c_str(), toString(t).c_str());
@ -89,7 +89,7 @@ void TypeFamilyReductionGuesser::dumpGuesses()
printf("Substitute %s for %s\n", toString(t).c_str(), toString(t_).c_str()); printf("Substitute %s for %s\n", toString(t).c_str(), toString(t_).c_str());
} }
std::optional<TypeId> TypeFamilyReductionGuesser::guess(TypeId typ) std::optional<TypeId> TypeFunctionReductionGuesser::guess(TypeId typ)
{ {
std::optional<TypeId> guessedType = guessType(typ); std::optional<TypeId> guessedType = guessType(typ);
@ -97,13 +97,13 @@ std::optional<TypeId> TypeFamilyReductionGuesser::guess(TypeId typ)
return {}; return {};
TypeId guess = follow(*guessedType); TypeId guess = follow(*guessedType);
if (get<TypeFamilyInstanceType>(guess)) if (get<TypeFunctionInstanceType>(guess))
return {}; return {};
return guess; return guess;
} }
std::optional<TypePackId> TypeFamilyReductionGuesser::guess(TypePackId tp) std::optional<TypePackId> TypeFunctionReductionGuesser::guess(TypePackId tp)
{ {
auto [head, tail] = flatten(tp); auto [head, tail] = flatten(tp);
@ -118,7 +118,7 @@ std::optional<TypePackId> TypeFamilyReductionGuesser::guess(TypePackId tp)
return {}; return {};
TypeId guess = follow(*guessedType); TypeId guess = follow(*guessedType);
if (get<TypeFamilyInstanceType>(guess)) if (get<TypeFunctionInstanceType>(guess))
return {}; return {};
guessedHead.push_back(*guessedType); guessedHead.push_back(*guessedType);
@ -127,7 +127,7 @@ std::optional<TypePackId> TypeFamilyReductionGuesser::guess(TypePackId tp)
return arena->addTypePack(TypePack{guessedHead, tail}); return arena->addTypePack(TypePack{guessedHead, tail});
} }
TypeFamilyReductionGuessResult TypeFamilyReductionGuesser::guessTypeFamilyReductionForFunction( TypeFunctionReductionGuessResult TypeFunctionReductionGuesser::guessTypeFunctionReductionForFunctionExpr(
const AstExprFunction& expr, const FunctionType* ftv, TypeId retTy) const AstExprFunction& expr, const FunctionType* ftv, TypeId retTy)
{ {
InstanceCollector2 collector; InstanceCollector2 collector;
@ -136,7 +136,7 @@ TypeFamilyReductionGuessResult TypeFamilyReductionGuesser::guessTypeFamilyReduct
cyclicInstances = std::move(collector.cyclicInstance); cyclicInstances = std::move(collector.cyclicInstance);
if (isFunctionGenericsSaturated(*ftv, collector.instanceArguments)) if (isFunctionGenericsSaturated(*ftv, collector.instanceArguments))
return TypeFamilyReductionGuessResult{{}, nullptr, false}; return TypeFunctionReductionGuessResult{{}, nullptr, false};
infer(); infer();
std::vector<std::pair<std::string, TypeId>> results; std::vector<std::pair<std::string, TypeId>> results;
@ -157,7 +157,7 @@ TypeFamilyReductionGuessResult TypeFamilyReductionGuesser::guessTypeFamilyReduct
if (!guessedType.has_value()) if (!guessedType.has_value())
continue; continue;
TypeId guess = follow(*guessedType); TypeId guess = follow(*guessedType);
if (get<TypeFamilyInstanceType>(guess)) if (get<TypeFunctionInstanceType>(guess))
continue; continue;
results.push_back({local->name.value, guess}); results.push_back({local->name.value, guess});
@ -170,7 +170,7 @@ TypeFamilyReductionGuessResult TypeFamilyReductionGuesser::guessTypeFamilyReduct
recommendedAnnotation = builtins->unknownType; recommendedAnnotation = builtins->unknownType;
else else
recommendedAnnotation = follow(*guessedReturnType); recommendedAnnotation = follow(*guessedReturnType);
if (auto t = get<TypeFamilyInstanceType>(recommendedAnnotation)) if (auto t = get<TypeFunctionInstanceType>(recommendedAnnotation))
recommendedAnnotation = builtins->unknownType; recommendedAnnotation = builtins->unknownType;
toInfer.clear(); toInfer.clear();
@ -178,10 +178,10 @@ TypeFamilyReductionGuessResult TypeFamilyReductionGuesser::guessTypeFamilyReduct
familyReducesTo.clear(); familyReducesTo.clear();
substitutable.clear(); substitutable.clear();
return TypeFamilyReductionGuessResult{results, recommendedAnnotation}; return TypeFunctionReductionGuessResult{results, recommendedAnnotation};
} }
std::optional<TypeId> TypeFamilyReductionGuesser::guessType(TypeId arg) std::optional<TypeId> TypeFunctionReductionGuesser::guessType(TypeId arg)
{ {
TypeId t = follow(arg); TypeId t = follow(arg);
if (substitutable.contains(t)) if (substitutable.contains(t))
@ -189,12 +189,12 @@ std::optional<TypeId> TypeFamilyReductionGuesser::guessType(TypeId arg)
TypeId subst = follow(substitutable[t]); TypeId subst = follow(substitutable[t]);
if (subst == t || substitutable.contains(subst)) if (subst == t || substitutable.contains(subst))
return subst; return subst;
else if (!get<TypeFamilyInstanceType>(subst)) else if (!get<TypeFunctionInstanceType>(subst))
return subst; return subst;
else else
return guessType(subst); return guessType(subst);
} }
if (get<TypeFamilyInstanceType>(t)) if (get<TypeFunctionInstanceType>(t))
{ {
if (familyReducesTo.contains(t)) if (familyReducesTo.contains(t))
return familyReducesTo[t]; return familyReducesTo[t];
@ -202,41 +202,41 @@ std::optional<TypeId> TypeFamilyReductionGuesser::guessType(TypeId arg)
return {}; return {};
} }
bool TypeFamilyReductionGuesser::isNumericBinopFamily(const TypeFamilyInstanceType& instance) bool TypeFunctionReductionGuesser::isNumericBinopFamily(const TypeFunctionInstanceType& instance)
{ {
return instance.family->name == "add" || instance.family->name == "sub" || instance.family->name == "mul" || instance.family->name == "div" || return instance.family->name == "add" || instance.family->name == "sub" || instance.family->name == "mul" || instance.family->name == "div" ||
instance.family->name == "idiv" || instance.family->name == "pow" || instance.family->name == "mod"; instance.family->name == "idiv" || instance.family->name == "pow" || instance.family->name == "mod";
} }
bool TypeFamilyReductionGuesser::isComparisonFamily(const TypeFamilyInstanceType& instance) bool TypeFunctionReductionGuesser::isComparisonFamily(const TypeFunctionInstanceType& instance)
{ {
return instance.family->name == "lt" || instance.family->name == "le" || instance.family->name == "eq"; return instance.family->name == "lt" || instance.family->name == "le" || instance.family->name == "eq";
} }
bool TypeFamilyReductionGuesser::isOrAndFamily(const TypeFamilyInstanceType& instance) bool TypeFunctionReductionGuesser::isOrAndFamily(const TypeFunctionInstanceType& instance)
{ {
return instance.family->name == "or" || instance.family->name == "and"; return instance.family->name == "or" || instance.family->name == "and";
} }
bool TypeFamilyReductionGuesser::isNotFamily(const TypeFamilyInstanceType& instance) bool TypeFunctionReductionGuesser::isNotFamily(const TypeFunctionInstanceType& instance)
{ {
return instance.family->name == "not"; return instance.family->name == "not";
} }
bool TypeFamilyReductionGuesser::isLenFamily(const TypeFamilyInstanceType& instance) bool TypeFunctionReductionGuesser::isLenFamily(const TypeFunctionInstanceType& instance)
{ {
return instance.family->name == "len"; return instance.family->name == "len";
} }
bool TypeFamilyReductionGuesser::isUnaryMinus(const TypeFamilyInstanceType& instance) bool TypeFunctionReductionGuesser::isUnaryMinus(const TypeFunctionInstanceType& instance)
{ {
return instance.family->name == "unm"; return instance.family->name == "unm";
} }
// Operand is assignable if it looks like a cyclic family instance, or a generic type // Operand is assignable if it looks like a cyclic family instance, or a generic type
bool TypeFamilyReductionGuesser::operandIsAssignable(TypeId ty) bool TypeFunctionReductionGuesser::operandIsAssignable(TypeId ty)
{ {
if (get<TypeFamilyInstanceType>(ty)) if (get<TypeFunctionInstanceType>(ty))
return true; return true;
if (get<GenericType>(ty)) if (get<GenericType>(ty))
return true; return true;
@ -245,17 +245,17 @@ bool TypeFamilyReductionGuesser::operandIsAssignable(TypeId ty)
return false; return false;
} }
std::shared_ptr<const NormalizedType> TypeFamilyReductionGuesser::normalize(TypeId ty) std::shared_ptr<const NormalizedType> TypeFunctionReductionGuesser::normalize(TypeId ty)
{ {
return normalizer->normalize(ty); return normalizer->normalize(ty);
} }
std::optional<TypeId> TypeFamilyReductionGuesser::tryAssignOperandType(TypeId ty) std::optional<TypeId> TypeFunctionReductionGuesser::tryAssignOperandType(TypeId ty)
{ {
// Because we collect innermost instances first, if we see a typefamily instance as an operand, // Because we collect innermost instances first, if we see a type function instance as an operand,
// We try to check if we guessed a type for it // We try to check if we guessed a type for it
if (auto tfit = get<TypeFamilyInstanceType>(ty)) if (auto tfit = get<TypeFunctionInstanceType>(ty))
{ {
if (familyReducesTo.contains(ty)) if (familyReducesTo.contains(ty))
return {familyReducesTo[ty]}; return {familyReducesTo[ty]};
@ -272,30 +272,30 @@ std::optional<TypeId> TypeFamilyReductionGuesser::tryAssignOperandType(TypeId ty
return {}; return {};
} }
void TypeFamilyReductionGuesser::step() void TypeFunctionReductionGuesser::step()
{ {
TypeId t = toInfer.front(); TypeId t = toInfer.front();
toInfer.pop_front(); toInfer.pop_front();
t = follow(t); t = follow(t);
if (auto tf = get<TypeFamilyInstanceType>(t)) if (auto tf = get<TypeFunctionInstanceType>(t))
inferTypeFamilySubstitutions(t, tf); inferTypeFunctionSubstitutions(t, tf);
} }
void TypeFamilyReductionGuesser::infer() void TypeFunctionReductionGuesser::infer()
{ {
while (!done()) while (!done())
step(); step();
} }
bool TypeFamilyReductionGuesser::done() bool TypeFunctionReductionGuesser::done()
{ {
return toInfer.empty(); return toInfer.empty();
} }
void TypeFamilyReductionGuesser::inferTypeFamilySubstitutions(TypeId ty, const TypeFamilyInstanceType* instance) void TypeFunctionReductionGuesser::inferTypeFunctionSubstitutions(TypeId ty, const TypeFunctionInstanceType* instance)
{ {
TypeFamilyInferenceResult result; TypeFunctionInferenceResult result;
LUAU_ASSERT(instance); LUAU_ASSERT(instance);
// TODO: Make an inexhaustive version of this warn in the compiler? // TODO: Make an inexhaustive version of this warn in the compiler?
if (isNumericBinopFamily(*instance)) if (isNumericBinopFamily(*instance))
@ -323,7 +323,7 @@ void TypeFamilyReductionGuesser::inferTypeFamilySubstitutions(TypeId ty, const T
{ {
TypeId arg = follow(instance->typeArguments[i]); TypeId arg = follow(instance->typeArguments[i]);
TypeId inference = follow(result.operandInference[i]); TypeId inference = follow(result.operandInference[i]);
if (auto tfit = get<TypeFamilyInstanceType>(arg)) if (auto tfit = get<TypeFunctionInstanceType>(arg))
{ {
if (!familyReducesTo.contains(arg)) if (!familyReducesTo.contains(arg))
familyReducesTo.try_insert(arg, inference); familyReducesTo.try_insert(arg, inference);
@ -334,14 +334,14 @@ void TypeFamilyReductionGuesser::inferTypeFamilySubstitutions(TypeId ty, const T
} }
} }
TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferNumericBinopFamily(const TypeFamilyInstanceType* instance) TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNumericBinopFamily(const TypeFunctionInstanceType* instance)
{ {
LUAU_ASSERT(instance->typeArguments.size() == 2); LUAU_ASSERT(instance->typeArguments.size() == 2);
TypeFamilyInferenceResult defaultNumericBinopInference{{builtins->numberType, builtins->numberType}, builtins->numberType}; TypeFunctionInferenceResult defaultNumericBinopInference{{builtins->numberType, builtins->numberType}, builtins->numberType};
return defaultNumericBinopInference; return defaultNumericBinopInference;
} }
TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferComparisonFamily(const TypeFamilyInstanceType* instance) TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferComparisonFamily(const TypeFunctionInstanceType* instance)
{ {
LUAU_ASSERT(instance->typeArguments.size() == 2); LUAU_ASSERT(instance->typeArguments.size() == 2);
// Comparison families are lt/le/eq. // Comparison families are lt/le/eq.
@ -350,8 +350,8 @@ TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferComparisonFamily(cons
TypeId lhsTy = follow(instance->typeArguments[0]); TypeId lhsTy = follow(instance->typeArguments[0]);
TypeId rhsTy = follow(instance->typeArguments[1]); TypeId rhsTy = follow(instance->typeArguments[1]);
auto comparisonInference = [&](TypeId op) -> TypeFamilyInferenceResult { auto comparisonInference = [&](TypeId op) -> TypeFunctionInferenceResult {
return TypeFamilyInferenceResult{{op, op}, builtins->booleanType}; return TypeFunctionInferenceResult{{op, op}, builtins->booleanType};
}; };
if (std::optional<TypeId> ty = tryAssignOperandType(lhsTy)) if (std::optional<TypeId> ty = tryAssignOperandType(lhsTy))
@ -365,7 +365,7 @@ TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferComparisonFamily(cons
return comparisonInference(builtins->numberType); return comparisonInference(builtins->numberType);
} }
TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferOrAndFamily(const TypeFamilyInstanceType* instance) TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const TypeFunctionInstanceType* instance)
{ {
LUAU_ASSERT(instance->typeArguments.size() == 2); LUAU_ASSERT(instance->typeArguments.size() == 2);
@ -377,7 +377,7 @@ TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferOrAndFamily(const Typ
lhsTy = follow(*ty); lhsTy = follow(*ty);
if (std::optional<TypeId> ty = tryAssignOperandType(rhsTy)) if (std::optional<TypeId> ty = tryAssignOperandType(rhsTy))
rhsTy = follow(*ty); rhsTy = follow(*ty);
TypeFamilyInferenceResult defaultAndOrInference{{builtins->unknownType, builtins->unknownType}, builtins->booleanType}; TypeFunctionInferenceResult defaultAndOrInference{{builtins->unknownType, builtins->unknownType}, builtins->booleanType};
std::shared_ptr<const NormalizedType> lty = normalize(lhsTy); std::shared_ptr<const NormalizedType> lty = normalize(lhsTy);
std::shared_ptr<const NormalizedType> rty = normalize(lhsTy); std::shared_ptr<const NormalizedType> rty = normalize(lhsTy);
@ -389,9 +389,9 @@ TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferOrAndFamily(const Typ
if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy)) if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy))
return defaultAndOrInference; return defaultAndOrInference;
if (operandIsAssignable(lhsTy)) if (operandIsAssignable(lhsTy))
return TypeFamilyInferenceResult{{builtins->unknownType, rhsTy}, rhsTy}; return TypeFunctionInferenceResult{{builtins->unknownType, rhsTy}, rhsTy};
if (operandIsAssignable(rhsTy)) if (operandIsAssignable(rhsTy))
return TypeFamilyInferenceResult{{lhsTy, builtins->unknownType}, lhsTy}; return TypeFunctionInferenceResult{{lhsTy, builtins->unknownType}, lhsTy};
if (lhsTruthy) if (lhsTruthy)
return {{lhsTy, rhsTy}, lhsTy}; return {{lhsTy, rhsTy}, lhsTy};
if (rhsTruthy) if (rhsTruthy)
@ -404,9 +404,9 @@ TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferOrAndFamily(const Typ
if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy)) if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy))
return defaultAndOrInference; return defaultAndOrInference;
if (operandIsAssignable(lhsTy)) if (operandIsAssignable(lhsTy))
return TypeFamilyInferenceResult{{}, rhsTy}; return TypeFunctionInferenceResult{{}, rhsTy};
if (operandIsAssignable(rhsTy)) if (operandIsAssignable(rhsTy))
return TypeFamilyInferenceResult{{}, lhsTy}; return TypeFunctionInferenceResult{{}, lhsTy};
if (lhsTruthy) if (lhsTruthy)
return {{lhsTy, rhsTy}, rhsTy}; return {{lhsTy, rhsTy}, rhsTy};
else else
@ -416,7 +416,7 @@ TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferOrAndFamily(const Typ
return defaultAndOrInference; return defaultAndOrInference;
} }
TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferNotFamily(const TypeFamilyInstanceType* instance) TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNotFamily(const TypeFunctionInstanceType* instance)
{ {
LUAU_ASSERT(instance->typeArguments.size() == 1); LUAU_ASSERT(instance->typeArguments.size() == 1);
TypeId opTy = follow(instance->typeArguments[0]); TypeId opTy = follow(instance->typeArguments[0]);
@ -425,7 +425,7 @@ TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferNotFamily(const TypeF
return {{opTy}, builtins->booleanType}; return {{opTy}, builtins->booleanType};
} }
TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferLenFamily(const TypeFamilyInstanceType* instance) TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferLenFamily(const TypeFunctionInstanceType* instance)
{ {
LUAU_ASSERT(instance->typeArguments.size() == 1); LUAU_ASSERT(instance->typeArguments.size() == 1);
TypeId opTy = follow(instance->typeArguments[0]); TypeId opTy = follow(instance->typeArguments[0]);
@ -434,7 +434,7 @@ TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferLenFamily(const TypeF
return {{opTy}, builtins->numberType}; return {{opTy}, builtins->numberType};
} }
TypeFamilyInferenceResult TypeFamilyReductionGuesser::inferUnaryMinusFamily(const TypeFamilyInstanceType* instance) TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferUnaryMinusFamily(const TypeFunctionInstanceType* instance)
{ {
LUAU_ASSERT(instance->typeArguments.size() == 1); LUAU_ASSERT(instance->typeArguments.size() == 1);
TypeId opTy = follow(instance->typeArguments[0]); TypeId opTy = follow(instance->typeArguments[0]);

View File

@ -454,11 +454,11 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
else if (isBlocked(log, superTy)) else if (isBlocked(log, superTy))
blockedTypes.push_back(superTy); blockedTypes.push_back(superTy);
if (log.get<TypeFamilyInstanceType>(superTy)) if (log.get<TypeFunctionInstanceType>(superTy))
ice("Unexpected TypeFamilyInstanceType superTy"); ice("Unexpected TypeFunctionInstanceType superTy");
if (log.get<TypeFamilyInstanceType>(subTy)) if (log.get<TypeFunctionInstanceType>(subTy))
ice("Unexpected TypeFamilyInstanceType subTy"); ice("Unexpected TypeFunctionInstanceType subTy");
auto superFree = log.getMutable<FreeType>(superTy); auto superFree = log.getMutable<FreeType>(superTy);
auto subFree = log.getMutable<FreeType>(subTy); auto subFree = log.getMutable<FreeType>(subTy);

View File

@ -8,7 +8,7 @@
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypeArena.h" #include "Luau/TypeArena.h"
#include "Luau/TypeCheckLimits.h" #include "Luau/TypeCheckLimits.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/TypeFwd.h" #include "Luau/TypeFwd.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
@ -76,13 +76,13 @@ static bool areCompatible(TypeId left, TypeId right)
// returns `true` if `ty` is irressolvable and should be added to `incompleteSubtypes`. // returns `true` if `ty` is irressolvable and should be added to `incompleteSubtypes`.
static bool isIrresolvable(TypeId ty) static bool isIrresolvable(TypeId ty)
{ {
return get<BlockedType>(ty) || get<TypeFamilyInstanceType>(ty); return get<BlockedType>(ty) || get<TypeFunctionInstanceType>(ty);
} }
// returns `true` if `tp` is irressolvable and should be added to `incompleteSubtypes`. // returns `true` if `tp` is irressolvable and should be added to `incompleteSubtypes`.
static bool isIrresolvable(TypePackId tp) static bool isIrresolvable(TypePackId tp)
{ {
return get<BlockedTypePack>(tp) || get<TypeFamilyInstanceTypePack>(tp); return get<BlockedTypePack>(tp) || get<TypeFunctionInstanceTypePack>(tp);
} }
Unifier2::Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice) Unifier2::Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice)

View File

@ -13,8 +13,6 @@
#include <stddef.h> #include <stddef.h>
LUAU_FASTFLAGVARIABLE(LuauCodegenInstG, false)
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -54,9 +52,7 @@ void updateUseCounts(IrFunction& function)
checkOp(inst.d); checkOp(inst.d);
checkOp(inst.e); checkOp(inst.e);
checkOp(inst.f); checkOp(inst.f);
checkOp(inst.g);
if (FFlag::LuauCodegenInstG)
checkOp(inst.g);
} }
} }
@ -100,9 +96,7 @@ void updateLastUseLocations(IrFunction& function, const std::vector<uint32_t>& s
checkOp(inst.d); checkOp(inst.d);
checkOp(inst.e); checkOp(inst.e);
checkOp(inst.f); checkOp(inst.f);
checkOp(inst.g);
if (FFlag::LuauCodegenInstG)
checkOp(inst.g);
} }
} }
} }
@ -137,11 +131,8 @@ uint32_t getNextInstUse(IrFunction& function, uint32_t targetInstIdx, uint32_t s
if (inst.f.kind == IrOpKind::Inst && inst.f.index == targetInstIdx) if (inst.f.kind == IrOpKind::Inst && inst.f.index == targetInstIdx)
return i; return i;
if (FFlag::LuauCodegenInstG) if (inst.g.kind == IrOpKind::Inst && inst.g.index == targetInstIdx)
{ return i;
if (inst.g.kind == IrOpKind::Inst && inst.g.index == targetInstIdx)
return i;
}
} }
// There must be a next use since there is the last use location // There must be a next use since there is the last use location
@ -179,9 +170,7 @@ std::pair<uint32_t, uint32_t> getLiveInOutValueCount(IrFunction& function, IrBlo
checkOp(inst.d); checkOp(inst.d);
checkOp(inst.e); checkOp(inst.e);
checkOp(inst.f); checkOp(inst.f);
checkOp(inst.g);
if (FFlag::LuauCodegenInstG)
checkOp(inst.g);
} }
return std::make_pair(liveIns, liveOuts); return std::make_pair(liveIns, liveOuts);
@ -505,9 +494,7 @@ static void computeCfgBlockEdges(IrFunction& function)
checkOp(inst.d); checkOp(inst.d);
checkOp(inst.e); checkOp(inst.e);
checkOp(inst.f); checkOp(inst.f);
checkOp(inst.g);
if (FFlag::LuauCodegenInstG)
checkOp(inst.g);
} }
} }

View File

@ -13,7 +13,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAG(LuauCodegenInstG)
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
namespace Luau namespace Luau
@ -641,9 +640,7 @@ void IrBuilder::clone(const IrBlock& source, bool removeCurrentTerminator)
redirect(clone.d); redirect(clone.d);
redirect(clone.e); redirect(clone.e);
redirect(clone.f); redirect(clone.f);
redirect(clone.g);
if (FFlag::LuauCodegenInstG)
redirect(clone.g);
addUse(function, clone.a); addUse(function, clone.a);
addUse(function, clone.b); addUse(function, clone.b);
@ -651,18 +648,13 @@ void IrBuilder::clone(const IrBlock& source, bool removeCurrentTerminator)
addUse(function, clone.d); addUse(function, clone.d);
addUse(function, clone.e); addUse(function, clone.e);
addUse(function, clone.f); addUse(function, clone.f);
addUse(function, clone.g);
if (FFlag::LuauCodegenInstG)
addUse(function, clone.g);
// Instructions that referenced the original will have to be adjusted to use the clone // Instructions that referenced the original will have to be adjusted to use the clone
instRedir[index] = uint32_t(function.instructions.size()); instRedir[index] = uint32_t(function.instructions.size());
// Reconstruct the fresh clone // Reconstruct the fresh clone
if (FFlag::LuauCodegenInstG) inst(clone.cmd, clone.a, clone.b, clone.c, clone.d, clone.e, clone.f, clone.g);
inst(clone.cmd, clone.a, clone.b, clone.c, clone.d, clone.e, clone.f, clone.g);
else
inst(clone.cmd, clone.a, clone.b, clone.c, clone.d, clone.e, clone.f);
} }
} }
@ -760,31 +752,11 @@ IrOp IrBuilder::inst(IrCmd cmd, IrOp a, IrOp b, IrOp c, IrOp d, IrOp e)
IrOp IrBuilder::inst(IrCmd cmd, IrOp a, IrOp b, IrOp c, IrOp d, IrOp e, IrOp f) IrOp IrBuilder::inst(IrCmd cmd, IrOp a, IrOp b, IrOp c, IrOp d, IrOp e, IrOp f)
{ {
if (FFlag::LuauCodegenInstG) return inst(cmd, a, b, c, d, e, f, {});
{
return inst(cmd, a, b, c, d, e, f, {});
}
else
{
uint32_t index = uint32_t(function.instructions.size());
function.instructions.push_back({cmd, a, b, c, d, e, f});
CODEGEN_ASSERT(!inTerminatedBlock);
if (isBlockTerminator(cmd))
{
function.blocks[activeBlockIdx].finish = index;
inTerminatedBlock = true;
}
return {IrOpKind::Inst, index};
}
} }
IrOp IrBuilder::inst(IrCmd cmd, IrOp a, IrOp b, IrOp c, IrOp d, IrOp e, IrOp f, IrOp g) IrOp IrBuilder::inst(IrCmd cmd, IrOp a, IrOp b, IrOp c, IrOp d, IrOp e, IrOp f, IrOp g)
{ {
CODEGEN_ASSERT(FFlag::LuauCodegenInstG);
uint32_t index = uint32_t(function.instructions.size()); uint32_t index = uint32_t(function.instructions.size());
function.instructions.push_back({cmd, a, b, c, d, e, f, g}); function.instructions.push_back({cmd, a, b, c, d, e, f, g});

View File

@ -7,8 +7,6 @@
#include <stdarg.h> #include <stdarg.h>
LUAU_FASTFLAG(LuauCodegenInstG)
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -419,9 +417,7 @@ void toString(IrToStringContext& ctx, const IrInst& inst, uint32_t index)
checkOp(inst.d, ", "); checkOp(inst.d, ", ");
checkOp(inst.e, ", "); checkOp(inst.e, ", ");
checkOp(inst.f, ", "); checkOp(inst.f, ", ");
checkOp(inst.g, ", ");
if (FFlag::LuauCodegenInstG)
checkOp(inst.g, ", ");
} }
void toString(IrToStringContext& ctx, const IrBlock& block, uint32_t index) void toString(IrToStringContext& ctx, const IrBlock& block, uint32_t index)
@ -611,7 +607,7 @@ static RegisterSet getJumpTargetExtraLiveIn(IrToStringContext& ctx, const IrBloc
op = inst.e; op = inst.e;
else if (inst.f.kind == IrOpKind::Block) else if (inst.f.kind == IrOpKind::Block)
op = inst.f; op = inst.f;
else if (FFlag::LuauCodegenInstG && inst.g.kind == IrOpKind::Block) else if (inst.g.kind == IrOpKind::Block)
op = inst.g; op = inst.g;
if (op.kind == IrOpKind::Block && op.index < ctx.cfg.in.size()) if (op.kind == IrOpKind::Block && op.index < ctx.cfg.in.size())
@ -897,9 +893,7 @@ std::string toDot(const IrFunction& function, bool includeInst)
checkOp(inst.d); checkOp(inst.d);
checkOp(inst.e); checkOp(inst.e);
checkOp(inst.f); checkOp(inst.f);
checkOp(inst.g);
if (FFlag::LuauCodegenInstG)
checkOp(inst.g);
} }
} }

View File

@ -11,7 +11,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(DebugCodegenChaosA64, false) LUAU_FASTFLAGVARIABLE(DebugCodegenChaosA64, false)
LUAU_FASTFLAG(LuauCodegenInstG)
namespace Luau namespace Luau
{ {
@ -257,9 +256,7 @@ void IrRegAllocA64::freeLastUseRegs(const IrInst& inst, uint32_t index)
checkOp(inst.d); checkOp(inst.d);
checkOp(inst.e); checkOp(inst.e);
checkOp(inst.f); checkOp(inst.f);
checkOp(inst.g);
if (FFlag::LuauCodegenInstG)
checkOp(inst.g);
} }
void IrRegAllocA64::freeTempRegs() void IrRegAllocA64::freeTempRegs()

View File

@ -6,8 +6,6 @@
#include "EmitCommonX64.h" #include "EmitCommonX64.h"
LUAU_FASTFLAG(LuauCodegenInstG)
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -183,9 +181,7 @@ void IrRegAllocX64::freeLastUseRegs(const IrInst& inst, uint32_t instIdx)
checkOp(inst.d); checkOp(inst.d);
checkOp(inst.e); checkOp(inst.e);
checkOp(inst.f); checkOp(inst.f);
checkOp(inst.g);
if (FFlag::LuauCodegenInstG)
checkOp(inst.g);
} }
bool IrRegAllocX64::isLastUseReg(const IrInst& target, uint32_t instIdx) const bool IrRegAllocX64::isLastUseReg(const IrInst& target, uint32_t instIdx) const

View File

@ -12,8 +12,6 @@
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
LUAU_FASTFLAG(LuauCodegenInstG)
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -317,9 +315,7 @@ void kill(IrFunction& function, IrInst& inst)
removeUse(function, inst.d); removeUse(function, inst.d);
removeUse(function, inst.e); removeUse(function, inst.e);
removeUse(function, inst.f); removeUse(function, inst.f);
removeUse(function, inst.g);
if (FFlag::LuauCodegenInstG)
removeUse(function, inst.g);
inst.a = {}; inst.a = {};
inst.b = {}; inst.b = {};
@ -327,9 +323,7 @@ void kill(IrFunction& function, IrInst& inst)
inst.d = {}; inst.d = {};
inst.e = {}; inst.e = {};
inst.f = {}; inst.f = {};
inst.g = {};
if (FFlag::LuauCodegenInstG)
inst.g = {};
} }
void kill(IrFunction& function, uint32_t start, uint32_t end) void kill(IrFunction& function, uint32_t start, uint32_t end)
@ -378,9 +372,7 @@ void replace(IrFunction& function, IrBlock& block, uint32_t instIdx, IrInst repl
addUse(function, replacement.d); addUse(function, replacement.d);
addUse(function, replacement.e); addUse(function, replacement.e);
addUse(function, replacement.f); addUse(function, replacement.f);
addUse(function, replacement.g);
if (FFlag::LuauCodegenInstG)
addUse(function, replacement.g);
// An extra reference is added so block will not remove itself // An extra reference is added so block will not remove itself
block.useCount++; block.useCount++;
@ -403,9 +395,7 @@ void replace(IrFunction& function, IrBlock& block, uint32_t instIdx, IrInst repl
removeUse(function, inst.d); removeUse(function, inst.d);
removeUse(function, inst.e); removeUse(function, inst.e);
removeUse(function, inst.f); removeUse(function, inst.f);
removeUse(function, inst.g);
if (FFlag::LuauCodegenInstG)
removeUse(function, inst.g);
// Inherit existing use count (last use is skipped as it will be defined later) // Inherit existing use count (last use is skipped as it will be defined later)
replacement.useCount = inst.useCount; replacement.useCount = inst.useCount;
@ -431,9 +421,7 @@ void substitute(IrFunction& function, IrInst& inst, IrOp replacement)
removeUse(function, inst.d); removeUse(function, inst.d);
removeUse(function, inst.e); removeUse(function, inst.e);
removeUse(function, inst.f); removeUse(function, inst.f);
removeUse(function, inst.g);
if (FFlag::LuauCodegenInstG)
removeUse(function, inst.g);
inst.a = replacement; inst.a = replacement;
inst.b = {}; inst.b = {};
@ -441,9 +429,7 @@ void substitute(IrFunction& function, IrInst& inst, IrOp replacement)
inst.d = {}; inst.d = {};
inst.e = {}; inst.e = {};
inst.f = {}; inst.f = {};
inst.g = {};
if (FFlag::LuauCodegenInstG)
inst.g = {};
} }
void applySubstitutions(IrFunction& function, IrOp& op) void applySubstitutions(IrFunction& function, IrOp& op)
@ -487,9 +473,7 @@ void applySubstitutions(IrFunction& function, IrInst& inst)
applySubstitutions(function, inst.d); applySubstitutions(function, inst.d);
applySubstitutions(function, inst.e); applySubstitutions(function, inst.e);
applySubstitutions(function, inst.f); applySubstitutions(function, inst.f);
applySubstitutions(function, inst.g);
if (FFlag::LuauCodegenInstG)
applySubstitutions(function, inst.g);
} }
bool compare(double a, double b, IrCondition cond) bool compare(double a, double b, IrCondition cond)

View File

@ -219,8 +219,8 @@ target_sources(Luau.Analysis PRIVATE
Analysis/include/Luau/TypeChecker2.h Analysis/include/Luau/TypeChecker2.h
Analysis/include/Luau/TypeCheckLimits.h Analysis/include/Luau/TypeCheckLimits.h
Analysis/include/Luau/TypedAllocator.h Analysis/include/Luau/TypedAllocator.h
Analysis/include/Luau/TypeFamily.h Analysis/include/Luau/TypeFunction.h
Analysis/include/Luau/TypeFamilyReductionGuesser.h Analysis/include/Luau/TypeFunctionReductionGuesser.h
Analysis/include/Luau/TypeFwd.h Analysis/include/Luau/TypeFwd.h
Analysis/include/Luau/TypeInfer.h Analysis/include/Luau/TypeInfer.h
Analysis/include/Luau/TypeOrPack.h Analysis/include/Luau/TypeOrPack.h
@ -283,8 +283,8 @@ target_sources(Luau.Analysis PRIVATE
Analysis/src/TypeAttach.cpp Analysis/src/TypeAttach.cpp
Analysis/src/TypeChecker2.cpp Analysis/src/TypeChecker2.cpp
Analysis/src/TypedAllocator.cpp Analysis/src/TypedAllocator.cpp
Analysis/src/TypeFamily.cpp Analysis/src/TypeFunction.cpp
Analysis/src/TypeFamilyReductionGuesser.cpp Analysis/src/TypeFunctionReductionGuesser.cpp
Analysis/src/TypeInfer.cpp Analysis/src/TypeInfer.cpp
Analysis/src/TypeOrPack.cpp Analysis/src/TypeOrPack.cpp
Analysis/src/TypePack.cpp Analysis/src/TypePack.cpp
@ -454,7 +454,7 @@ if(TARGET Luau.UnitTest)
tests/ToString.test.cpp tests/ToString.test.cpp
tests/Transpiler.test.cpp tests/Transpiler.test.cpp
tests/TxnLog.test.cpp tests/TxnLog.test.cpp
tests/TypeFamily.test.cpp tests/TypeFunction.test.cpp
tests/TypeInfer.aliases.test.cpp tests/TypeInfer.aliases.test.cpp
tests/TypeInfer.annotations.test.cpp tests/TypeInfer.annotations.test.cpp
tests/TypeInfer.anyerror.test.cpp tests/TypeInfer.anyerror.test.cpp

View File

@ -13,7 +13,6 @@
#include <limits.h> #include <limits.h>
LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTFLAG(DebugLuauAbortingChecks)
LUAU_FASTFLAG(LuauCodegenInstG)
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
LUAU_FASTFLAG(LuauCodegenMathSign) LUAU_FASTFLAG(LuauCodegenMathSign)
@ -1123,8 +1122,6 @@ bb_0:
TEST_CASE_FIXTURE(IrBuilderFixture, "BuiltinFastcallsMayInvalidateMemory") TEST_CASE_FIXTURE(IrBuilderFixture, "BuiltinFastcallsMayInvalidateMemory")
{ {
ScopedFastFlag luauCodegenInstG{FFlag::LuauCodegenInstG, true};
IrOp block = build.block(IrBlockKind::Internal); IrOp block = build.block(IrBlockKind::Internal);
IrOp fallback = build.block(IrBlockKind::Fallback); IrOp fallback = build.block(IrBlockKind::Fallback);
@ -2814,7 +2811,6 @@ bb_1:
TEST_CASE_FIXTURE(IrBuilderFixture, "ExplicitUseOfRegisterInVarargSequence") TEST_CASE_FIXTURE(IrBuilderFixture, "ExplicitUseOfRegisterInVarargSequence")
{ {
ScopedFastFlag luauCodegenInstG{FFlag::LuauCodegenInstG, true};
ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true};
IrOp entry = build.block(IrBlockKind::Internal); IrOp entry = build.block(IrBlockKind::Internal);

View File

@ -7,7 +7,7 @@
#include "Luau/Subtyping.h" #include "Luau/Subtyping.h"
#include "Luau/Type.h" #include "Luau/Type.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "doctest.h" #include "doctest.h"
#include "Fixture.h" #include "Fixture.h"
@ -73,7 +73,7 @@ struct SubtypeFixture : Fixture
ScopePtr moduleScope{new Scope(rootScope)}; ScopePtr moduleScope{new Scope(rootScope)};
Subtyping subtyping = mkSubtyping(rootScope); Subtyping subtyping = mkSubtyping(rootScope);
BuiltinTypeFamilies builtinTypeFamilies{}; BuiltinTypeFunctions builtinTypeFunctions{};
Subtyping mkSubtyping(const ScopePtr& scope) Subtyping mkSubtyping(const ScopePtr& scope)
{ {
@ -398,51 +398,51 @@ TEST_SUITE_BEGIN("Subtyping");
TEST_IS_SUBTYPE(builtinTypes->numberType, builtinTypes->anyType); TEST_IS_SUBTYPE(builtinTypes->numberType, builtinTypes->anyType);
TEST_IS_NOT_SUBTYPE(builtinTypes->numberType, builtinTypes->stringType); TEST_IS_NOT_SUBTYPE(builtinTypes->numberType, builtinTypes->stringType);
TEST_CASE_FIXTURE(SubtypeFixture, "basic_reducible_sub_typefamily") TEST_CASE_FIXTURE(SubtypeFixture, "basic_reducible_sub_type_function")
{ {
// add<number, number> <: number // add<number, number> <: number
TypeId typeFamilyNum = TypeId typeFunctionNum =
arena.addType(TypeFamilyInstanceType{NotNull{&builtinTypeFamilies.addFamily}, {builtinTypes->numberType, builtinTypes->numberType}, {}}); arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {builtinTypes->numberType, builtinTypes->numberType}, {}});
TypeId superTy = builtinTypes->numberType; TypeId superTy = builtinTypes->numberType;
SubtypingResult result = isSubtype(typeFamilyNum, superTy); SubtypingResult result = isSubtype(typeFunctionNum, superTy);
CHECK(result.isSubtype); CHECK(result.isSubtype);
} }
TEST_CASE_FIXTURE(SubtypeFixture, "basic_reducible_super_typefamily") TEST_CASE_FIXTURE(SubtypeFixture, "basic_reducible_super_type_function")
{ {
// number <: add<number, number> ~ number // number <: add<number, number> ~ number
TypeId typeFamilyNum = TypeId typeFunctionNum =
arena.addType(TypeFamilyInstanceType{NotNull{&builtinTypeFamilies.addFamily}, {builtinTypes->numberType, builtinTypes->numberType}, {}}); arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {builtinTypes->numberType, builtinTypes->numberType}, {}});
TypeId subTy = builtinTypes->numberType; TypeId subTy = builtinTypes->numberType;
SubtypingResult result = isSubtype(subTy, typeFamilyNum); SubtypingResult result = isSubtype(subTy, typeFunctionNum);
CHECK(result.isSubtype); CHECK(result.isSubtype);
} }
TEST_CASE_FIXTURE(SubtypeFixture, "basic_irreducible_sub_typefamily") TEST_CASE_FIXTURE(SubtypeFixture, "basic_irreducible_sub_type_function")
{ {
// add<string, boolean> ~ never <: number // add<string, boolean> ~ never <: number
TypeId typeFamilyNum = TypeId typeFunctionNum =
arena.addType(TypeFamilyInstanceType{NotNull{&builtinTypeFamilies.addFamily}, {builtinTypes->stringType, builtinTypes->booleanType}, {}}); arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {builtinTypes->stringType, builtinTypes->booleanType}, {}});
TypeId superTy = builtinTypes->numberType; TypeId superTy = builtinTypes->numberType;
SubtypingResult result = isSubtype(typeFamilyNum, superTy); SubtypingResult result = isSubtype(typeFunctionNum, superTy);
CHECK(result.isSubtype); CHECK(result.isSubtype);
} }
TEST_CASE_FIXTURE(SubtypeFixture, "basic_irreducible_super_typefamily") TEST_CASE_FIXTURE(SubtypeFixture, "basic_irreducible_super_type_function")
{ {
// number <\: add<string, boolean> ~ irreducible/never // number <\: add<string, boolean> ~ irreducible/never
TypeId typeFamilyNum = TypeId typeFunctionNum =
arena.addType(TypeFamilyInstanceType{NotNull{&builtinTypeFamilies.addFamily}, {builtinTypes->stringType, builtinTypes->booleanType}, {}}); arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {builtinTypes->stringType, builtinTypes->booleanType}, {}});
TypeId subTy = builtinTypes->numberType; TypeId subTy = builtinTypes->numberType;
SubtypingResult result = isSubtype(subTy, typeFamilyNum); SubtypingResult result = isSubtype(subTy, typeFunctionNum);
CHECK(!result.isSubtype); CHECK(!result.isSubtype);
} }
TEST_CASE_FIXTURE(SubtypeFixture, "basic_typefamily_with_generics") TEST_CASE_FIXTURE(SubtypeFixture, "basic_type_function_with_generics")
{ {
// <T,U>(x: T, x: U) -> add<T,U> <: (number, number) -> number // <T,U>(x: T, x: U) -> add<T,U> <: (number, number) -> number
TypeId addFamily = arena.addType(TypeFamilyInstanceType{NotNull{&builtinTypeFamilies.addFamily}, {genericT, genericU}, {}}); TypeId addTypeFunction = arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {genericT, genericU}, {}});
FunctionType ft{{genericT, genericU}, {}, arena.addTypePack({genericT, genericU}), arena.addTypePack({addFamily})}; FunctionType ft{{genericT, genericU}, {}, arena.addTypePack({genericT, genericU}), arena.addTypePack({addTypeFunction})};
TypeId functionType = arena.addType(std::move(ft)); TypeId functionType = arena.addType(std::move(ft));
FunctionType superFt{arena.addTypePack({builtinTypes->numberType, builtinTypes->numberType}), arena.addTypePack({builtinTypes->numberType})}; FunctionType superFt{arena.addTypePack({builtinTypes->numberType, builtinTypes->numberType}), arena.addTypePack({builtinTypes->numberType})};
TypeId superFunction = arena.addType(std::move(superFt)); TypeId superFunction = arena.addType(std::move(superFt));
@ -1292,7 +1292,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "subtyping_reasonings_to_follow_a_reduced_type
builtinTypes->numberType, builtinTypes->stringType, builtinTypes->tableType, builtinTypes->threadType}}); builtinTypes->numberType, builtinTypes->stringType, builtinTypes->tableType, builtinTypes->threadType}});
TypeId tblTy = tbl({{"depth", builtinTypes->unknownType}}); TypeId tblTy = tbl({{"depth", builtinTypes->unknownType}});
TypeId combined = meet(longTy, tblTy); TypeId combined = meet(longTy, tblTy);
TypeId subTy = arena.addType(TypeFamilyInstanceType{NotNull{&builtinTypeFamilies.unionFamily}, {combined, builtinTypes->neverType}, {}}); TypeId subTy = arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.unionFunc}, {combined, builtinTypes->neverType}, {}});
TypeId superTy = builtinTypes->neverType; TypeId superTy = builtinTypes->neverType;
SubtypingResult result = isSubtype(subTy, superTy); SubtypingResult result = isSubtype(subTy, superTy);
CHECK(!result.isSubtype); CHECK(!result.isSubtype);
@ -1321,9 +1321,10 @@ TEST_CASE_FIXTURE(SubtypeFixture, "table_property")
SubtypingResult result = isSubtype(subTy, superTy); SubtypingResult result = isSubtype(subTy, superTy);
CHECK(!result.isSubtype); CHECK(!result.isSubtype);
CHECK(result.reasoning == std::vector{SubtypingReasoning{/* subPath */ Path(TypePath::Property::read("X")), REQUIRE(result.reasoning.size() == 1);
CHECK(*result.reasoning.begin() == SubtypingReasoning{/* subPath */ Path(TypePath::Property::read("X")),
/* superPath */ Path(TypePath::Property::read("X")), /* superPath */ Path(TypePath::Property::read("X")),
/* variance */ SubtypingVariance::Invariant}}); /* variance */ SubtypingVariance::Invariant});
} }
TEST_CASE_FIXTURE(SubtypeFixture, "table_indexers") TEST_CASE_FIXTURE(SubtypeFixture, "table_indexers")
@ -1394,10 +1395,11 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets")
SubtypingResult result = isSubtype(subTy, superTy); SubtypingResult result = isSubtype(subTy, superTy);
CHECK(!result.isSubtype); CHECK(!result.isSubtype);
CHECK(result.reasoning == std::vector{SubtypingReasoning{ REQUIRE(result.reasoning.size() == 1);
CHECK(*result.reasoning.begin() == SubtypingReasoning{
/* subPath */ TypePath::PathBuilder().rets().index(0).build(), /* subPath */ TypePath::PathBuilder().rets().index(0).build(),
/* superPath */ TypePath::PathBuilder().rets().index(0).build(), /* superPath */ TypePath::PathBuilder().rets().index(0).build(),
}}); });
} }
TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets_tail") TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets_tail")
@ -1407,10 +1409,11 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets_tail")
SubtypingResult result = isSubtype(subTy, superTy); SubtypingResult result = isSubtype(subTy, superTy);
CHECK(!result.isSubtype); CHECK(!result.isSubtype);
CHECK(result.reasoning == std::vector{SubtypingReasoning{ REQUIRE(result.reasoning.size() == 1);
CHECK(*result.reasoning.begin() == SubtypingReasoning{
/* subPath */ TypePath::PathBuilder().rets().tail().variadic().build(), /* subPath */ TypePath::PathBuilder().rets().tail().variadic().build(),
/* superPath */ TypePath::PathBuilder().rets().tail().variadic().build(), /* superPath */ TypePath::PathBuilder().rets().tail().variadic().build(),
}}); });
} }
TEST_CASE_FIXTURE(SubtypeFixture, "nested_table_properties") TEST_CASE_FIXTURE(SubtypeFixture, "nested_table_properties")
@ -1420,11 +1423,12 @@ TEST_CASE_FIXTURE(SubtypeFixture, "nested_table_properties")
SubtypingResult result = isSubtype(subTy, superTy); SubtypingResult result = isSubtype(subTy, superTy);
CHECK(!result.isSubtype); CHECK(!result.isSubtype);
CHECK(result.reasoning == std::vector{SubtypingReasoning{ REQUIRE(result.reasoning.size() == 1);
CHECK(*result.reasoning.begin() == SubtypingReasoning{
/* subPath */ TypePath::PathBuilder().readProp("X").readProp("Y").readProp("Z").build(), /* subPath */ TypePath::PathBuilder().readProp("X").readProp("Y").readProp("Z").build(),
/* superPath */ TypePath::PathBuilder().readProp("X").readProp("Y").readProp("Z").build(), /* superPath */ TypePath::PathBuilder().readProp("X").readProp("Y").readProp("Z").build(),
/* variance */ SubtypingVariance::Invariant, /* variance */ SubtypingVariance::Invariant,
}}); });
} }
TEST_CASE_FIXTURE(SubtypeFixture, "string_table_mt") TEST_CASE_FIXTURE(SubtypeFixture, "string_table_mt")

View File

@ -1,5 +1,5 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeFamily.h" #include "Luau/TypeFunction.h"
#include "Luau/ConstraintSolver.h" #include "Luau/ConstraintSolver.h"
#include "Luau/NotNull.h" #include "Luau/NotNull.h"
@ -17,34 +17,34 @@ LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
struct FamilyFixture : Fixture struct FamilyFixture : Fixture
{ {
TypeFamily swapFamily; TypeFunction swapFamily;
FamilyFixture() FamilyFixture()
: Fixture(true, false) : Fixture(true, false)
{ {
swapFamily = TypeFamily{/* name */ "Swap", swapFamily = TypeFunction{/* name */ "Swap",
/* reducer */ /* reducer */
[](TypeId instance, const std::vector<TypeId>& tys, const std::vector<TypePackId>& tps, [](TypeId instance, const std::vector<TypeId>& tys, const std::vector<TypePackId>& tps,
NotNull<TypeFamilyContext> ctx) -> TypeFamilyReductionResult<TypeId> { NotNull<TypeFunctionContext> ctx) -> TypeFunctionReductionResult<TypeId> {
LUAU_ASSERT(tys.size() == 1); LUAU_ASSERT(tys.size() == 1);
TypeId param = follow(tys.at(0)); TypeId param = follow(tys.at(0));
if (isString(param)) if (isString(param))
{ {
return TypeFamilyReductionResult<TypeId>{ctx->builtins->numberType, false, {}, {}}; return TypeFunctionReductionResult<TypeId>{ctx->builtins->numberType, false, {}, {}};
} }
else if (isNumber(param)) else if (isNumber(param))
{ {
return TypeFamilyReductionResult<TypeId>{ctx->builtins->stringType, false, {}, {}}; return TypeFunctionReductionResult<TypeId>{ctx->builtins->stringType, false, {}, {}};
} }
else if (is<BlockedType>(param) || is<PendingExpansionType>(param) || is<TypeFamilyInstanceType>(param) || else if (is<BlockedType>(param) || is<PendingExpansionType>(param) || is<TypeFunctionInstanceType>(param) ||
(ctx->solver && ctx->solver->hasUnresolvedConstraints(param))) (ctx->solver && ctx->solver->hasUnresolvedConstraints(param)))
{ {
return TypeFamilyReductionResult<TypeId>{std::nullopt, false, {param}, {}}; return TypeFunctionReductionResult<TypeId>{std::nullopt, false, {param}, {}};
} }
else else
{ {
return TypeFamilyReductionResult<TypeId>{std::nullopt, true, {}, {}}; return TypeFunctionReductionResult<TypeId>{std::nullopt, true, {}, {}};
} }
}}; }};
@ -54,12 +54,12 @@ struct FamilyFixture : Fixture
ScopePtr globalScope = frontend.globals.globalScope; ScopePtr globalScope = frontend.globals.globalScope;
globalScope->exportedTypeBindings["Swap"] = globalScope->exportedTypeBindings["Swap"] =
TypeFun{{genericT}, frontend.globals.globalTypes.addType(TypeFamilyInstanceType{NotNull{&swapFamily}, {t}, {}})}; TypeFun{{genericT}, frontend.globals.globalTypes.addType(TypeFunctionInstanceType{NotNull{&swapFamily}, {t}, {}})};
freeze(frontend.globals.globalTypes); freeze(frontend.globals.globalTypes);
} }
}; };
TEST_SUITE_BEGIN("TypeFamilyTests"); TEST_SUITE_BEGIN("TypeFunctionTests");
TEST_CASE_FIXTURE(FamilyFixture, "basic_type_family") TEST_CASE_FIXTURE(FamilyFixture, "basic_type_family")
{ {
@ -80,7 +80,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "basic_type_family")
CHECK("number" == toString(requireTypeAlias("B"))); CHECK("number" == toString(requireTypeAlias("B")));
CHECK("Swap<boolean>" == toString(requireTypeAlias("C"))); CHECK("Swap<boolean>" == toString(requireTypeAlias("C")));
CHECK("string" == toString(requireType("y"))); CHECK("string" == toString(requireType("y")));
CHECK("Type family instance Swap<boolean> is uninhabited" == toString(result.errors[0])); CHECK("Type function instance Swap<boolean> is uninhabited" == toString(result.errors[0]));
}; };
TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_ret") TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_ret")
@ -99,7 +99,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_ret")
CHECK("string" == toString(requireType("a"))); CHECK("string" == toString(requireType("a")));
CHECK("number" == toString(requireType("b"))); CHECK("number" == toString(requireType("b")));
CHECK("Swap<boolean>" == toString(requireType("c"))); CHECK("Swap<boolean>" == toString(requireType("c")));
CHECK("Type family instance Swap<boolean> is uninhabited" == toString(result.errors[0])); CHECK("Type function instance Swap<boolean> is uninhabited" == toString(result.errors[0]));
} }
TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_arg") TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_arg")
@ -117,8 +117,8 @@ TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_arg")
// FIXME: Can we constrain these to `never` or `unknown`? // FIXME: Can we constrain these to `never` or `unknown`?
CHECK("a" == toString(requireType("a"))); CHECK("a" == toString(requireType("a")));
CHECK("a" == toString(requireType("b"))); CHECK("a" == toString(requireType("b")));
CHECK("Type family instance Swap<a> is uninhabited" == toString(result.errors[0])); CHECK("Type function instance Swap<a> is uninhabited" == toString(result.errors[0]));
CHECK("Type family instance Swap<a> is uninhabited" == toString(result.errors[1])); CHECK("Type function instance Swap<a> is uninhabited" == toString(result.errors[1]));
} }
TEST_CASE_FIXTURE(FamilyFixture, "resolve_deep_families") TEST_CASE_FIXTURE(FamilyFixture, "resolve_deep_families")
@ -148,7 +148,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "unsolvable_family")
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
for (size_t i = 0; i < 2; ++i) for (size_t i = 0; i < 2; ++i)
{ {
CHECK(toString(result.errors[i]) == "Type family instance Swap<a> is uninhabited"); CHECK(toString(result.errors[i]) == "Type function instance Swap<a> is uninhabited");
} }
} }
@ -169,7 +169,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "table_internal_families")
CHECK(toString(requireType("b")) == "{number}"); CHECK(toString(requireType("b")) == "{number}");
// FIXME: table types are constructing a trivial union here. // FIXME: table types are constructing a trivial union here.
CHECK(toString(requireType("c")) == "{Swap<boolean | boolean | boolean>}"); CHECK(toString(requireType("c")) == "{Swap<boolean | boolean | boolean>}");
CHECK(toString(result.errors[0]) == "Type family instance Swap<boolean | boolean | boolean> is uninhabited"); CHECK(toString(result.errors[0]) == "Type function instance Swap<boolean | boolean | boolean> is uninhabited");
} }
TEST_CASE_FIXTURE(FamilyFixture, "function_internal_families") TEST_CASE_FIXTURE(FamilyFixture, "function_internal_families")
@ -190,7 +190,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "function_internal_families")
CHECK(toString(requireType("a")) == "() -> string"); CHECK(toString(requireType("a")) == "() -> string");
CHECK(toString(requireType("b")) == "() -> number"); CHECK(toString(requireType("b")) == "() -> number");
CHECK(toString(requireType("c")) == "() -> Swap<boolean>"); CHECK(toString(requireType("c")) == "() -> Swap<boolean>");
CHECK(toString(result.errors[0]) == "Type family instance Swap<boolean> is uninhabited"); CHECK(toString(result.errors[0]) == "Type function instance Swap<boolean> is uninhabited");
} }
TEST_CASE_FIXTURE(Fixture, "add_family_at_work") TEST_CASE_FIXTURE(Fixture, "add_family_at_work")
@ -212,8 +212,8 @@ TEST_CASE_FIXTURE(Fixture, "add_family_at_work")
CHECK(toString(requireType("a")) == "number"); CHECK(toString(requireType("a")) == "number");
CHECK(toString(requireType("b")) == "Add<number, string>"); CHECK(toString(requireType("b")) == "Add<number, string>");
CHECK(toString(requireType("c")) == "Add<string, number>"); CHECK(toString(requireType("c")) == "Add<string, number>");
CHECK(toString(result.errors[0]) == "Type family instance Add<number, string> is uninhabited"); CHECK(toString(result.errors[0]) == "Type function instance Add<number, string> is uninhabited");
CHECK(toString(result.errors[1]) == "Type family instance Add<string, number> is uninhabited"); CHECK(toString(result.errors[1]) == "Type function instance Add<string, number> is uninhabited");
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_add_family_at_work") TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_add_family_at_work")
@ -284,7 +284,7 @@ TEST_CASE_FIXTURE(Fixture, "internal_families_raise_errors")
)"); )");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) == "Type family instance Add<a, b> depends on generic function parameters but does not appear in the function " CHECK(toString(result.errors[0]) == "Type function instance Add<a, b> depends on generic function parameters but does not appear in the function "
"signature; this construct cannot be type-checked at this time"); "signature; this construct cannot be type-checked at this time");
} }
@ -301,7 +301,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_families_can_be_shadowed")
return string.format("hi %s", f) return string.format("hi %s", f)
end end
-- this should still work totally fine (and use the real type family) -- this should still work totally fine (and use the real type function)
function plus(a, b) function plus(a, b)
return a + b return a + b
end end
@ -387,7 +387,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_errors_if_it_has_nontable_
local function err(idx: KeysOfMyObject): "x" | "y" | "z" return idx end local function err(idx: KeysOfMyObject): "x" | "y" | "z" return idx end
)"); )");
// FIXME(CLI-95289): we should actually only report the type family being uninhabited error at its first use, I think? // FIXME(CLI-95289): we should actually only report the type function being uninhabited error at its first use, I think?
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK(toString(result.errors[0]) == "Type 'MyObject | boolean' does not have keys, so 'keyof<MyObject | boolean>' is invalid"); CHECK(toString(result.errors[0]) == "Type 'MyObject | boolean' does not have keys, so 'keyof<MyObject | boolean>' is invalid");
CHECK(toString(result.errors[1]) == "Type 'MyObject | boolean' does not have keys, so 'keyof<MyObject | boolean>' is invalid"); CHECK(toString(result.errors[1]) == "Type 'MyObject | boolean' does not have keys, so 'keyof<MyObject | boolean>' is invalid");
@ -513,7 +513,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_errors_if_it_has_nontab
local function err(idx: KeysOfMyObject): "x" | "y" | "z" return idx end local function err(idx: KeysOfMyObject): "x" | "y" | "z" return idx end
)"); )");
// FIXME(CLI-95289): we should actually only report the type family being uninhabited error at its first use, I think? // FIXME(CLI-95289): we should actually only report the type function being uninhabited error at its first use, I think?
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK(toString(result.errors[0]) == "Type 'MyObject | boolean' does not have keys, so 'rawkeyof<MyObject | boolean>' is invalid"); CHECK(toString(result.errors[0]) == "Type 'MyObject | boolean' does not have keys, so 'rawkeyof<MyObject | boolean>' is invalid");
CHECK(toString(result.errors[1]) == "Type 'MyObject | boolean' does not have keys, so 'rawkeyof<MyObject | boolean>' is invalid"); CHECK(toString(result.errors[1]) == "Type 'MyObject | boolean' does not have keys, so 'rawkeyof<MyObject | boolean>' is invalid");
@ -586,7 +586,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_errors_if_it_has_nonclass_par
local function err(idx: KeysOfMyObject): "BaseMethod" | "BaseField" return idx end local function err(idx: KeysOfMyObject): "BaseMethod" | "BaseField" return idx end
)"); )");
// FIXME(CLI-95289): we should actually only report the type family being uninhabited error at its first use, I think? // FIXME(CLI-95289): we should actually only report the type function being uninhabited error at its first use, I think?
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK(toString(result.errors[0]) == "Type 'BaseClass | boolean' does not have keys, so 'keyof<BaseClass | boolean>' is invalid"); CHECK(toString(result.errors[0]) == "Type 'BaseClass | boolean' does not have keys, so 'keyof<BaseClass | boolean>' is invalid");
CHECK(toString(result.errors[1]) == "Type 'BaseClass | boolean' does not have keys, so 'keyof<BaseClass | boolean>' is invalid"); CHECK(toString(result.errors[1]) == "Type 'BaseClass | boolean' does not have keys, so 'keyof<BaseClass | boolean>' is invalid");
@ -757,7 +757,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "exceeded_distributivity_limits")
)"); )");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<UninhabitedTypeFamily>(result.errors[0])); CHECK(get<UninhabitedTypeFunction>(result.errors[0]));
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "didnt_quite_exceed_distributivity_limits") TEST_CASE_FIXTURE(BuiltinsFixture, "didnt_quite_exceed_distributivity_limits")
@ -1163,4 +1163,4 @@ TEST_CASE_FIXTURE(ClassFixture, "rawget_type_family_errors_w_classes")
CHECK(toString(result.errors[0]) == "Property '\"BaseField\"' does not exist on type 'BaseClass'"); CHECK(toString(result.errors[0]) == "Property '\"BaseField\"' does not exist on type 'BaseClass'");
} }
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -386,6 +386,20 @@ stat = stat and tonumber(stat) or stat
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "table_of_any_calls")
{
CheckResult result = check(R"(
local function testFunc(input: {any})
end
local v = {true}
testFunc(v)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "intersection_of_any_can_have_props") TEST_CASE_FIXTURE(Fixture, "intersection_of_any_can_have_props")
{ {
// *blocked-130* ~ hasProp any & ~(false?), "_status" // *blocked-130* ~ hasProp any & ~(false?), "_status"

View File

@ -2262,7 +2262,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "regex_benchmark_string_format_minimization")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "subgeneric_typefamily_super_monomorphic") TEST_CASE_FIXTURE(BuiltinsFixture, "subgeneric_type_function_super_monomorphic")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
local a: (number, number) -> number = function(a, b) return a - b end local a: (number, number) -> number = function(a, b) return a - b end
@ -2747,5 +2747,21 @@ _ = _,{}
)"); )");
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "overload_resolution_crash_when_argExprs_is_smaller_than_type_args")
{
CheckResult result = check(R"(
--!strict
local parseError
type Set<T> = {[T]: any}
local function captureDependencies(
saveToSet: Set<PubTypes.Dependency>,
callback: (...any) -> any,
...
)
local data = table.pack(xpcall(callback, parseError, ...))
end
)");
}
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -264,7 +264,7 @@ TEST_CASE_FIXTURE(Fixture, "cannot_indirectly_compare_types_that_do_not_have_a_m
if (FFlag::DebugLuauDeferredConstraintResolution) if (FFlag::DebugLuauDeferredConstraintResolution)
{ {
UninhabitedTypeFamily* utf = get<UninhabitedTypeFamily>(result.errors[0]); UninhabitedTypeFunction* utf = get<UninhabitedTypeFunction>(result.errors[0]);
REQUIRE(utf); REQUIRE(utf);
REQUIRE_EQ(toString(utf->ty), "lt<a, b>"); REQUIRE_EQ(toString(utf->ty), "lt<a, b>");
} }
@ -294,7 +294,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cannot_indirectly_compare_types_that_do_not_
if (FFlag::DebugLuauDeferredConstraintResolution) if (FFlag::DebugLuauDeferredConstraintResolution)
{ {
UninhabitedTypeFamily* utf = get<UninhabitedTypeFamily>(result.errors[0]); UninhabitedTypeFunction* utf = get<UninhabitedTypeFunction>(result.errors[0]);
REQUIRE(utf); REQUIRE(utf);
REQUIRE_EQ(toString(utf->ty), "lt<M, M>"); REQUIRE_EQ(toString(utf->ty), "lt<M, M>");
} }
@ -557,7 +557,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus")
{ {
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
UninhabitedTypeFamily* utf = get<UninhabitedTypeFamily>(result.errors[0]); UninhabitedTypeFunction* utf = get<UninhabitedTypeFunction>(result.errors[0]);
REQUIRE(utf); REQUIRE(utf);
CHECK_EQ(toString(utf->ty), "unm<bar>"); CHECK_EQ(toString(utf->ty), "unm<bar>");
@ -786,7 +786,7 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato
if (FFlag::DebugLuauDeferredConstraintResolution) if (FFlag::DebugLuauDeferredConstraintResolution)
{ {
UninhabitedTypeFamily* utf = get<UninhabitedTypeFamily>(result.errors[0]); UninhabitedTypeFunction* utf = get<UninhabitedTypeFunction>(result.errors[0]);
REQUIRE(utf); REQUIRE(utf);
REQUIRE_EQ(toString(utf->ty), "lt<boolean, boolean>"); REQUIRE_EQ(toString(utf->ty), "lt<boolean, boolean>");
} }
@ -817,7 +817,7 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato
if (FFlag::DebugLuauDeferredConstraintResolution) if (FFlag::DebugLuauDeferredConstraintResolution)
{ {
UninhabitedTypeFamily* utf = get<UninhabitedTypeFamily>(result.errors[0]); UninhabitedTypeFunction* utf = get<UninhabitedTypeFunction>(result.errors[0]);
REQUIRE(utf); REQUIRE(utf);
REQUIRE_EQ(toString(utf->ty), "lt<number | string, number | string>"); REQUIRE_EQ(toString(utf->ty), "lt<number | string, number | string>");
} }

View File

@ -246,18 +246,18 @@ TypeAliases.report_shadowed_aliases
TypeAliases.type_alias_local_mutation TypeAliases.type_alias_local_mutation
TypeAliases.type_alias_local_rename TypeAliases.type_alias_local_rename
TypeAliases.type_alias_of_an_imported_recursive_generic_type TypeAliases.type_alias_of_an_imported_recursive_generic_type
TypeFamilyTests.add_family_at_work TypeFunctionTests.add_family_at_work
TypeFamilyTests.cyclic_add_family_at_work TypeFunctionTests.cyclic_add_family_at_work
TypeFamilyTests.cyclic_concat_family_at_work TypeFunctionTests.cyclic_concat_family_at_work
TypeFamilyTests.didnt_quite_exceed_distributivity_limits TypeFunctionTests.didnt_quite_exceed_distributivity_limits
TypeFamilyTests.ensure_equivalence_with_distributivity TypeFunctionTests.ensure_equivalence_with_distributivity
TypeFamilyTests.family_as_fn_arg TypeFunctionTests.family_as_fn_arg
TypeFamilyTests.index_type_family_works_w_generic_types TypeFunctionTests.index_type_family_works_w_generic_types
TypeFamilyTests.internal_families_raise_errors TypeFunctionTests.internal_families_raise_errors
TypeFamilyTests.keyof_oss_crash_gh1161 TypeFunctionTests.keyof_oss_crash_gh1161
TypeFamilyTests.mul_family_with_union_of_multiplicatives TypeFunctionTests.mul_family_with_union_of_multiplicatives
TypeFamilyTests.mul_family_with_union_of_multiplicatives_2 TypeFunctionTests.mul_family_with_union_of_multiplicatives_2
TypeFamilyTests.unsolvable_family TypeFunctionTests.unsolvable_family
TypeInfer.be_sure_to_use_active_txnlog_when_evaluating_a_variadic_overload TypeInfer.be_sure_to_use_active_txnlog_when_evaluating_a_variadic_overload
TypeInfer.check_type_infer_recursion_count TypeInfer.check_type_infer_recursion_count
TypeInfer.checking_should_not_ice TypeInfer.checking_should_not_ice
@ -277,7 +277,6 @@ TypeInfer.tc_if_else_expressions_expected_type_3
TypeInfer.type_infer_recursion_limit_no_ice TypeInfer.type_infer_recursion_limit_no_ice
TypeInfer.type_infer_recursion_limit_normalizer TypeInfer.type_infer_recursion_limit_normalizer
TypeInfer.unify_nearly_identical_recursive_types TypeInfer.unify_nearly_identical_recursive_types
TypeInferAnyError.can_subscript_any
TypeInferAnyError.for_in_loop_iterator_is_any TypeInferAnyError.for_in_loop_iterator_is_any
TypeInferAnyError.for_in_loop_iterator_is_any2 TypeInferAnyError.for_in_loop_iterator_is_any2
TypeInferAnyError.for_in_loop_iterator_is_any_pack TypeInferAnyError.for_in_loop_iterator_is_any_pack
@ -290,7 +289,6 @@ TypeInferClasses.class_type_mismatch_with_name_conflict
TypeInferClasses.detailed_class_unification_error TypeInferClasses.detailed_class_unification_error
TypeInferClasses.indexable_classes TypeInferClasses.indexable_classes
TypeInferClasses.table_class_unification_reports_sane_errors_for_missing_properties TypeInferClasses.table_class_unification_reports_sane_errors_for_missing_properties
TypeInferClasses.table_indexers_are_invariant
TypeInferClasses.we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class TypeInferClasses.we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class
TypeInferFunctions.another_other_higher_order_function TypeInferFunctions.another_other_higher_order_function
TypeInferFunctions.bidirectional_checking_of_callback_property TypeInferFunctions.bidirectional_checking_of_callback_property