mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Sync to upstream/release/635 (#1337)
# What's Changed? - Bugfixes in the new solver ## New Solver - Equality graphs(E-Graphs) data structures were added - Refactored even more instances of "type family" with "type function" - `table.insert` no longer spuriously warns while selecting an overload for reasonable arguments. - Add time tracing for the new solver - Miscellaneous fixes to unit tests --- ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@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: 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> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
This commit is contained in:
parent
2874ca9e86
commit
a7299c3f0f
@ -371,7 +371,7 @@ private:
|
|||||||
std::vector<std::optional<TypeId>> getExpectedCallTypesForFunctionOverloads(const TypeId fnType);
|
std::vector<std::optional<TypeId>> getExpectedCallTypesForFunctionOverloads(const TypeId fnType);
|
||||||
|
|
||||||
TypeId createTypeFunctionInstance(
|
TypeId createTypeFunctionInstance(
|
||||||
const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments, const ScopePtr& scope, Location location);
|
const TypeFunction& function, 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.
|
||||||
|
@ -91,8 +91,8 @@ struct ConstraintSolver
|
|||||||
// A mapping from free types to the number of unresolved constraints that mention them.
|
// A mapping from free types to the number of unresolved constraints that mention them.
|
||||||
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
|
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
|
||||||
|
|
||||||
// Irreducible/uninhabited type families or type pack families.
|
// Irreducible/uninhabited type functions or type pack functions.
|
||||||
DenseHashSet<const void*> uninhabitedTypeFamilies{{}};
|
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
|
||||||
|
|
||||||
// The set of types that will definitely be unchanged by generalization.
|
// The set of types that will definitely be unchanged by generalization.
|
||||||
DenseHashSet<TypeId> generalizedTypes_{nullptr};
|
DenseHashSet<TypeId> generalizedTypes_{nullptr};
|
||||||
@ -107,7 +107,7 @@ struct ConstraintSolver
|
|||||||
DcrLogger* logger;
|
DcrLogger* logger;
|
||||||
TypeCheckLimits limits;
|
TypeCheckLimits limits;
|
||||||
|
|
||||||
DenseHashMap<TypeId, const Constraint*> typeFamiliesToFinalize{nullptr};
|
DenseHashMap<TypeId, const Constraint*> typeFunctionsToFinalize{nullptr};
|
||||||
|
|
||||||
explicit ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, std::vector<NotNull<Constraint>> constraints,
|
explicit ConstraintSolver(NotNull<Normalizer> normalizer, NotNull<Scope> rootScope, std::vector<NotNull<Constraint>> constraints,
|
||||||
ModuleName moduleName, NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger,
|
ModuleName moduleName, NotNull<ModuleResolver> moduleResolver, std::vector<RequireCycle> requireCycles, DcrLogger* logger,
|
||||||
@ -124,10 +124,10 @@ struct ConstraintSolver
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to perform one final reduction on type families after every constraint has been completed
|
* Attempts to perform one final reduction on type functions after every constraint has been completed
|
||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
void finalizeTypeFamilies();
|
void finalizeTypeFunctions();
|
||||||
|
|
||||||
bool isDone();
|
bool isDone();
|
||||||
|
|
||||||
@ -345,7 +345,7 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reproduces any constraints necessary for new types that are copied when applying a substitution.
|
* Reproduces any constraints necessary for new types that are copied when applying a substitution.
|
||||||
* At the time of writing, this pertains only to type families.
|
* At the time of writing, this pertains only to type functions.
|
||||||
* @param subst the substitution that was applied
|
* @param subst the substitution that was applied
|
||||||
**/
|
**/
|
||||||
void reproduceConstraints(NotNull<Scope> scope, const Location& location, const Substitution& subst);
|
void reproduceConstraints(NotNull<Scope> scope, const Location& location, const Substitution& subst);
|
||||||
|
@ -85,7 +85,7 @@ struct SolveResult
|
|||||||
DenseHashMap<TypeId, std::vector<TypeId>> expandedFreeTypes{nullptr};
|
DenseHashMap<TypeId, std::vector<TypeId>> expandedFreeTypes{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper utility, presently used for binary operator type families.
|
// Helper utility, presently used for binary operator type functions.
|
||||||
//
|
//
|
||||||
// Given a function and a set of arguments, select a suitable overload.
|
// Given a function and a set of arguments, select a suitable overload.
|
||||||
SolveResult solveFunctionCall(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Normalizer> normalizer,
|
SolveResult solveFunctionCall(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Normalizer> normalizer,
|
||||||
|
@ -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 TypeFunctionInstanceType* subFamilyInstance, const TypeId superTy);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFunctionInstance, const TypeId superTy);
|
||||||
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFamilyInstance);
|
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFunctionInstance);
|
||||||
|
|
||||||
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> handleTypeFunctionReductionResult(const TypeFunctionInstanceType* familyInstance);
|
std::pair<TypeId, ErrorVec> handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance);
|
||||||
|
|
||||||
[[noreturn]] void unexpected(TypeId ty);
|
[[noreturn]] void unexpected(TypeId ty);
|
||||||
[[noreturn]] void unexpected(TypePackId tp);
|
[[noreturn]] void unexpected(TypePackId tp);
|
||||||
|
@ -540,27 +540,27 @@ struct ClassType
|
|||||||
*/
|
*/
|
||||||
struct TypeFunctionInstanceType
|
struct TypeFunctionInstanceType
|
||||||
{
|
{
|
||||||
NotNull<const TypeFunction> family;
|
NotNull<const TypeFunction> function;
|
||||||
|
|
||||||
std::vector<TypeId> typeArguments;
|
std::vector<TypeId> typeArguments;
|
||||||
std::vector<TypePackId> packArguments;
|
std::vector<TypePackId> packArguments;
|
||||||
|
|
||||||
TypeFunctionInstanceType(NotNull<const TypeFunction> family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
TypeFunctionInstanceType(NotNull<const TypeFunction> function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||||
: family(family)
|
: function(function)
|
||||||
, typeArguments(typeArguments)
|
, typeArguments(typeArguments)
|
||||||
, packArguments(packArguments)
|
, packArguments(packArguments)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionInstanceType(const TypeFunction& family, std::vector<TypeId> typeArguments)
|
TypeFunctionInstanceType(const TypeFunction& function, std::vector<TypeId> typeArguments)
|
||||||
: family{&family}
|
: function{&function}
|
||||||
, typeArguments(typeArguments)
|
, typeArguments(typeArguments)
|
||||||
, packArguments{}
|
, packArguments{}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionInstanceType(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
TypeFunctionInstanceType(const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||||
: family{&family}
|
: function{&function}
|
||||||
, typeArguments(typeArguments)
|
, typeArguments(typeArguments)
|
||||||
, packArguments(packArguments)
|
, packArguments(packArguments)
|
||||||
{
|
{
|
||||||
|
@ -49,10 +49,10 @@ struct TypeArena
|
|||||||
return addTypePack(TypePackVar(std::move(tp)));
|
return addTypePack(TypePackVar(std::move(tp)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId addTypeFunction(const TypeFunction& family, std::initializer_list<TypeId> types);
|
TypeId addTypeFunction(const TypeFunction& function, std::initializer_list<TypeId> types);
|
||||||
TypeId addTypeFunction(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
|
TypeId addTypeFunction(const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
|
||||||
TypePackId addTypePackFunction(const TypePackFunction& family, std::initializer_list<TypeId> types);
|
TypePackId addTypePackFunction(const TypePackFunction& function, std::initializer_list<TypeId> types);
|
||||||
TypePackId addTypePackFunction(const TypePackFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
|
TypePackId addTypePackFunction(const TypePackFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments = {});
|
||||||
};
|
};
|
||||||
|
|
||||||
void freeze(TypeArena& arena);
|
void freeze(TypeArena& arena);
|
||||||
|
@ -29,13 +29,13 @@ struct TypeFunctionReductionGuessResult
|
|||||||
struct TypeFunctionInferenceResult
|
struct TypeFunctionInferenceResult
|
||||||
{
|
{
|
||||||
std::vector<TypeId> operandInference;
|
std::vector<TypeId> operandInference;
|
||||||
TypeId familyResultInference;
|
TypeId functionResultInference;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TypeFunctionReductionGuesser
|
struct TypeFunctionReductionGuesser
|
||||||
{
|
{
|
||||||
// Tracks our hypothesis about what a type function reduces to
|
// Tracks our hypothesis about what a type function reduces to
|
||||||
DenseHashMap<TypeId, TypeId> familyReducesTo{nullptr};
|
DenseHashMap<TypeId, TypeId> functionReducesTo{nullptr};
|
||||||
// Tracks our constraints on type function operands
|
// Tracks our constraints on type function operands
|
||||||
DenseHashMap<TypeId, TypeId> substitutable{nullptr};
|
DenseHashMap<TypeId, TypeId> substitutable{nullptr};
|
||||||
// List of instances to try progress
|
// List of instances to try progress
|
||||||
@ -57,14 +57,14 @@ private:
|
|||||||
std::optional<TypeId> guessType(TypeId arg);
|
std::optional<TypeId> guessType(TypeId arg);
|
||||||
void dumpGuesses();
|
void dumpGuesses();
|
||||||
|
|
||||||
bool isNumericBinopFamily(const TypeFunctionInstanceType& instance);
|
bool isNumericBinopFunction(const TypeFunctionInstanceType& instance);
|
||||||
bool isComparisonFamily(const TypeFunctionInstanceType& instance);
|
bool isComparisonFunction(const TypeFunctionInstanceType& instance);
|
||||||
bool isOrAndFamily(const TypeFunctionInstanceType& instance);
|
bool isOrAndFunction(const TypeFunctionInstanceType& instance);
|
||||||
bool isNotFamily(const TypeFunctionInstanceType& instance);
|
bool isNotFunction(const TypeFunctionInstanceType& instance);
|
||||||
bool isLenFamily(const TypeFunctionInstanceType& instance);
|
bool isLenFunction(const TypeFunctionInstanceType& instance);
|
||||||
bool isUnaryMinus(const TypeFunctionInstanceType& instance);
|
bool isUnaryMinus(const TypeFunctionInstanceType& instance);
|
||||||
|
|
||||||
// Operand is assignable if it looks like a cyclic family instance, or a generic type
|
// Operand is assignable if it looks like a cyclic type function instance, or a generic type
|
||||||
bool operandIsAssignable(TypeId ty);
|
bool operandIsAssignable(TypeId ty);
|
||||||
std::optional<TypeId> tryAssignOperandType(TypeId ty);
|
std::optional<TypeId> tryAssignOperandType(TypeId ty);
|
||||||
|
|
||||||
@ -75,11 +75,11 @@ private:
|
|||||||
|
|
||||||
bool isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& instanceArgs);
|
bool isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& instanceArgs);
|
||||||
void inferTypeFunctionSubstitutions(TypeId ty, const TypeFunctionInstanceType* instance);
|
void inferTypeFunctionSubstitutions(TypeId ty, const TypeFunctionInstanceType* instance);
|
||||||
TypeFunctionInferenceResult inferNumericBinopFamily(const TypeFunctionInstanceType* instance);
|
TypeFunctionInferenceResult inferNumericBinopFunction(const TypeFunctionInstanceType* instance);
|
||||||
TypeFunctionInferenceResult inferComparisonFamily(const TypeFunctionInstanceType* instance);
|
TypeFunctionInferenceResult inferComparisonFunction(const TypeFunctionInstanceType* instance);
|
||||||
TypeFunctionInferenceResult inferOrAndFamily(const TypeFunctionInstanceType* instance);
|
TypeFunctionInferenceResult inferOrAndFunction(const TypeFunctionInstanceType* instance);
|
||||||
TypeFunctionInferenceResult inferNotFamily(const TypeFunctionInstanceType* instance);
|
TypeFunctionInferenceResult inferNotFunction(const TypeFunctionInstanceType* instance);
|
||||||
TypeFunctionInferenceResult inferLenFamily(const TypeFunctionInstanceType* instance);
|
TypeFunctionInferenceResult inferLenFunction(const TypeFunctionInstanceType* instance);
|
||||||
TypeFunctionInferenceResult inferUnaryMinusFamily(const TypeFunctionInstanceType* instance);
|
TypeFunctionInferenceResult inferUnaryMinusFunction(const TypeFunctionInstanceType* instance);
|
||||||
};
|
};
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
@ -92,7 +92,7 @@ struct BlockedTypePack
|
|||||||
*/
|
*/
|
||||||
struct TypeFunctionInstanceTypePack
|
struct TypeFunctionInstanceTypePack
|
||||||
{
|
{
|
||||||
NotNull<const TypePackFunction> family;
|
NotNull<const TypePackFunction> function;
|
||||||
|
|
||||||
std::vector<TypeId> typeArguments;
|
std::vector<TypeId> typeArguments;
|
||||||
std::vector<TypePackId> packArguments;
|
std::vector<TypePackId> packArguments;
|
||||||
|
@ -49,11 +49,11 @@ struct Unifier2
|
|||||||
|
|
||||||
std::vector<ConstraintV> incompleteSubtypes;
|
std::vector<ConstraintV> incompleteSubtypes;
|
||||||
// null if not in a constraint solving context
|
// null if not in a constraint solving context
|
||||||
DenseHashSet<const void*>* uninhabitedTypeFamilies;
|
DenseHashSet<const void*>* uninhabitedTypeFunctions;
|
||||||
|
|
||||||
Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice);
|
Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice);
|
||||||
Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice,
|
Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice,
|
||||||
DenseHashSet<const void*>* uninhabitedTypeFamilies);
|
DenseHashSet<const void*>* uninhabitedTypeFunctions);
|
||||||
|
|
||||||
/** Attempt to commit the subtype relation subTy <: superTy to the type
|
/** Attempt to commit the subtype relation subTy <: superTy to the type
|
||||||
* graph.
|
* graph.
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "Luau/Simplify.h"
|
#include "Luau/Simplify.h"
|
||||||
#include "Luau/StringUtils.h"
|
#include "Luau/StringUtils.h"
|
||||||
#include "Luau/TableLiteralInference.h"
|
#include "Luau/TableLiteralInference.h"
|
||||||
|
#include "Luau/TimeTrace.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeFunction.h"
|
#include "Luau/TypeFunction.h"
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
@ -211,6 +212,8 @@ ConstraintGenerator::ConstraintGenerator(ModulePtr module, NotNull<Normalizer> n
|
|||||||
|
|
||||||
void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("ConstraintGenerator::visitModuleRoot", "Typechecking");
|
||||||
|
|
||||||
LUAU_ASSERT(scopes.empty());
|
LUAU_ASSERT(scopes.empty());
|
||||||
LUAU_ASSERT(rootScope == nullptr);
|
LUAU_ASSERT(rootScope == nullptr);
|
||||||
ScopePtr scope = std::make_shared<Scope>(globalScope);
|
ScopePtr scope = std::make_shared<Scope>(globalScope);
|
||||||
@ -3356,9 +3359,9 @@ std::vector<std::optional<TypeId>> ConstraintGenerator::getExpectedCallTypesForF
|
|||||||
}
|
}
|
||||||
|
|
||||||
TypeId ConstraintGenerator::createTypeFunctionInstance(
|
TypeId ConstraintGenerator::createTypeFunctionInstance(
|
||||||
const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments, const ScopePtr& scope, Location location)
|
const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments, const ScopePtr& scope, Location location)
|
||||||
{
|
{
|
||||||
TypeId result = arena->addTypeFunction(family, typeArguments, packArguments);
|
TypeId result = arena->addTypeFunction(function, typeArguments, packArguments);
|
||||||
addConstraint(scope, location, ReduceConstraint{result});
|
addConstraint(scope, location, ReduceConstraint{result});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -366,6 +366,8 @@ void ConstraintSolver::randomize(unsigned seed)
|
|||||||
|
|
||||||
void ConstraintSolver::run()
|
void ConstraintSolver::run()
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("ConstraintSolver::run", "Typechecking");
|
||||||
|
|
||||||
if (isDone())
|
if (isDone())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -489,11 +491,11 @@ void ConstraintSolver::run()
|
|||||||
if (!unsolvedConstraints.empty())
|
if (!unsolvedConstraints.empty())
|
||||||
reportError(InternalError{"Type inference failed to complete, you may see some confusing types and type errors."}, Location{});
|
reportError(InternalError{"Type inference failed to complete, you may see some confusing types and type errors."}, Location{});
|
||||||
|
|
||||||
// After we have run all the constraints, type families should be generalized
|
// After we have run all the constraints, type functions should be generalized
|
||||||
// At this point, we can try to perform one final simplification to suss out
|
// At this point, we can try to perform one final simplification to suss out
|
||||||
// whether type families are truly uninhabited or if they can reduce
|
// whether type functions are truly uninhabited or if they can reduce
|
||||||
|
|
||||||
finalizeTypeFamilies();
|
finalizeTypeFunctions();
|
||||||
|
|
||||||
if (FFlag::DebugLuauLogSolver || FFlag::DebugLuauLogBindings)
|
if (FFlag::DebugLuauLogSolver || FFlag::DebugLuauLogBindings)
|
||||||
dumpBindings(rootScope, opts);
|
dumpBindings(rootScope, opts);
|
||||||
@ -504,10 +506,10 @@ void ConstraintSolver::run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintSolver::finalizeTypeFamilies()
|
void ConstraintSolver::finalizeTypeFunctions()
|
||||||
{
|
{
|
||||||
// At this point, we've generalized. Let's try to finish reducing as much as we can, we'll leave warning to the typechecker
|
// At this point, we've generalized. Let's try to finish reducing as much as we can, we'll leave warning to the typechecker
|
||||||
for (auto [t, constraint] : typeFamiliesToFinalize)
|
for (auto [t, constraint] : typeFunctionsToFinalize)
|
||||||
{
|
{
|
||||||
TypeId ty = follow(t);
|
TypeId ty = follow(t);
|
||||||
if (get<TypeFunctionInstanceType>(ty))
|
if (get<TypeFunctionInstanceType>(ty))
|
||||||
@ -721,7 +723,7 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
|
|||||||
* to figure out which of the above shapes we are actually working with.
|
* to figure out which of the above shapes we are actually working with.
|
||||||
*
|
*
|
||||||
* If `force` is true and we still do not know, we must flag a warning. Type
|
* If `force` is true and we still do not know, we must flag a warning. Type
|
||||||
* families are the fix for this.
|
* functions are the fix for this.
|
||||||
*
|
*
|
||||||
* Since we need to know all of this stuff about the types of the iteratee,
|
* Since we need to know all of this stuff about the types of the iteratee,
|
||||||
* we have no choice but for ConstraintSolver to also be the thing that
|
* we have no choice but for ConstraintSolver to also be the thing that
|
||||||
@ -1294,7 +1296,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
|||||||
ftv = get<FunctionType>(*res);
|
ftv = get<FunctionType>(*res);
|
||||||
LUAU_ASSERT(ftv);
|
LUAU_ASSERT(ftv);
|
||||||
|
|
||||||
// we've potentially copied type families here, so we need to reproduce their reduce constraint.
|
// we've potentially copied type functions here, so we need to reproduce their reduce constraint.
|
||||||
reproduceConstraints(constraint->scope, constraint->location, replacer);
|
reproduceConstraints(constraint->scope, constraint->location, replacer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1975,17 +1977,17 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
|
|||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
// If we couldn't reduce this type function, stick it in the set!
|
// If we couldn't reduce this type function, stick it in the set!
|
||||||
if (get<TypeFunctionInstanceType>(ty))
|
if (get<TypeFunctionInstanceType>(ty))
|
||||||
typeFamiliesToFinalize[ty] = constraint;
|
typeFunctionsToFinalize[ty] = constraint;
|
||||||
|
|
||||||
if (force || reductionFinished)
|
if (force || reductionFinished)
|
||||||
{
|
{
|
||||||
// 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 functions to unblock.
|
||||||
for (auto error : result.errors)
|
for (auto error : result.errors)
|
||||||
{
|
{
|
||||||
if (auto utf = get<UninhabitedTypeFunction>(error))
|
if (auto utf = get<UninhabitedTypeFunction>(error))
|
||||||
uninhabitedTypeFamilies.insert(utf->ty);
|
uninhabitedTypeFunctions.insert(utf->ty);
|
||||||
else if (auto utpf = get<UninhabitedTypePackFunction>(error))
|
else if (auto utpf = get<UninhabitedTypePackFunction>(error))
|
||||||
uninhabitedTypeFamilies.insert(utpf->tp);
|
uninhabitedTypeFunctions.insert(utpf->tp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2017,13 +2019,13 @@ bool ConstraintSolver::tryDispatch(const ReducePackConstraint& c, NotNull<const
|
|||||||
|
|
||||||
if (force || reductionFinished)
|
if (force || reductionFinished)
|
||||||
{
|
{
|
||||||
// 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 functions to unblock.
|
||||||
for (auto error : result.errors)
|
for (auto error : result.errors)
|
||||||
{
|
{
|
||||||
if (auto utf = get<UninhabitedTypeFunction>(error))
|
if (auto utf = get<UninhabitedTypeFunction>(error))
|
||||||
uninhabitedTypeFamilies.insert(utf->ty);
|
uninhabitedTypeFunctions.insert(utf->ty);
|
||||||
else if (auto utpf = get<UninhabitedTypePackFunction>(error))
|
else if (auto utpf = get<UninhabitedTypePackFunction>(error))
|
||||||
uninhabitedTypeFamilies.insert(utpf->tp);
|
uninhabitedTypeFunctions.insert(utpf->tp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2498,7 +2500,7 @@ std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTa
|
|||||||
template<typename TID>
|
template<typename TID>
|
||||||
bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TID subTy, TID superTy)
|
bool ConstraintSolver::unify(NotNull<const Constraint> constraint, TID subTy, TID superTy)
|
||||||
{
|
{
|
||||||
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}, &uninhabitedTypeFamilies};
|
Unifier2 u2{NotNull{arena}, builtinTypes, constraint->scope, NotNull{&iceReporter}, &uninhabitedTypeFunctions};
|
||||||
|
|
||||||
const bool ok = u2.unify(subTy, superTy);
|
const bool ok = u2.unify(subTy, superTy);
|
||||||
|
|
||||||
@ -2735,7 +2737,7 @@ bool ConstraintSolver::isBlocked(TypeId ty)
|
|||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
|
||||||
if (auto tfit = get<TypeFunctionInstanceType>(ty))
|
if (auto tfit = get<TypeFunctionInstanceType>(ty))
|
||||||
return uninhabitedTypeFamilies.contains(ty) == false;
|
return uninhabitedTypeFunctions.contains(ty) == false;
|
||||||
|
|
||||||
return nullptr != get<BlockedType>(ty) || nullptr != get<PendingExpansionType>(ty);
|
return nullptr != get<BlockedType>(ty) || nullptr != get<PendingExpansionType>(ty);
|
||||||
}
|
}
|
||||||
@ -2745,7 +2747,7 @@ bool ConstraintSolver::isBlocked(TypePackId tp)
|
|||||||
tp = follow(tp);
|
tp = follow(tp);
|
||||||
|
|
||||||
if (auto tfitp = get<TypeFunctionInstanceTypePack>(tp))
|
if (auto tfitp = get<TypeFunctionInstanceTypePack>(tp))
|
||||||
return uninhabitedTypeFamilies.contains(tp) == false;
|
return uninhabitedTypeFunctions.contains(tp) == false;
|
||||||
|
|
||||||
return nullptr != get<BlockedTypePack>(tp);
|
return nullptr != get<BlockedTypePack>(tp);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "Luau/Def.h"
|
#include "Luau/Def.h"
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/Error.h"
|
#include "Luau/Error.h"
|
||||||
|
#include "Luau/TimeTrace.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
@ -136,6 +137,8 @@ bool DfgScope::canUpdateDefinition(DefId def, const std::string& key) const
|
|||||||
|
|
||||||
DataFlowGraph DataFlowGraphBuilder::build(AstStatBlock* block, NotNull<InternalErrorReporter> handle)
|
DataFlowGraph DataFlowGraphBuilder::build(AstStatBlock* block, NotNull<InternalErrorReporter> handle)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("DataFlowGraphBuilder::build", "Typechecking");
|
||||||
|
|
||||||
LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution);
|
LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution);
|
||||||
|
|
||||||
DataFlowGraphBuilder builder;
|
DataFlowGraphBuilder builder;
|
||||||
|
@ -585,7 +585,7 @@ struct ErrorConverter
|
|||||||
return "Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type function.";
|
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->function->name); unaryString != kUnaryOps.end())
|
||||||
{
|
{
|
||||||
std::string result = "Operator '" + std::string(unaryString->second) + "' could not be applied to ";
|
std::string result = "Operator '" + std::string(unaryString->second) + "' could not be applied to ";
|
||||||
|
|
||||||
@ -593,8 +593,8 @@ struct ErrorConverter
|
|||||||
{
|
{
|
||||||
result += "operand of type " + Luau::toString(tfit->typeArguments[0]);
|
result += "operand of type " + Luau::toString(tfit->typeArguments[0]);
|
||||||
|
|
||||||
if (tfit->family->name != "not")
|
if (tfit->function->name != "not")
|
||||||
result += "; there is no corresponding overload for __" + tfit->family->name;
|
result += "; there is no corresponding overload for __" + tfit->function->name;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -619,7 +619,7 @@ struct ErrorConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// binary operators
|
// binary operators
|
||||||
if (auto binaryString = kBinaryOps.find(tfit->family->name); binaryString != kBinaryOps.end())
|
if (auto binaryString = kBinaryOps.find(tfit->function->name); binaryString != kBinaryOps.end())
|
||||||
{
|
{
|
||||||
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
|
||||||
|
|
||||||
@ -646,14 +646,14 @@ struct ErrorConverter
|
|||||||
result += ", " + Luau::toString(packArg);
|
result += ", " + Luau::toString(packArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
result += "; there is no corresponding overload for __" + tfit->family->name;
|
result += "; there is no corresponding overload for __" + tfit->function->name;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// miscellaneous
|
// miscellaneous
|
||||||
|
|
||||||
if ("keyof" == tfit->family->name || "rawkeyof" == tfit->family->name)
|
if ("keyof" == tfit->function->name || "rawkeyof" == tfit->function->name)
|
||||||
{
|
{
|
||||||
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";
|
||||||
@ -661,19 +661,19 @@ struct ErrorConverter
|
|||||||
return "Type function 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->function->name || "rawget" == tfit->function->name)
|
||||||
{
|
{
|
||||||
if (tfit->typeArguments.size() != 2)
|
if (tfit->typeArguments.size() != 2)
|
||||||
return "Type function 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->function->name + "<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type";
|
||||||
else // Property `indexer` does not exist on type `indexee`
|
else // Property `indexer` does not exist on type `indexee`
|
||||||
return "Property '" + Luau::toString(tfit->typeArguments[1]) + "' does not exist on type '" + Luau::toString(tfit->typeArguments[0]) +
|
return "Property '" + Luau::toString(tfit->typeArguments[1]) + "' does not exist on type '" + Luau::toString(tfit->typeArguments[0]) +
|
||||||
"'";
|
"'";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kUnreachableTypeFunctions.count(tfit->family->name))
|
if (kUnreachableTypeFunctions.count(tfit->function->name))
|
||||||
{
|
{
|
||||||
return "Type function 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";
|
||||||
@ -706,7 +706,7 @@ struct ErrorConverter
|
|||||||
|
|
||||||
std::string operator()(const UninhabitedTypePackFunction& e) const
|
std::string operator()(const UninhabitedTypePackFunction& e) const
|
||||||
{
|
{
|
||||||
return "Type pack family instance " + Luau::toString(e.tp) + " is uninhabited";
|
return "Type pack function instance " + Luau::toString(e.tp) + " is uninhabited";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string operator()(const WhereClauseNeeded& e) const
|
std::string operator()(const WhereClauseNeeded& e) const
|
||||||
@ -718,7 +718,7 @@ struct ErrorConverter
|
|||||||
|
|
||||||
std::string operator()(const PackWhereClauseNeeded& e) const
|
std::string operator()(const PackWhereClauseNeeded& e) const
|
||||||
{
|
{
|
||||||
return "Type pack family instance " + Luau::toString(e.tp) +
|
return "Type pack function instance " + Luau::toString(e.tp) +
|
||||||
" 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";
|
||||||
}
|
}
|
||||||
|
@ -1249,6 +1249,10 @@ ModulePtr check(const SourceModule& sourceModule, Mode mode, const std::vector<R
|
|||||||
const ScopePtr& parentScope, std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope, FrontendOptions options,
|
const ScopePtr& parentScope, std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope, FrontendOptions options,
|
||||||
TypeCheckLimits limits, bool recordJsonLog, std::function<void(const ModuleName&, std::string)> writeJsonLog)
|
TypeCheckLimits limits, bool recordJsonLog, std::function<void(const ModuleName&, std::string)> writeJsonLog)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("Frontend::check", "Typechecking");
|
||||||
|
LUAU_TIMETRACE_ARGUMENT("module", sourceModule.name.c_str());
|
||||||
|
LUAU_TIMETRACE_ARGUMENT("name", sourceModule.humanReadableName.c_str());
|
||||||
|
|
||||||
ModulePtr result = std::make_shared<Module>();
|
ModulePtr result = std::make_shared<Module>();
|
||||||
result->name = sourceModule.name;
|
result->name = sourceModule.name;
|
||||||
result->humanReadableName = sourceModule.humanReadableName;
|
result->humanReadableName = sourceModule.humanReadableName;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "Luau/Subtyping.h"
|
#include "Luau/Subtyping.h"
|
||||||
#include "Luau/Normalize.h"
|
#include "Luau/Normalize.h"
|
||||||
#include "Luau/Error.h"
|
#include "Luau/Error.h"
|
||||||
|
#include "Luau/TimeTrace.h"
|
||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
#include "Luau/TypeFunction.h"
|
#include "Luau/TypeFunction.h"
|
||||||
#include "Luau/Def.h"
|
#include "Luau/Def.h"
|
||||||
@ -206,7 +207,7 @@ struct NonStrictTypeChecker
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TypeId checkForFamilyInhabitance(TypeId instance, Location location)
|
TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location)
|
||||||
{
|
{
|
||||||
if (noTypeFunctionErrors.find(instance))
|
if (noTypeFunctionErrors.find(instance))
|
||||||
return instance;
|
return instance;
|
||||||
@ -227,11 +228,11 @@ struct NonStrictTypeChecker
|
|||||||
{
|
{
|
||||||
TypeId* ty = module->astTypes.find(expr);
|
TypeId* ty = module->astTypes.find(expr);
|
||||||
if (ty)
|
if (ty)
|
||||||
return checkForFamilyInhabitance(follow(*ty), expr->location);
|
return checkForTypeFunctionInhabitance(follow(*ty), expr->location);
|
||||||
|
|
||||||
TypePackId* tp = module->astTypePacks.find(expr);
|
TypePackId* tp = module->astTypePacks.find(expr);
|
||||||
if (tp)
|
if (tp)
|
||||||
return checkForFamilyInhabitance(flattenPack(*tp), expr->location);
|
return checkForTypeFunctionInhabitance(flattenPack(*tp), expr->location);
|
||||||
return builtinTypes->anyType;
|
return builtinTypes->anyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,6 +729,8 @@ private:
|
|||||||
void checkNonStrict(NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> ice, NotNull<UnifierSharedState> unifierState,
|
void checkNonStrict(NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> ice, NotNull<UnifierSharedState> unifierState,
|
||||||
NotNull<const DataFlowGraph> dfg, NotNull<TypeCheckLimits> limits, const SourceModule& sourceModule, Module* module)
|
NotNull<const DataFlowGraph> dfg, NotNull<TypeCheckLimits> limits, const SourceModule& sourceModule, Module* module)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("checkNonStrict", "Typechecking");
|
||||||
|
|
||||||
NonStrictTypeChecker typeChecker{NotNull{&module->internalTypes}, builtinTypes, ice, unifierState, dfg, limits, module};
|
NonStrictTypeChecker typeChecker{NotNull{&module->internalTypes}, builtinTypes, ice, unifierState, dfg, limits, module};
|
||||||
typeChecker.visit(sourceModule.root);
|
typeChecker.visit(sourceModule.root);
|
||||||
unfreeze(module->interfaceTypes);
|
unfreeze(module->interfaceTypes);
|
||||||
|
@ -127,7 +127,7 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
|
|||||||
return dest.addType(NegationType{a.ty});
|
return dest.addType(NegationType{a.ty});
|
||||||
else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
|
else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
|
||||||
{
|
{
|
||||||
TypeFunctionInstanceType clone{a.family, a.typeArguments, a.packArguments};
|
TypeFunctionInstanceType clone{a.function, a.typeArguments, a.packArguments};
|
||||||
return dest.addType(std::move(clone));
|
return dest.addType(std::move(clone));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -672,7 +672,7 @@ TypePackId Substitution::clone(TypePackId tp)
|
|||||||
else if (const TypeFunctionInstanceTypePack* tfitp = get<TypeFunctionInstanceTypePack>(tp))
|
else if (const TypeFunctionInstanceTypePack* tfitp = get<TypeFunctionInstanceTypePack>(tp))
|
||||||
{
|
{
|
||||||
TypeFunctionInstanceTypePack clone{
|
TypeFunctionInstanceTypePack clone{
|
||||||
tfitp->family, std::vector<TypeId>(tfitp->typeArguments.size()), std::vector<TypePackId>(tfitp->packArguments.size())};
|
tfitp->function, 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());
|
||||||
return addTypePack(std::move(clone));
|
return addTypePack(std::move(clone));
|
||||||
|
@ -530,6 +530,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||||||
}
|
}
|
||||||
else if (get<AnyType>(superTy))
|
else if (get<AnyType>(superTy))
|
||||||
result = {true};
|
result = {true};
|
||||||
|
|
||||||
|
// We have added this as an exception - the set of inhabitants of any is exactly the set of inhabitants of unknown (since error has no
|
||||||
|
// inhabitants). any = err | unknown, so under semantic subtyping, {} U unknown = unknown
|
||||||
|
else if (get<AnyType>(subTy) && get<UnknownType>(superTy))
|
||||||
|
result = {true};
|
||||||
else if (get<AnyType>(subTy))
|
else if (get<AnyType>(subTy))
|
||||||
{
|
{
|
||||||
// any = unknown | error, so we rewrite this to match.
|
// any = unknown | error, so we rewrite this to match.
|
||||||
@ -1584,19 +1589,19 @@ bool Subtyping::bindGeneric(SubtypingEnvironment& env, TypeId subTy, TypeId supe
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFamilyInstance, const TypeId superTy)
|
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFunctionInstance, const TypeId superTy)
|
||||||
{
|
{
|
||||||
// Reduce the type function instance
|
// Reduce the type function instance
|
||||||
auto [ty, errors] = handleTypeFunctionReductionResult(subFamilyInstance);
|
auto [ty, errors] = handleTypeFunctionReductionResult(subFunctionInstance);
|
||||||
|
|
||||||
// If we return optional, that means the type function 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 TypeFunctionInstanceType* superFamilyInstance)
|
SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFunctionInstance)
|
||||||
{
|
{
|
||||||
// Reduce the type function instance
|
// Reduce the type function instance
|
||||||
auto [ty, errors] = handleTypeFunctionReductionResult(superFamilyInstance);
|
auto [ty, errors] = handleTypeFunctionReductionResult(superFunctionInstance);
|
||||||
return isCovariantWith(env, subTy, ty).withErrors(errors).withSuperComponent(TypePath::Reduction{ty});
|
return isCovariantWith(env, subTy, ty).withErrors(errors).withSuperComponent(TypePath::Reduction{ty});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1632,19 +1637,19 @@ 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::handleTypeFunctionReductionResult(const TypeFunctionInstanceType* familyInstance)
|
std::pair<TypeId, ErrorVec> Subtyping::handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance)
|
||||||
{
|
{
|
||||||
TypeFunctionContext context{arena, builtinTypes, scope, normalizer, iceReporter, NotNull{&limits}};
|
TypeFunctionContext context{arena, builtinTypes, scope, normalizer, iceReporter, NotNull{&limits}};
|
||||||
TypeId family = arena->addType(*familyInstance);
|
TypeId function = arena->addType(*functionInstance);
|
||||||
FunctionGraphReductionResult result = reduceTypeFunctions(family, {}, context, true);
|
FunctionGraphReductionResult result = reduceTypeFunctions(function, {}, 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{{}, UninhabitedTypeFunction{family}});
|
errors.push_back(TypeError{{}, UninhabitedTypeFunction{function}});
|
||||||
return {builtinTypes->neverType, errors};
|
return {builtinTypes->neverType, errors};
|
||||||
}
|
}
|
||||||
if (result.reducedTypes.contains(family))
|
if (result.reducedTypes.contains(function))
|
||||||
return {family, errors};
|
return {function, errors};
|
||||||
return {builtinTypes->neverType, errors};
|
return {builtinTypes->neverType, errors};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ void StateDot::visitChildren(TypeId ty, int index)
|
|||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
|
else if constexpr (std::is_same_v<T, TypeFunctionInstanceType>)
|
||||||
{
|
{
|
||||||
formatAppend(result, "TypeFunctionInstanceType %s %d", t.family->name.c_str(), index);
|
formatAppend(result, "TypeFunctionInstanceType %s %d", t.function->name.c_str(), index);
|
||||||
finishNodeLabel(ty);
|
finishNodeLabel(ty);
|
||||||
finishNode();
|
finishNode();
|
||||||
|
|
||||||
|
@ -1033,7 +1033,7 @@ struct TypeStringifier
|
|||||||
|
|
||||||
void operator()(TypeId, const TypeFunctionInstanceType& tfitv)
|
void operator()(TypeId, const TypeFunctionInstanceType& tfitv)
|
||||||
{
|
{
|
||||||
state.emit(tfitv.family->name);
|
state.emit(tfitv.function->name);
|
||||||
state.emit("<");
|
state.emit("<");
|
||||||
|
|
||||||
bool comma = false;
|
bool comma = false;
|
||||||
@ -1234,7 +1234,7 @@ struct TypePackStringifier
|
|||||||
|
|
||||||
void operator()(TypePackId, const TypeFunctionInstanceTypePack& tfitp)
|
void operator()(TypePackId, const TypeFunctionInstanceTypePack& tfitp)
|
||||||
{
|
{
|
||||||
state.emit(tfitp.family->name);
|
state.emit(tfitp.function->name);
|
||||||
state.emit("<");
|
state.emit("<");
|
||||||
|
|
||||||
bool comma = false;
|
bool comma = false;
|
||||||
|
@ -424,7 +424,7 @@ bool maybeSingleton(TypeId ty)
|
|||||||
if (maybeSingleton(part)) // will i regret this?
|
if (maybeSingleton(part)) // will i regret this?
|
||||||
return true;
|
return true;
|
||||||
if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(ty))
|
if (const TypeFunctionInstanceType* tfit = get<TypeFunctionInstanceType>(ty))
|
||||||
if (tfit->family->name == "keyof" || tfit->family->name == "rawkeyof")
|
if (tfit->function->name == "keyof" || tfit->function->name == "rawkeyof")
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -94,24 +94,24 @@ TypePackId TypeArena::addTypePack(TypePackVar tp)
|
|||||||
return allocated;
|
return allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId TypeArena::addTypeFunction(const TypeFunction& family, std::initializer_list<TypeId> types)
|
TypeId TypeArena::addTypeFunction(const TypeFunction& function, std::initializer_list<TypeId> types)
|
||||||
{
|
{
|
||||||
return addType(TypeFunctionInstanceType{family, std::move(types)});
|
return addType(TypeFunctionInstanceType{function, std::move(types)});
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId TypeArena::addTypeFunction(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
TypeId TypeArena::addTypeFunction(const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||||
{
|
{
|
||||||
return addType(TypeFunctionInstanceType{family, std::move(typeArguments), std::move(packArguments)});
|
return addType(TypeFunctionInstanceType{function, std::move(typeArguments), std::move(packArguments)});
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId TypeArena::addTypePackFunction(const TypePackFunction& family, std::initializer_list<TypeId> types)
|
TypePackId TypeArena::addTypePackFunction(const TypePackFunction& function, std::initializer_list<TypeId> types)
|
||||||
{
|
{
|
||||||
return addTypePack(TypeFunctionInstanceTypePack{NotNull{&family}, std::move(types)});
|
return addTypePack(TypeFunctionInstanceTypePack{NotNull{&function}, std::move(types)});
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId TypeArena::addTypePackFunction(const TypePackFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
TypePackId TypeArena::addTypePackFunction(const TypePackFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
|
||||||
{
|
{
|
||||||
return addTypePack(TypeFunctionInstanceTypePack{NotNull{&family}, std::move(typeArguments), std::move(packArguments)});
|
return addTypePack(TypeFunctionInstanceTypePack{NotNull{&function}, std::move(typeArguments), std::move(packArguments)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeze(TypeArena& arena)
|
void freeze(TypeArena& arena)
|
||||||
|
@ -382,7 +382,7 @@ public:
|
|||||||
}
|
}
|
||||||
AstType* operator()(const TypeFunctionInstanceType& 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.function->name.c_str()}, std::nullopt, Location());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -456,7 +456,7 @@ public:
|
|||||||
|
|
||||||
AstTypePack* operator()(const TypeFunctionInstanceTypePack& 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.function->name.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "Luau/Normalize.h"
|
#include "Luau/Normalize.h"
|
||||||
#include "Luau/OverloadResolution.h"
|
#include "Luau/OverloadResolution.h"
|
||||||
#include "Luau/Subtyping.h"
|
#include "Luau/Subtyping.h"
|
||||||
|
#include "Luau/TimeTrace.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/TxnLog.h"
|
#include "Luau/TxnLog.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
@ -95,7 +96,7 @@ static std::optional<std::string> getIdentifierOfBaseVar(AstExpr* node)
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
bool areEquivalent(const T& a, const T& b)
|
bool areEquivalent(const T& a, const T& b)
|
||||||
{
|
{
|
||||||
if (a.family != b.family)
|
if (a.function != b.function)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (a.typeArguments.size() != b.typeArguments.size() || a.packArguments.size() != b.packArguments.size())
|
if (a.typeArguments.size() != b.typeArguments.size() || a.packArguments.size() != b.packArguments.size())
|
||||||
@ -116,39 +117,39 @@ bool areEquivalent(const T& a, const T& b)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FamilyFinder : TypeOnceVisitor
|
struct TypeFunctionFinder : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
DenseHashSet<TypeId> mentionedFamilies{nullptr};
|
DenseHashSet<TypeId> mentionedFunctions{nullptr};
|
||||||
DenseHashSet<TypePackId> mentionedFamilyPacks{nullptr};
|
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
|
||||||
|
|
||||||
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
|
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
|
||||||
{
|
{
|
||||||
mentionedFamilies.insert(ty);
|
mentionedFunctions.insert(ty);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypePackId tp, const TypeFunctionInstanceTypePack&) override
|
bool visit(TypePackId tp, const TypeFunctionInstanceTypePack&) override
|
||||||
{
|
{
|
||||||
mentionedFamilyPacks.insert(tp);
|
mentionedFunctionPacks.insert(tp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InternalFamilyFinder : TypeOnceVisitor
|
struct InternalTypeFunctionFinder : TypeOnceVisitor
|
||||||
{
|
{
|
||||||
DenseHashSet<TypeId> internalFamilies{nullptr};
|
DenseHashSet<TypeId> internalFunctions{nullptr};
|
||||||
DenseHashSet<TypePackId> internalPackFamilies{nullptr};
|
DenseHashSet<TypePackId> internalPackFunctions{nullptr};
|
||||||
DenseHashSet<TypeId> mentionedFamilies{nullptr};
|
DenseHashSet<TypeId> mentionedFunctions{nullptr};
|
||||||
DenseHashSet<TypePackId> mentionedFamilyPacks{nullptr};
|
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
|
||||||
|
|
||||||
InternalFamilyFinder(std::vector<TypeId>& declStack)
|
InternalTypeFunctionFinder(std::vector<TypeId>& declStack)
|
||||||
{
|
{
|
||||||
FamilyFinder f;
|
TypeFunctionFinder f;
|
||||||
for (TypeId fn : declStack)
|
for (TypeId fn : declStack)
|
||||||
f.traverse(fn);
|
f.traverse(fn);
|
||||||
|
|
||||||
mentionedFamilies = std::move(f.mentionedFamilies);
|
mentionedFunctions = std::move(f.mentionedFunctions);
|
||||||
mentionedFamilyPacks = std::move(f.mentionedFamilyPacks);
|
mentionedFunctionPacks = std::move(f.mentionedFunctionPacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
|
bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
|
||||||
@ -175,7 +176,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
|
|||||||
|
|
||||||
if (hasGeneric)
|
if (hasGeneric)
|
||||||
{
|
{
|
||||||
for (TypeId mentioned : mentionedFamilies)
|
for (TypeId mentioned : mentionedFunctions)
|
||||||
{
|
{
|
||||||
const TypeFunctionInstanceType* mentionedTfit = get<TypeFunctionInstanceType>(mentioned);
|
const TypeFunctionInstanceType* mentionedTfit = get<TypeFunctionInstanceType>(mentioned);
|
||||||
LUAU_ASSERT(mentionedTfit);
|
LUAU_ASSERT(mentionedTfit);
|
||||||
@ -185,7 +186,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internalFamilies.insert(ty);
|
internalFunctions.insert(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -215,7 +216,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
|
|||||||
|
|
||||||
if (hasGeneric)
|
if (hasGeneric)
|
||||||
{
|
{
|
||||||
for (TypePackId mentioned : mentionedFamilyPacks)
|
for (TypePackId mentioned : mentionedFunctionPacks)
|
||||||
{
|
{
|
||||||
const TypeFunctionInstanceTypePack* mentionedTfitp = get<TypeFunctionInstanceTypePack>(mentioned);
|
const TypeFunctionInstanceTypePack* mentionedTfitp = get<TypeFunctionInstanceTypePack>(mentioned);
|
||||||
LUAU_ASSERT(mentionedTfitp);
|
LUAU_ASSERT(mentionedTfitp);
|
||||||
@ -225,7 +226,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internalPackFamilies.insert(tp);
|
internalPackFunctions.insert(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -423,19 +424,19 @@ struct TypeChecker2
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkForInternalFamily(TypeId ty, Location location)
|
void checkForInternalTypeFunction(TypeId ty, Location location)
|
||||||
{
|
{
|
||||||
InternalFamilyFinder finder(functionDeclStack);
|
InternalTypeFunctionFinder finder(functionDeclStack);
|
||||||
finder.traverse(ty);
|
finder.traverse(ty);
|
||||||
|
|
||||||
for (TypeId internal : finder.internalFamilies)
|
for (TypeId internal : finder.internalFunctions)
|
||||||
reportError(WhereClauseNeeded{internal}, location);
|
reportError(WhereClauseNeeded{internal}, location);
|
||||||
|
|
||||||
for (TypePackId internal : finder.internalPackFamilies)
|
for (TypePackId internal : finder.internalPackFunctions)
|
||||||
reportError(PackWhereClauseNeeded{internal}, location);
|
reportError(PackWhereClauseNeeded{internal}, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId checkForFamilyInhabitance(TypeId instance, Location location)
|
TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location)
|
||||||
{
|
{
|
||||||
if (seenTypeFunctionInstances.find(instance))
|
if (seenTypeFunctionInstances.find(instance))
|
||||||
return instance;
|
return instance;
|
||||||
@ -468,11 +469,11 @@ struct TypeChecker2
|
|||||||
// allows us not to think about this very much in the actual typechecking logic.
|
// allows us not to think about this very much in the actual typechecking logic.
|
||||||
TypeId* ty = module->astTypes.find(expr);
|
TypeId* ty = module->astTypes.find(expr);
|
||||||
if (ty)
|
if (ty)
|
||||||
return checkForFamilyInhabitance(follow(*ty), expr->location);
|
return checkForTypeFunctionInhabitance(follow(*ty), expr->location);
|
||||||
|
|
||||||
TypePackId* tp = module->astTypePacks.find(expr);
|
TypePackId* tp = module->astTypePacks.find(expr);
|
||||||
if (tp)
|
if (tp)
|
||||||
return checkForFamilyInhabitance(flattenPack(*tp), expr->location);
|
return checkForTypeFunctionInhabitance(flattenPack(*tp), expr->location);
|
||||||
|
|
||||||
return builtinTypes->anyType;
|
return builtinTypes->anyType;
|
||||||
}
|
}
|
||||||
@ -495,7 +496,7 @@ struct TypeChecker2
|
|||||||
|
|
||||||
TypeId* ty = module->astResolvedTypes.find(annotation);
|
TypeId* ty = module->astResolvedTypes.find(annotation);
|
||||||
LUAU_ASSERT(ty);
|
LUAU_ASSERT(ty);
|
||||||
return checkForFamilyInhabitance(follow(*ty), annotation->location);
|
return checkForTypeFunctionInhabitance(follow(*ty), annotation->location);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation)
|
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation)
|
||||||
@ -1725,7 +1726,7 @@ struct TypeChecker2
|
|||||||
visit(*fn->returnAnnotation);
|
visit(*fn->returnAnnotation);
|
||||||
|
|
||||||
|
|
||||||
// If the function type has a family annotation, we need to see if we can suggest an annotation
|
// If the function type has a function annotation, we need to see if we can suggest an annotation
|
||||||
if (normalizedFnTy)
|
if (normalizedFnTy)
|
||||||
{
|
{
|
||||||
const FunctionType* inferredFtv = get<FunctionType>(normalizedFnTy->functions.parts.front());
|
const FunctionType* inferredFtv = get<FunctionType>(normalizedFnTy->functions.parts.front());
|
||||||
@ -1855,7 +1856,7 @@ struct TypeChecker2
|
|||||||
|
|
||||||
if (get<TypeFunctionInstanceType>(expectedResult))
|
if (get<TypeFunctionInstanceType>(expectedResult))
|
||||||
{
|
{
|
||||||
checkForInternalFamily(expectedResult, expr->location);
|
checkForInternalTypeFunction(expectedResult, expr->location);
|
||||||
return expectedResult;
|
return expectedResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2254,7 +2255,7 @@ struct TypeChecker2
|
|||||||
{
|
{
|
||||||
TypeId* resolvedTy = module->astResolvedTypes.find(ty);
|
TypeId* resolvedTy = module->astResolvedTypes.find(ty);
|
||||||
if (resolvedTy)
|
if (resolvedTy)
|
||||||
checkForFamilyInhabitance(follow(*resolvedTy), ty->location);
|
checkForTypeFunctionInhabitance(follow(*resolvedTy), ty->location);
|
||||||
|
|
||||||
if (auto t = ty->as<AstTypeReference>())
|
if (auto t = ty->as<AstTypeReference>())
|
||||||
return visit(t);
|
return visit(t);
|
||||||
@ -3057,6 +3058,8 @@ struct TypeChecker2
|
|||||||
void check(NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> unifierState, NotNull<TypeCheckLimits> limits, DcrLogger* logger,
|
void check(NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> unifierState, NotNull<TypeCheckLimits> limits, DcrLogger* logger,
|
||||||
const SourceModule& sourceModule, Module* module)
|
const SourceModule& sourceModule, Module* module)
|
||||||
{
|
{
|
||||||
|
LUAU_TIMETRACE_SCOPE("check", "Typechecking");
|
||||||
|
|
||||||
TypeChecker2 typeChecker{builtinTypes, unifierState, limits, logger, &sourceModule, module};
|
TypeChecker2 typeChecker{builtinTypes, unifierState, limits, logger, &sourceModule, module};
|
||||||
|
|
||||||
typeChecker.visit(sourceModule.root);
|
typeChecker.visit(sourceModule.root);
|
||||||
|
@ -104,7 +104,7 @@ struct TypeFunctionReducer
|
|||||||
VecDeque<TypeId> queuedTys;
|
VecDeque<TypeId> queuedTys;
|
||||||
VecDeque<TypePackId> queuedTps;
|
VecDeque<TypePackId> queuedTps;
|
||||||
TypeOrTypePackIdSet shouldGuess;
|
TypeOrTypePackIdSet shouldGuess;
|
||||||
std::vector<TypeId> cyclicTypeFamilies;
|
std::vector<TypeId> cyclicTypeFunctions;
|
||||||
TypeOrTypePackIdSet irreducible{nullptr};
|
TypeOrTypePackIdSet irreducible{nullptr};
|
||||||
FunctionGraphReductionResult result;
|
FunctionGraphReductionResult result;
|
||||||
bool force = false;
|
bool force = false;
|
||||||
@ -118,7 +118,7 @@ struct TypeFunctionReducer
|
|||||||
, queuedTys(std::move(queuedTys))
|
, queuedTys(std::move(queuedTys))
|
||||||
, queuedTps(std::move(queuedTps))
|
, queuedTps(std::move(queuedTps))
|
||||||
, shouldGuess(std::move(shouldGuess))
|
, shouldGuess(std::move(shouldGuess))
|
||||||
, cyclicTypeFamilies(std::move(cyclicTypes))
|
, cyclicTypeFunctions(std::move(cyclicTypes))
|
||||||
, force(force)
|
, force(force)
|
||||||
, location(location)
|
, location(location)
|
||||||
{
|
{
|
||||||
@ -138,7 +138,7 @@ struct TypeFunctionReducer
|
|||||||
|
|
||||||
if (is<TypeFunctionInstanceType>(ty))
|
if (is<TypeFunctionInstanceType>(ty))
|
||||||
{
|
{
|
||||||
for (auto t : cyclicTypeFamilies)
|
for (auto t : cyclicTypeFunctions)
|
||||||
{
|
{
|
||||||
if (ty == t)
|
if (ty == t)
|
||||||
return SkipTestResult::CyclicTypeFunction;
|
return SkipTestResult::CyclicTypeFunction;
|
||||||
@ -339,7 +339,7 @@ struct TypeFunctionReducer
|
|||||||
if (!testParameters(subject, tfit) && testCyclic != SkipTestResult::CyclicTypeFunction)
|
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 function\n");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -347,7 +347,7 @@ struct TypeFunctionReducer
|
|||||||
if (tryGuessing(subject))
|
if (tryGuessing(subject))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> result = tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx});
|
TypeFunctionReductionResult<TypeId> result = tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx});
|
||||||
handleTypeFunctionReduction(subject, result);
|
handleTypeFunctionReduction(subject, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -371,7 +371,7 @@ struct TypeFunctionReducer
|
|||||||
if (tryGuessing(subject))
|
if (tryGuessing(subject))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypePackId> result = tfit->family->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx});
|
TypeFunctionReductionResult<TypePackId> result = tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx});
|
||||||
handleTypeFunctionReduction(subject, result);
|
handleTypeFunctionReduction(subject, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -385,7 +385,7 @@ struct TypeFunctionReducer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static FunctionGraphReductionResult reduceFamiliesInternal(VecDeque<TypeId> queuedTys, VecDeque<TypePackId> queuedTps, TypeOrTypePackIdSet shouldGuess,
|
static FunctionGraphReductionResult reduceFunctionsInternal(VecDeque<TypeId> queuedTys, VecDeque<TypePackId> queuedTps, TypeOrTypePackIdSet shouldGuess,
|
||||||
std::vector<TypeId> cyclics, Location location, TypeFunctionContext ctx, bool force)
|
std::vector<TypeId> cyclics, Location location, TypeFunctionContext ctx, bool force)
|
||||||
{
|
{
|
||||||
TypeFunctionReducer 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};
|
||||||
@ -422,7 +422,7 @@ FunctionGraphReductionResult reduceTypeFunctions(TypeId entrypoint, Location loc
|
|||||||
if (collector.tys.empty() && collector.tps.empty())
|
if (collector.tys.empty() && collector.tps.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return reduceFamiliesInternal(std::move(collector.tys), std::move(collector.tps), std::move(collector.shouldGuess),
|
return reduceFunctionsInternal(std::move(collector.tys), std::move(collector.tps), std::move(collector.shouldGuess),
|
||||||
std::move(collector.cyclicInstance), location, ctx, force);
|
std::move(collector.cyclicInstance), location, ctx, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,7 +442,7 @@ FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location
|
|||||||
if (collector.tys.empty() && collector.tps.empty())
|
if (collector.tys.empty() && collector.tps.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return reduceFamiliesInternal(std::move(collector.tys), std::move(collector.tps), std::move(collector.shouldGuess),
|
return reduceFunctionsInternal(std::move(collector.tys), std::move(collector.tps), std::move(collector.shouldGuess),
|
||||||
std::move(collector.cyclicInstance), location, ctx, force);
|
std::move(collector.cyclicInstance), location, ctx, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,7 +528,7 @@ static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> notFamilyFn(
|
TypeFunctionReductionResult<TypeId> notTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -545,14 +545,14 @@ TypeFunctionReductionResult<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 = tryDistributeTypeFunctionApp(notFamilyFn, instance, typeParams, packParams, ctx))
|
if (auto result = tryDistributeTypeFunctionApp(notTypeFunction, 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, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> lenFamilyFn(
|
TypeFunctionReductionResult<TypeId> lenTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -604,7 +604,7 @@ TypeFunctionReductionResult<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 = tryDistributeTypeFunctionApp(notFamilyFn, instance, typeParams, packParams, ctx))
|
if (auto result = tryDistributeTypeFunctionApp(notTypeFunction, 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,7 +644,7 @@ TypeFunctionReductionResult<TypeId> lenFamilyFn(
|
|||||||
return {ctx->builtins->numberType, false, {}, {}};
|
return {ctx->builtins->numberType, false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> unmFamilyFn(
|
TypeFunctionReductionResult<TypeId> unmTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -689,7 +689,7 @@ TypeFunctionReductionResult<TypeId> unmFamilyFn(
|
|||||||
if (normTy->isExactlyNumber())
|
if (normTy->isExactlyNumber())
|
||||||
return {ctx->builtins->numberType, false, {}, {}};
|
return {ctx->builtins->numberType, false, {}, {}};
|
||||||
|
|
||||||
if (auto result = tryDistributeTypeFunctionApp(notFamilyFn, instance, typeParams, packParams, ctx))
|
if (auto result = tryDistributeTypeFunctionApp(notTypeFunction, 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
|
||||||
@ -744,7 +744,7 @@ NotNull<Constraint> TypeFunctionContext::pushConstraint(ConstraintV&& c)
|
|||||||
return newConstraint;
|
return newConstraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const std::vector<TypeId>& typeParams,
|
TypeFunctionReductionResult<TypeId> numericBinopTypeFunction(TypeId instance, const std::vector<TypeId>& typeParams,
|
||||||
const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -787,7 +787,7 @@ TypeFunctionReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const
|
|||||||
rhsTy = *rhsMaybeGeneralized;
|
rhsTy = *rhsMaybeGeneralized;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Normalization needs to remove cyclic type families from a `NormalizedType`.
|
// TODO: Normalization needs to remove cyclic type functions from a `NormalizedType`.
|
||||||
std::shared_ptr<const NormalizedType> normLhsTy = ctx->normalizer->normalize(lhsTy);
|
std::shared_ptr<const NormalizedType> normLhsTy = ctx->normalizer->normalize(lhsTy);
|
||||||
std::shared_ptr<const NormalizedType> normRhsTy = ctx->normalizer->normalize(rhsTy);
|
std::shared_ptr<const NormalizedType> normRhsTy = ctx->normalizer->normalize(rhsTy);
|
||||||
|
|
||||||
@ -803,7 +803,7 @@ TypeFunctionReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const
|
|||||||
if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber())
|
if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber())
|
||||||
return {ctx->builtins->numberType, false, {}, {}};
|
return {ctx->builtins->numberType, false, {}, {}};
|
||||||
|
|
||||||
if (auto result = tryDistributeTypeFunctionApp(numericBinopFamilyFn, instance, typeParams, packParams, ctx, metamethod))
|
if (auto result = tryDistributeTypeFunctionApp(numericBinopTypeFunction, 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,7 +847,7 @@ TypeFunctionReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const
|
|||||||
return {extracted.head.front(), false, {}, {}};
|
return {extracted.head.front(), false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> addFamilyFn(
|
TypeFunctionReductionResult<TypeId> addTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -856,10 +856,10 @@ TypeFunctionReductionResult<TypeId> addFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__add");
|
return numericBinopTypeFunction(instance, typeParams, packParams, ctx, "__add");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> subFamilyFn(
|
TypeFunctionReductionResult<TypeId> subTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -868,10 +868,10 @@ TypeFunctionReductionResult<TypeId> subFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__sub");
|
return numericBinopTypeFunction(instance, typeParams, packParams, ctx, "__sub");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> mulFamilyFn(
|
TypeFunctionReductionResult<TypeId> mulTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -880,10 +880,10 @@ TypeFunctionReductionResult<TypeId> mulFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__mul");
|
return numericBinopTypeFunction(instance, typeParams, packParams, ctx, "__mul");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> divFamilyFn(
|
TypeFunctionReductionResult<TypeId> divTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -892,10 +892,10 @@ TypeFunctionReductionResult<TypeId> divFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__div");
|
return numericBinopTypeFunction(instance, typeParams, packParams, ctx, "__div");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> idivFamilyFn(
|
TypeFunctionReductionResult<TypeId> idivTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -904,10 +904,10 @@ TypeFunctionReductionResult<TypeId> idivFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__idiv");
|
return numericBinopTypeFunction(instance, typeParams, packParams, ctx, "__idiv");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> powFamilyFn(
|
TypeFunctionReductionResult<TypeId> powTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -916,10 +916,10 @@ TypeFunctionReductionResult<TypeId> powFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__pow");
|
return numericBinopTypeFunction(instance, typeParams, packParams, ctx, "__pow");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> modFamilyFn(
|
TypeFunctionReductionResult<TypeId> modTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -928,10 +928,10 @@ TypeFunctionReductionResult<TypeId> modFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return numericBinopFamilyFn(instance, typeParams, packParams, ctx, "__mod");
|
return numericBinopTypeFunction(instance, typeParams, packParams, ctx, "__mod");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> concatFamilyFn(
|
TypeFunctionReductionResult<TypeId> concatTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -987,7 +987,7 @@ TypeFunctionReductionResult<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 = tryDistributeTypeFunctionApp(concatFamilyFn, instance, typeParams, packParams, ctx))
|
if (auto result = tryDistributeTypeFunctionApp(concatTypeFunction, 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,7 +1039,7 @@ TypeFunctionReductionResult<TypeId> concatFamilyFn(
|
|||||||
return {ctx->builtins->stringType, false, {}, {}};
|
return {ctx->builtins->stringType, false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> andFamilyFn(
|
TypeFunctionReductionResult<TypeId> andTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1090,7 +1090,7 @@ TypeFunctionReductionResult<TypeId> andFamilyFn(
|
|||||||
return {overallResult.result, false, std::move(blockedTypes), {}};
|
return {overallResult.result, false, std::move(blockedTypes), {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> orFamilyFn(
|
TypeFunctionReductionResult<TypeId> orTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1141,7 +1141,7 @@ TypeFunctionReductionResult<TypeId> orFamilyFn(
|
|||||||
return {overallResult.result, false, std::move(blockedTypes), {}};
|
return {overallResult.result, false, std::move(blockedTypes), {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, const std::vector<TypeId>& typeParams,
|
static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(TypeId instance, const std::vector<TypeId>& typeParams,
|
||||||
const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx, const std::string metamethod)
|
const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx, const std::string metamethod)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -1162,7 +1162,7 @@ static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, c
|
|||||||
else if (isPending(rhsTy, ctx->solver))
|
else if (isPending(rhsTy, ctx->solver))
|
||||||
return {std::nullopt, false, {rhsTy}, {}};
|
return {std::nullopt, false, {rhsTy}, {}};
|
||||||
|
|
||||||
// Algebra Reduction Rules for comparison family functions
|
// Algebra Reduction Rules for comparison type functions
|
||||||
// Note that comparing to never tells you nothing about the other operand
|
// Note that comparing to never tells you nothing about the other operand
|
||||||
// lt< 'a , never> -> continue
|
// lt< 'a , never> -> continue
|
||||||
// lt< never, 'a> -> continue
|
// lt< never, 'a> -> continue
|
||||||
@ -1173,7 +1173,7 @@ static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, c
|
|||||||
bool rhsFree = get<FreeType>(rhsTy) != nullptr;
|
bool rhsFree = get<FreeType>(rhsTy) != nullptr;
|
||||||
if (canSubmitConstraint)
|
if (canSubmitConstraint)
|
||||||
{
|
{
|
||||||
// Implement injective type families for comparison type families
|
// Implement injective type functions for comparison type functions
|
||||||
// lt <number, t> implies t is number
|
// lt <number, t> implies t is number
|
||||||
// lt <t, number> implies t is number
|
// lt <t, number> implies t is number
|
||||||
if (lhsFree && isNumber(rhsTy))
|
if (lhsFree && isNumber(rhsTy))
|
||||||
@ -1238,7 +1238,7 @@ static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, c
|
|||||||
if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber())
|
if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber())
|
||||||
return {ctx->builtins->booleanType, false, {}, {}};
|
return {ctx->builtins->booleanType, false, {}, {}};
|
||||||
|
|
||||||
if (auto result = tryDistributeTypeFunctionApp(comparisonFamilyFn, instance, typeParams, packParams, ctx, metamethod))
|
if (auto result = tryDistributeTypeFunctionApp(comparisonTypeFunction, 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,7 +1280,7 @@ static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, c
|
|||||||
return {ctx->builtins->booleanType, false, {}, {}};
|
return {ctx->builtins->booleanType, false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> ltFamilyFn(
|
TypeFunctionReductionResult<TypeId> ltTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1289,10 +1289,10 @@ TypeFunctionReductionResult<TypeId> ltFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return comparisonFamilyFn(instance, typeParams, packParams, ctx, "__lt");
|
return comparisonTypeFunction(instance, typeParams, packParams, ctx, "__lt");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> leFamilyFn(
|
TypeFunctionReductionResult<TypeId> leTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1301,10 +1301,10 @@ TypeFunctionReductionResult<TypeId> leFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return comparisonFamilyFn(instance, typeParams, packParams, ctx, "__le");
|
return comparisonTypeFunction(instance, typeParams, packParams, ctx, "__le");
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> eqFamilyFn(
|
TypeFunctionReductionResult<TypeId> eqTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1381,7 +1381,7 @@ TypeFunctionReductionResult<TypeId> eqFamilyFn(
|
|||||||
return {ctx->builtins->falseType, false, {}, {}};
|
return {ctx->builtins->falseType, false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {std::nullopt, true, {}, {}}; // if it's not, then this family is irreducible!
|
return {std::nullopt, true, {}, {}}; // if it's not, then this type function is irreducible!
|
||||||
}
|
}
|
||||||
|
|
||||||
mmType = follow(*mmType);
|
mmType = follow(*mmType);
|
||||||
@ -1435,7 +1435,7 @@ struct FindRefinementBlockers : TypeOnceVisitor
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> refineFamilyFn(
|
TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1520,7 +1520,7 @@ TypeFunctionReductionResult<TypeId> refineFamilyFn(
|
|||||||
return {resultTy, false, {}, {}};
|
return {resultTy, false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> singletonFamilyFn(
|
TypeFunctionReductionResult<TypeId> singletonTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1557,7 +1557,7 @@ TypeFunctionReductionResult<TypeId> singletonFamilyFn(
|
|||||||
return {ctx->builtins->unknownType, false, {}, {}};
|
return {ctx->builtins->unknownType, false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> unionFamilyFn(
|
TypeFunctionReductionResult<TypeId> unionTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
|
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
|
||||||
{
|
{
|
||||||
if (!packParams.empty())
|
if (!packParams.empty())
|
||||||
@ -1618,7 +1618,7 @@ TypeFunctionReductionResult<TypeId> unionFamilyFn(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> intersectFamilyFn(
|
TypeFunctionReductionResult<TypeId> intersectTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
|
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
|
||||||
{
|
{
|
||||||
if (!packParams.empty())
|
if (!packParams.empty())
|
||||||
@ -1725,7 +1725,7 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> keyofFamilyImpl(
|
TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
|
||||||
const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1842,7 +1842,7 @@ TypeFunctionReductionResult<TypeId> keyofFamilyImpl(
|
|||||||
return {ctx->arena->addType(UnionType{singletons}), false, {}, {}};
|
return {ctx->arena->addType(UnionType{singletons}), false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> keyofFamilyFn(
|
TypeFunctionReductionResult<TypeId> keyofTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1851,10 +1851,10 @@ TypeFunctionReductionResult<TypeId> keyofFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyofFamilyImpl(typeParams, packParams, ctx, /* isRaw */ false);
|
return keyofFunctionImpl(typeParams, packParams, ctx, /* isRaw */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> rawkeyofFamilyFn(
|
TypeFunctionReductionResult<TypeId> rawkeyofTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -1863,7 +1863,7 @@ TypeFunctionReductionResult<TypeId> rawkeyofFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyofFamilyImpl(typeParams, packParams, ctx, /* isRaw */ true);
|
return keyofFunctionImpl(typeParams, packParams, ctx, /* isRaw */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Searches through table's or class's props/indexer to find the property of `ty`
|
/* Searches through table's or class's props/indexer to find the property of `ty`
|
||||||
@ -1960,7 +1960,7 @@ 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 */
|
||||||
TypeFunctionReductionResult<TypeId> indexFamilyImpl(
|
TypeFunctionReductionResult<TypeId> indexFunctionImpl(
|
||||||
const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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));
|
||||||
@ -2064,7 +2064,7 @@ TypeFunctionReductionResult<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, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> indexFamilyFn(
|
TypeFunctionReductionResult<TypeId> indexTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -2073,10 +2073,10 @@ TypeFunctionReductionResult<TypeId> indexFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ false);
|
return indexFunctionImpl(typeParams, packParams, ctx, /* isRaw */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionReductionResult<TypeId> rawgetFamilyFn(
|
TypeFunctionReductionResult<TypeId> rawgetTypeFunction(
|
||||||
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> 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())
|
||||||
@ -2085,34 +2085,34 @@ TypeFunctionReductionResult<TypeId> rawgetFamilyFn(
|
|||||||
LUAU_ASSERT(false);
|
LUAU_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ true);
|
return indexFunctionImpl(typeParams, packParams, ctx, /* isRaw */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
BuiltinTypeFunctions::BuiltinTypeFunctions()
|
BuiltinTypeFunctions::BuiltinTypeFunctions()
|
||||||
: notFunc{"not", notFamilyFn}
|
: notFunc{"not", notTypeFunction}
|
||||||
, lenFunc{"len", lenFamilyFn}
|
, lenFunc{"len", lenTypeFunction}
|
||||||
, unmFunc{"unm", unmFamilyFn}
|
, unmFunc{"unm", unmTypeFunction}
|
||||||
, addFunc{"add", addFamilyFn}
|
, addFunc{"add", addTypeFunction}
|
||||||
, subFunc{"sub", subFamilyFn}
|
, subFunc{"sub", subTypeFunction}
|
||||||
, mulFunc{"mul", mulFamilyFn}
|
, mulFunc{"mul", mulTypeFunction}
|
||||||
, divFunc{"div", divFamilyFn}
|
, divFunc{"div", divTypeFunction}
|
||||||
, idivFunc{"idiv", idivFamilyFn}
|
, idivFunc{"idiv", idivTypeFunction}
|
||||||
, powFunc{"pow", powFamilyFn}
|
, powFunc{"pow", powTypeFunction}
|
||||||
, modFunc{"mod", modFamilyFn}
|
, modFunc{"mod", modTypeFunction}
|
||||||
, concatFunc{"concat", concatFamilyFn}
|
, concatFunc{"concat", concatTypeFunction}
|
||||||
, andFunc{"and", andFamilyFn}
|
, andFunc{"and", andTypeFunction}
|
||||||
, orFunc{"or", orFamilyFn}
|
, orFunc{"or", orTypeFunction}
|
||||||
, ltFunc{"lt", ltFamilyFn}
|
, ltFunc{"lt", ltTypeFunction}
|
||||||
, leFunc{"le", leFamilyFn}
|
, leFunc{"le", leTypeFunction}
|
||||||
, eqFunc{"eq", eqFamilyFn}
|
, eqFunc{"eq", eqTypeFunction}
|
||||||
, refineFunc{"refine", refineFamilyFn}
|
, refineFunc{"refine", refineTypeFunction}
|
||||||
, singletonFunc{"singleton", singletonFamilyFn}
|
, singletonFunc{"singleton", singletonTypeFunction}
|
||||||
, unionFunc{"union", unionFamilyFn}
|
, unionFunc{"union", unionTypeFunction}
|
||||||
, intersectFunc{"intersect", intersectFamilyFn}
|
, intersectFunc{"intersect", intersectTypeFunction}
|
||||||
, keyofFunc{"keyof", keyofFamilyFn}
|
, keyofFunc{"keyof", keyofTypeFunction}
|
||||||
, rawkeyofFunc{"rawkeyof", rawkeyofFamilyFn}
|
, rawkeyofFunc{"rawkeyof", rawkeyofTypeFunction}
|
||||||
, indexFunc{"index", indexFamilyFn}
|
, indexFunc{"index", indexTypeFunction}
|
||||||
, rawgetFunc{"rawget", rawgetFamilyFn}
|
, rawgetFunc{"rawget", rawgetTypeFunction}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ bool TypeFunctionReductionGuesser::isFunctionGenericsSaturated(const FunctionTyp
|
|||||||
|
|
||||||
void TypeFunctionReductionGuesser::dumpGuesses()
|
void TypeFunctionReductionGuesser::dumpGuesses()
|
||||||
{
|
{
|
||||||
for (auto [tf, t] : familyReducesTo)
|
for (auto [tf, t] : functionReducesTo)
|
||||||
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());
|
||||||
for (auto [t, t_] : substitutable)
|
for (auto [t, t_] : substitutable)
|
||||||
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());
|
||||||
@ -175,7 +175,7 @@ TypeFunctionReductionGuessResult TypeFunctionReductionGuesser::guessTypeFunction
|
|||||||
|
|
||||||
toInfer.clear();
|
toInfer.clear();
|
||||||
cyclicInstances.clear();
|
cyclicInstances.clear();
|
||||||
familyReducesTo.clear();
|
functionReducesTo.clear();
|
||||||
substitutable.clear();
|
substitutable.clear();
|
||||||
|
|
||||||
return TypeFunctionReductionGuessResult{results, recommendedAnnotation};
|
return TypeFunctionReductionGuessResult{results, recommendedAnnotation};
|
||||||
@ -196,44 +196,44 @@ std::optional<TypeId> TypeFunctionReductionGuesser::guessType(TypeId arg)
|
|||||||
}
|
}
|
||||||
if (get<TypeFunctionInstanceType>(t))
|
if (get<TypeFunctionInstanceType>(t))
|
||||||
{
|
{
|
||||||
if (familyReducesTo.contains(t))
|
if (functionReducesTo.contains(t))
|
||||||
return familyReducesTo[t];
|
return functionReducesTo[t];
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeFunctionReductionGuesser::isNumericBinopFamily(const TypeFunctionInstanceType& instance)
|
bool TypeFunctionReductionGuesser::isNumericBinopFunction(const TypeFunctionInstanceType& instance)
|
||||||
{
|
{
|
||||||
return instance.family->name == "add" || instance.family->name == "sub" || instance.family->name == "mul" || instance.family->name == "div" ||
|
return instance.function->name == "add" || instance.function->name == "sub" || instance.function->name == "mul" || instance.function->name == "div" ||
|
||||||
instance.family->name == "idiv" || instance.family->name == "pow" || instance.family->name == "mod";
|
instance.function->name == "idiv" || instance.function->name == "pow" || instance.function->name == "mod";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeFunctionReductionGuesser::isComparisonFamily(const TypeFunctionInstanceType& instance)
|
bool TypeFunctionReductionGuesser::isComparisonFunction(const TypeFunctionInstanceType& instance)
|
||||||
{
|
{
|
||||||
return instance.family->name == "lt" || instance.family->name == "le" || instance.family->name == "eq";
|
return instance.function->name == "lt" || instance.function->name == "le" || instance.function->name == "eq";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeFunctionReductionGuesser::isOrAndFamily(const TypeFunctionInstanceType& instance)
|
bool TypeFunctionReductionGuesser::isOrAndFunction(const TypeFunctionInstanceType& instance)
|
||||||
{
|
{
|
||||||
return instance.family->name == "or" || instance.family->name == "and";
|
return instance.function->name == "or" || instance.function->name == "and";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeFunctionReductionGuesser::isNotFamily(const TypeFunctionInstanceType& instance)
|
bool TypeFunctionReductionGuesser::isNotFunction(const TypeFunctionInstanceType& instance)
|
||||||
{
|
{
|
||||||
return instance.family->name == "not";
|
return instance.function->name == "not";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeFunctionReductionGuesser::isLenFamily(const TypeFunctionInstanceType& instance)
|
bool TypeFunctionReductionGuesser::isLenFunction(const TypeFunctionInstanceType& instance)
|
||||||
{
|
{
|
||||||
return instance.family->name == "len";
|
return instance.function->name == "len";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeFunctionReductionGuesser::isUnaryMinus(const TypeFunctionInstanceType& instance)
|
bool TypeFunctionReductionGuesser::isUnaryMinus(const TypeFunctionInstanceType& instance)
|
||||||
{
|
{
|
||||||
return instance.family->name == "unm";
|
return instance.function->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 function instance, or a generic type
|
||||||
bool TypeFunctionReductionGuesser::operandIsAssignable(TypeId ty)
|
bool TypeFunctionReductionGuesser::operandIsAssignable(TypeId ty)
|
||||||
{
|
{
|
||||||
if (get<TypeFunctionInstanceType>(ty))
|
if (get<TypeFunctionInstanceType>(ty))
|
||||||
@ -257,8 +257,8 @@ std::optional<TypeId> TypeFunctionReductionGuesser::tryAssignOperandType(TypeId
|
|||||||
// 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<TypeFunctionInstanceType>(ty))
|
if (auto tfit = get<TypeFunctionInstanceType>(ty))
|
||||||
{
|
{
|
||||||
if (familyReducesTo.contains(ty))
|
if (functionReducesTo.contains(ty))
|
||||||
return {familyReducesTo[ty]};
|
return {functionReducesTo[ty]};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If ty is a generic, we need to check if we inferred a substitution
|
// If ty is a generic, we need to check if we inferred a substitution
|
||||||
@ -298,24 +298,24 @@ void TypeFunctionReductionGuesser::inferTypeFunctionSubstitutions(TypeId ty, con
|
|||||||
TypeFunctionInferenceResult 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 (isNumericBinopFunction(*instance))
|
||||||
result = inferNumericBinopFamily(instance);
|
result = inferNumericBinopFunction(instance);
|
||||||
else if (isComparisonFamily(*instance))
|
else if (isComparisonFunction(*instance))
|
||||||
result = inferComparisonFamily(instance);
|
result = inferComparisonFunction(instance);
|
||||||
else if (isOrAndFamily(*instance))
|
else if (isOrAndFunction(*instance))
|
||||||
result = inferOrAndFamily(instance);
|
result = inferOrAndFunction(instance);
|
||||||
else if (isNotFamily(*instance))
|
else if (isNotFunction(*instance))
|
||||||
result = inferNotFamily(instance);
|
result = inferNotFunction(instance);
|
||||||
else if (isLenFamily(*instance))
|
else if (isLenFunction(*instance))
|
||||||
result = inferLenFamily(instance);
|
result = inferLenFunction(instance);
|
||||||
else if (isUnaryMinus(*instance))
|
else if (isUnaryMinus(*instance))
|
||||||
result = inferUnaryMinusFamily(instance);
|
result = inferUnaryMinusFunction(instance);
|
||||||
else
|
else
|
||||||
result = {{}, builtins->unknownType};
|
result = {{}, builtins->unknownType};
|
||||||
|
|
||||||
TypeId resultInference = follow(result.familyResultInference);
|
TypeId resultInference = follow(result.functionResultInference);
|
||||||
if (!familyReducesTo.contains(resultInference))
|
if (!functionReducesTo.contains(resultInference))
|
||||||
familyReducesTo[ty] = resultInference;
|
functionReducesTo[ty] = resultInference;
|
||||||
|
|
||||||
for (size_t i = 0; i < instance->typeArguments.size(); i++)
|
for (size_t i = 0; i < instance->typeArguments.size(); i++)
|
||||||
{
|
{
|
||||||
@ -325,8 +325,8 @@ void TypeFunctionReductionGuesser::inferTypeFunctionSubstitutions(TypeId ty, con
|
|||||||
TypeId inference = follow(result.operandInference[i]);
|
TypeId inference = follow(result.operandInference[i]);
|
||||||
if (auto tfit = get<TypeFunctionInstanceType>(arg))
|
if (auto tfit = get<TypeFunctionInstanceType>(arg))
|
||||||
{
|
{
|
||||||
if (!familyReducesTo.contains(arg))
|
if (!functionReducesTo.contains(arg))
|
||||||
familyReducesTo.try_insert(arg, inference);
|
functionReducesTo.try_insert(arg, inference);
|
||||||
}
|
}
|
||||||
else if (auto gt = get<GenericType>(arg))
|
else if (auto gt = get<GenericType>(arg))
|
||||||
substitutable[arg] = inference;
|
substitutable[arg] = inference;
|
||||||
@ -334,17 +334,17 @@ void TypeFunctionReductionGuesser::inferTypeFunctionSubstitutions(TypeId ty, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNumericBinopFamily(const TypeFunctionInstanceType* instance)
|
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNumericBinopFunction(const TypeFunctionInstanceType* instance)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(instance->typeArguments.size() == 2);
|
LUAU_ASSERT(instance->typeArguments.size() == 2);
|
||||||
TypeFunctionInferenceResult defaultNumericBinopInference{{builtins->numberType, builtins->numberType}, builtins->numberType};
|
TypeFunctionInferenceResult defaultNumericBinopInference{{builtins->numberType, builtins->numberType}, builtins->numberType};
|
||||||
return defaultNumericBinopInference;
|
return defaultNumericBinopInference;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferComparisonFamily(const TypeFunctionInstanceType* instance)
|
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferComparisonFunction(const TypeFunctionInstanceType* instance)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(instance->typeArguments.size() == 2);
|
LUAU_ASSERT(instance->typeArguments.size() == 2);
|
||||||
// Comparison families are lt/le/eq.
|
// Comparison functions are lt/le/eq.
|
||||||
// Heuristic: these are type functions from t -> t -> bool
|
// Heuristic: these are type functions from t -> t -> bool
|
||||||
|
|
||||||
TypeId lhsTy = follow(instance->typeArguments[0]);
|
TypeId lhsTy = follow(instance->typeArguments[0]);
|
||||||
@ -365,7 +365,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferComparisonFamily(
|
|||||||
return comparisonInference(builtins->numberType);
|
return comparisonInference(builtins->numberType);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const TypeFunctionInstanceType* instance)
|
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFunction(const TypeFunctionInstanceType* instance)
|
||||||
{
|
{
|
||||||
|
|
||||||
LUAU_ASSERT(instance->typeArguments.size() == 2);
|
LUAU_ASSERT(instance->typeArguments.size() == 2);
|
||||||
@ -384,7 +384,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const
|
|||||||
bool lhsTruthy = lty ? lty->isTruthy() : false;
|
bool lhsTruthy = lty ? lty->isTruthy() : false;
|
||||||
bool rhsTruthy = rty ? rty->isTruthy() : false;
|
bool rhsTruthy = rty ? rty->isTruthy() : false;
|
||||||
// If at the end, we still don't have good substitutions, return the default type
|
// If at the end, we still don't have good substitutions, return the default type
|
||||||
if (instance->family->name == "or")
|
if (instance->function->name == "or")
|
||||||
{
|
{
|
||||||
if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy))
|
if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy))
|
||||||
return defaultAndOrInference;
|
return defaultAndOrInference;
|
||||||
@ -398,7 +398,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const
|
|||||||
return {{builtins->unknownType, rhsTy}, rhsTy};
|
return {{builtins->unknownType, rhsTy}, rhsTy};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance->family->name == "and")
|
if (instance->function->name == "and")
|
||||||
{
|
{
|
||||||
|
|
||||||
if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy))
|
if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy))
|
||||||
@ -416,7 +416,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const
|
|||||||
return defaultAndOrInference;
|
return defaultAndOrInference;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNotFamily(const TypeFunctionInstanceType* instance)
|
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNotFunction(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 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNotFamily(const T
|
|||||||
return {{opTy}, builtins->booleanType};
|
return {{opTy}, builtins->booleanType};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferLenFamily(const TypeFunctionInstanceType* instance)
|
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferLenFunction(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 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferLenFamily(const T
|
|||||||
return {{opTy}, builtins->numberType};
|
return {{opTy}, builtins->numberType};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferUnaryMinusFamily(const TypeFunctionInstanceType* instance)
|
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferUnaryMinusFunction(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]);
|
||||||
|
@ -92,19 +92,19 @@ Unifier2::Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes,
|
|||||||
, ice(ice)
|
, ice(ice)
|
||||||
, limits(TypeCheckLimits{}) // TODO: typecheck limits in unifier2
|
, limits(TypeCheckLimits{}) // TODO: typecheck limits in unifier2
|
||||||
, recursionLimit(FInt::LuauTypeInferRecursionLimit)
|
, recursionLimit(FInt::LuauTypeInferRecursionLimit)
|
||||||
, uninhabitedTypeFamilies(nullptr)
|
, uninhabitedTypeFunctions(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
DenseHashSet<const void*>* uninhabitedTypeFamilies)
|
DenseHashSet<const void*>* uninhabitedTypeFunctions)
|
||||||
: arena(arena)
|
: arena(arena)
|
||||||
, builtinTypes(builtinTypes)
|
, builtinTypes(builtinTypes)
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
, ice(ice)
|
, ice(ice)
|
||||||
, limits(TypeCheckLimits{}) // TODO: typecheck limits in unifier2
|
, limits(TypeCheckLimits{}) // TODO: typecheck limits in unifier2
|
||||||
, recursionLimit(FInt::LuauTypeInferRecursionLimit)
|
, recursionLimit(FInt::LuauTypeInferRecursionLimit)
|
||||||
, uninhabitedTypeFamilies(uninhabitedTypeFamilies)
|
, uninhabitedTypeFunctions(uninhabitedTypeFunctions)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ bool Unifier2::unify(TypeId subTy, TypeId superTy)
|
|||||||
// - *blocked* <: unknown
|
// - *blocked* <: unknown
|
||||||
if ((isIrresolvable(subTy) || isIrresolvable(superTy)) && !get<NeverType>(subTy) && !get<UnknownType>(superTy))
|
if ((isIrresolvable(subTy) || isIrresolvable(superTy)) && !get<NeverType>(subTy) && !get<UnknownType>(superTy))
|
||||||
{
|
{
|
||||||
if (uninhabitedTypeFamilies && (uninhabitedTypeFamilies->contains(subTy) || uninhabitedTypeFamilies->contains(superTy)))
|
if (uninhabitedTypeFunctions && (uninhabitedTypeFunctions->contains(subTy) || uninhabitedTypeFunctions->contains(superTy)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
incompleteSubtypes.push_back(SubtypeConstraint{subTy, superTy});
|
incompleteSubtypes.push_back(SubtypeConstraint{subTy, superTy});
|
||||||
@ -539,7 +539,7 @@ bool Unifier2::unify(TypePackId subTp, TypePackId superTp)
|
|||||||
|
|
||||||
if (isIrresolvable(subTp) || isIrresolvable(superTp))
|
if (isIrresolvable(subTp) || isIrresolvable(superTp))
|
||||||
{
|
{
|
||||||
if (uninhabitedTypeFamilies && (uninhabitedTypeFamilies->contains(subTp) || uninhabitedTypeFamilies->contains(superTp)))
|
if (uninhabitedTypeFunctions && (uninhabitedTypeFunctions->contains(subTp) || uninhabitedTypeFunctions->contains(superTp)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
incompleteSubtypes.push_back(PackSubtypeConstraint{subTp, superTp});
|
incompleteSubtypes.push_back(PackSubtypeConstraint{subTp, superTp});
|
||||||
|
@ -11,7 +11,6 @@ Id UnionFind::makeSet()
|
|||||||
Id id{parents.size()};
|
Id id{parents.size()};
|
||||||
parents.push_back(id);
|
parents.push_back(id);
|
||||||
ranks.push_back(0);
|
ranks.push_back(0);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ local x: Account = 5
|
|||||||
CHECK_EQ("Type 'number' could not be converted into 'Account'", toString(result.errors[0]));
|
CHECK_EQ("Type 'number' could not be converted into 'Account'", toString(result.errors[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_family_errors")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_function_errors")
|
||||||
{
|
{
|
||||||
frontend.options.retainFullTypeGraphs = false;
|
frontend.options.retainFullTypeGraphs = false;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_family_errors")
|
|||||||
CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0]));
|
CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "unary_op_type_family_errors")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "unary_op_type_function_errors")
|
||||||
{
|
{
|
||||||
frontend.options.retainFullTypeGraphs = false;
|
frontend.options.retainFullTypeGraphs = false;
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "nocheck_cycle_used_by_checked")
|
|||||||
REQUIRE(bool(cExports));
|
REQUIRE(bool(cExports));
|
||||||
|
|
||||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
CHECK_EQ("{ a: any, b: any }", toString(*cExports));
|
CHECK_EQ("{ a: { hello: any }, b: { hello: any } }", toString(*cExports));
|
||||||
else
|
else
|
||||||
CHECK_EQ("{| a: any, b: any |}", toString(*cExports));
|
CHECK_EQ("{| a: any, b: any |}", toString(*cExports));
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ end
|
|||||||
REQUIRE(0 == result.warnings.size());
|
REQUIRE(0 == result.warnings.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "type_family_fully_reduces")
|
TEST_CASE_FIXTURE(Fixture, "type_function_fully_reduces")
|
||||||
{
|
{
|
||||||
LintResult result = lint(R"(
|
LintResult result = lint(R"(
|
||||||
function fib(n)
|
function fib(n)
|
||||||
|
@ -404,8 +404,16 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "error_suppression")
|
|||||||
CHECK(!isSubtype(any, str));
|
CHECK(!isSubtype(any, str));
|
||||||
CHECK(isSubtype(str, any));
|
CHECK(isSubtype(str, any));
|
||||||
|
|
||||||
|
// We have added this as an exception - the set of inhabitants of any is exactly the set of inhabitants of unknown (since error has no
|
||||||
|
// inhabitants). any = err | unknown, so under semantic subtyping, {} U unknown = unknown
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
{
|
||||||
|
CHECK(isSubtype(any, unk));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
CHECK(!isSubtype(any, unk));
|
CHECK(!isSubtype(any, unk));
|
||||||
CHECK(isSubtype(unk, any));
|
}
|
||||||
|
|
||||||
CHECK(!isSubtype(err, str));
|
CHECK(!isSubtype(err, str));
|
||||||
CHECK(!isSubtype(str, err));
|
CHECK(!isSubtype(str, err));
|
||||||
|
@ -470,9 +470,11 @@ TEST_CASE_FIXTURE(SubtypeFixture, "variadic_subpath_in_pack")
|
|||||||
CHECK(!result.isSubtype);
|
CHECK(!result.isSubtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "any <!: unknown")
|
TEST_CASE_FIXTURE(SubtypeFixture, "any <: unknown")
|
||||||
{
|
{
|
||||||
CHECK_IS_NOT_SUBTYPE(builtinTypes->anyType, builtinTypes->unknownType);
|
// We have added this as an exception - the set of inhabitants of any is exactly the set of inhabitants of unknown (since error has no
|
||||||
|
// inhabitants). any = err | unknown, so under semantic subtyping, {} U unknown = unknown
|
||||||
|
CHECK_IS_SUBTYPE(builtinTypes->anyType, builtinTypes->unknownType);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "number? <: unknown")
|
TEST_CASE_FIXTURE(SubtypeFixture, "number? <: unknown")
|
||||||
@ -990,11 +992,15 @@ TEST_CASE_FIXTURE(SubtypeFixture, "semantic_subtyping_disj")
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <: t2 where t2 = {trim: (t2) -> string}")
|
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <: t2 where t2 = {trim: (t2) -> string}")
|
||||||
{
|
{
|
||||||
TypeId t1 = cyclicTable([&](TypeId ty, TableType* tt) {
|
TypeId t1 = cyclicTable(
|
||||||
|
[&](TypeId ty, TableType* tt)
|
||||||
|
{
|
||||||
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
|
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
|
||||||
});
|
});
|
||||||
|
|
||||||
TypeId t2 = cyclicTable([&](TypeId ty, TableType* tt) {
|
TypeId t2 = cyclicTable(
|
||||||
|
[&](TypeId ty, TableType* tt)
|
||||||
|
{
|
||||||
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
|
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1003,11 +1009,15 @@ TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <: t2 wh
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <!: t2 where t2 = {trim: (t2) -> t2}")
|
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <!: t2 where t2 = {trim: (t2) -> t2}")
|
||||||
{
|
{
|
||||||
TypeId t1 = cyclicTable([&](TypeId ty, TableType* tt) {
|
TypeId t1 = cyclicTable(
|
||||||
|
[&](TypeId ty, TableType* tt)
|
||||||
|
{
|
||||||
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
|
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
|
||||||
});
|
});
|
||||||
|
|
||||||
TypeId t2 = cyclicTable([&](TypeId ty, TableType* tt) {
|
TypeId t2 = cyclicTable(
|
||||||
|
[&](TypeId ty, TableType* tt)
|
||||||
|
{
|
||||||
tt->props["trim"] = fn({ty}, {ty});
|
tt->props["trim"] = fn({ty}, {ty});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1016,11 +1026,15 @@ TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <!: t2 w
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> t1} <!: t2 where t2 = {trim: (t2) -> string}")
|
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> t1} <!: t2 where t2 = {trim: (t2) -> string}")
|
||||||
{
|
{
|
||||||
TypeId t1 = cyclicTable([&](TypeId ty, TableType* tt) {
|
TypeId t1 = cyclicTable(
|
||||||
|
[&](TypeId ty, TableType* tt)
|
||||||
|
{
|
||||||
tt->props["trim"] = fn({ty}, {ty});
|
tt->props["trim"] = fn({ty}, {ty});
|
||||||
});
|
});
|
||||||
|
|
||||||
TypeId t2 = cyclicTable([&](TypeId ty, TableType* tt) {
|
TypeId t2 = cyclicTable(
|
||||||
|
[&](TypeId ty, TableType* tt)
|
||||||
|
{
|
||||||
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
|
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1286,7 +1300,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "<T>({ x: T }) -> T <: ({ method: <T>({ x: T }
|
|||||||
CHECK_IS_SUBTYPE(tableToPropType, otherType);
|
CHECK_IS_SUBTYPE(tableToPropType, otherType);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "subtyping_reasonings_to_follow_a_reduced_type_family_instance")
|
TEST_CASE_FIXTURE(SubtypeFixture, "subtyping_reasonings_to_follow_a_reduced_type_function_instance")
|
||||||
{
|
{
|
||||||
TypeId longTy = arena.addType(UnionType{{builtinTypes->booleanType, builtinTypes->bufferType, builtinTypes->classType, builtinTypes->functionType,
|
TypeId longTy = arena.addType(UnionType{{builtinTypes->booleanType, builtinTypes->bufferType, builtinTypes->classType, builtinTypes->functionType,
|
||||||
builtinTypes->numberType, builtinTypes->stringType, builtinTypes->tableType, builtinTypes->threadType}});
|
builtinTypes->numberType, builtinTypes->stringType, builtinTypes->tableType, builtinTypes->threadType}});
|
||||||
|
@ -15,14 +15,14 @@ using namespace Luau;
|
|||||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
|
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
|
||||||
|
|
||||||
struct FamilyFixture : Fixture
|
struct TypeFunctionFixture : Fixture
|
||||||
{
|
{
|
||||||
TypeFunction swapFamily;
|
TypeFunction swapFunction;
|
||||||
|
|
||||||
FamilyFixture()
|
TypeFunctionFixture()
|
||||||
: Fixture(true, false)
|
: Fixture(true, false)
|
||||||
{
|
{
|
||||||
swapFamily = TypeFunction{/* name */ "Swap",
|
swapFunction = 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<TypeFunctionContext> ctx) -> TypeFunctionReductionResult<TypeId> {
|
NotNull<TypeFunctionContext> ctx) -> TypeFunctionReductionResult<TypeId> {
|
||||||
@ -54,14 +54,14 @@ 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(TypeFunctionInstanceType{NotNull{&swapFamily}, {t}, {}})};
|
TypeFun{{genericT}, frontend.globals.globalTypes.addType(TypeFunctionInstanceType{NotNull{&swapFunction}, {t}, {}})};
|
||||||
freeze(frontend.globals.globalTypes);
|
freeze(frontend.globals.globalTypes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeFunctionTests");
|
TEST_SUITE_BEGIN("TypeFunctionTests");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FamilyFixture, "basic_type_family")
|
TEST_CASE_FIXTURE(TypeFunctionFixture, "basic_type_function")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -83,7 +83,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "basic_type_family")
|
|||||||
CHECK("Type function 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(TypeFunctionFixture, "function_as_fn_ret")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -102,7 +102,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_ret")
|
|||||||
CHECK("Type function 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(TypeFunctionFixture, "function_as_fn_arg")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -121,7 +121,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_arg")
|
|||||||
CHECK("Type function 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(TypeFunctionFixture, "resolve_deep_functions")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -134,7 +134,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "resolve_deep_families")
|
|||||||
CHECK("number" == toString(requireType("x")));
|
CHECK("number" == toString(requireType("x")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FamilyFixture, "unsolvable_family")
|
TEST_CASE_FIXTURE(TypeFunctionFixture, "unsolvable_function")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -152,7 +152,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "unsolvable_family")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FamilyFixture, "table_internal_families")
|
TEST_CASE_FIXTURE(TypeFunctionFixture, "table_internal_functions")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -172,7 +172,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "table_internal_families")
|
|||||||
CHECK(toString(result.errors[0]) == "Type function 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(TypeFunctionFixture, "function_internal_functions")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -193,7 +193,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "function_internal_families")
|
|||||||
CHECK(toString(result.errors[0]) == "Type function 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_function_at_work")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -216,7 +216,7 @@ TEST_CASE_FIXTURE(Fixture, "add_family_at_work")
|
|||||||
CHECK(toString(result.errors[1]) == "Type function 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_function_at_work")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -229,7 +229,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_add_family_at_work")
|
|||||||
CHECK(toString(requireTypeAlias("T")) == "number");
|
CHECK(toString(requireTypeAlias("T")) == "number");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "mul_family_with_union_of_multiplicatives")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "mul_function_with_union_of_multiplicatives")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -252,7 +252,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "mul_family_with_union_of_multiplicatives")
|
|||||||
CHECK(toString(requireTypeAlias("T")) == "Vec2 | Vec3");
|
CHECK(toString(requireTypeAlias("T")) == "Vec2 | Vec3");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "mul_family_with_union_of_multiplicatives_2")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "mul_function_with_union_of_multiplicatives_2")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -272,7 +272,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "mul_family_with_union_of_multiplicatives_2")
|
|||||||
CHECK(toString(requireTypeAlias("T")) == "Vec3");
|
CHECK(toString(requireTypeAlias("T")) == "Vec3");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "internal_families_raise_errors")
|
TEST_CASE_FIXTURE(Fixture, "internal_functions_raise_errors")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -288,7 +288,7 @@ TEST_CASE_FIXTURE(Fixture, "internal_families_raise_errors")
|
|||||||
"signature; this construct cannot be type-checked at this time");
|
"signature; this construct cannot be type-checked at this time");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_families_can_be_shadowed")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "type_functions_can_be_shadowed")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -313,7 +313,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_families_can_be_shadowed")
|
|||||||
CHECK(toString(requireType("plus")) == "<a, b>(a, b) -> add<a, b>");
|
CHECK(toString(requireType("plus")) == "<a, b>(a, b) -> add<a, b>");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_families_inhabited_with_normalization")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "type_functions_inhabited_with_normalization")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -331,7 +331,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_families_inhabited_with_normalization")
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -352,7 +352,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_works")
|
|||||||
CHECK_EQ("\"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
CHECK_EQ("\"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_works_with_metatables")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -375,7 +375,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_works_with_metatables")
|
|||||||
CHECK_EQ("\"w\" | \"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
CHECK_EQ("\"w\" | \"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_errors_if_it_has_nontable_part")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_errors_if_it_has_nontable_part")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -393,7 +393,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_errors_if_it_has_nontable_
|
|||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_string_indexer")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -421,7 +421,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_string_indexer")
|
|||||||
CHECK_EQ("\"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
CHECK_EQ("\"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_common_subset_if_union_of_differing_tables")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_common_subset_if_union_of_differing_tables")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -442,7 +442,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_common_subset_if_union_of_
|
|||||||
CHECK_EQ("\"y\" | \"z\"", toString(tpm->givenTp));
|
CHECK_EQ("\"y\" | \"z\"", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_never_for_empty_table")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_never_for_empty_table")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -457,7 +457,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_never_for_empty_table")
|
|||||||
CHECK(toString(requireType("foo")) == "never");
|
CHECK(toString(requireType("foo")) == "never");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_works")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -478,7 +478,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_works")
|
|||||||
CHECK_EQ("\"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
CHECK_EQ("\"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_ignores_metatables")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_ignores_metatables")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -501,7 +501,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_ignores_metatables")
|
|||||||
CHECK_EQ("\"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
CHECK_EQ("\"x\" | \"y\" | \"z\"", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_errors_if_it_has_nontable_part")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_errors_if_it_has_nontable_part")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -519,7 +519,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_errors_if_it_has_nontab
|
|||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_common_subset_if_union_of_differing_tables")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_common_subset_if_union_of_differing_tables")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -540,7 +540,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_common_subset_if_union_
|
|||||||
CHECK_EQ("\"y\" | \"z\"", toString(tpm->givenTp));
|
CHECK_EQ("\"y\" | \"z\"", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_never_for_empty_table")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_never_for_empty_table")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -555,7 +555,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_never_for_empty_table")
|
|||||||
CHECK(toString(requireType("foo")) == "never");
|
CHECK(toString(requireType("foo")) == "never");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_works_on_classes")
|
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_works_on_classes")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -575,7 +575,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_works_on_classes")
|
|||||||
CHECK_EQ("\"BaseField\" | \"BaseMethod\" | \"Touched\"", toString(tpm->givenTp));
|
CHECK_EQ("\"BaseField\" | \"BaseMethod\" | \"Touched\"", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_errors_if_it_has_nonclass_part")
|
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_errors_if_it_has_nonclass_part")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -592,7 +592,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_errors_if_it_has_nonclass_par
|
|||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_common_subset_if_union_of_differing_classes")
|
TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_common_subset_if_union_of_differing_classes")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -606,7 +606,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_common_subset_if_union_of_dif
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ClassFixture, "binary_type_family_works_with_default_argument")
|
TEST_CASE_FIXTURE(ClassFixture, "binary_type_function_works_with_default_argument")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -699,7 +699,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_oss_crash_gh1161")
|
|||||||
CHECK(get<FunctionExitsWithoutReturning>(result.errors[0]));
|
CHECK(get<FunctionExitsWithoutReturning>(result.errors[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FamilyFixture, "fuzzer_numeric_binop_doesnt_assert_on_generalizeFreeType")
|
TEST_CASE_FIXTURE(TypeFunctionFixture, "fuzzer_numeric_binop_doesnt_assert_on_generalizeFreeType")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
Module 'l0':
|
Module 'l0':
|
||||||
@ -714,7 +714,7 @@ _(setmetatable(_,{[...]=_,}))
|
|||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_concat_family_at_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_concat_function_at_work")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -827,7 +827,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "ensure_equivalence_with_distributivity")
|
|||||||
CHECK(toString(requireTypeAlias("U")) == "A | A | B | B");
|
CHECK(toString(requireTypeAlias("U")) == "A | A | B | B");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "we_shouldnt_warn_that_a_reducible_type_family_is_uninhabited")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "we_shouldnt_warn_that_a_reducible_type_function_is_uninhabited")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -857,7 +857,7 @@ end
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -880,7 +880,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works")
|
|||||||
CHECK_EQ("string", toString(tpm->givenTp));
|
CHECK_EQ("string", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_array")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_array")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -895,7 +895,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_array")
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_generic_types")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_generic_types")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -917,7 +917,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_generic_types")
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_errors_w_bad_indexer")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_errors_w_bad_indexer")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -933,7 +933,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_errors_w_bad_indexer")
|
|||||||
CHECK(toString(result.errors[1]) == "Property 'boolean' does not exist on type 'MyObject'");
|
CHECK(toString(result.errors[1]) == "Property 'boolean' does not exist on type 'MyObject'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_errors_w_var_indexer")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_errors_w_var_indexer")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -950,7 +950,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_errors_w_var_indexer")
|
|||||||
CHECK(toString(result.errors[1]) == "Unknown type 'key'");
|
CHECK(toString(result.errors[1]) == "Unknown type 'key'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_union_type_indexer")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_union_type_indexer")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -968,7 +968,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_union_type_indexer
|
|||||||
CHECK(toString(result.errors[0]) == "Property '\"a\" | \"d\"' does not exist on type 'MyObject'");
|
CHECK(toString(result.errors[0]) == "Property '\"a\" | \"d\"' does not exist on type 'MyObject'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_union_type_indexee")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_union_type_indexee")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -987,7 +987,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_union_type_indexee
|
|||||||
CHECK(toString(result.errors[0]) == "Property '\"b\"' does not exist on type 'MyObject | MyObject2'");
|
CHECK(toString(result.errors[0]) == "Property '\"b\"' does not exist on type 'MyObject | MyObject2'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_rfc_alternative_section")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_rfc_alternative_section")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1005,7 +1005,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_rfc_alternative_section")
|
|||||||
CHECK(toString(result.errors[0]) == "Property '\"b\"' does not exist on type 'MyObject'");
|
CHECK(toString(result.errors[0]) == "Property '\"b\"' does not exist on type 'MyObject'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ClassFixture, "index_type_family_works_on_classes")
|
TEST_CASE_FIXTURE(ClassFixture, "index_type_function_works_on_classes")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1019,7 +1019,7 @@ TEST_CASE_FIXTURE(ClassFixture, "index_type_family_works_on_classes")
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_index_metatables")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_index_metatables")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1045,7 +1045,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_index_metatables")
|
|||||||
CHECK(toString(result.errors[0]) == "Property '\"Car\"' does not exist on type 'exampleClass2'");
|
CHECK(toString(result.errors[0]) == "Property '\"Car\"' does not exist on type 'exampleClass2'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1067,7 +1067,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works")
|
|||||||
CHECK_EQ("string", toString(tpm->givenTp));
|
CHECK_EQ("string", toString(tpm->givenTp));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_array")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_array")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1081,7 +1081,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_array")
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_errors_w_var_indexer")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_errors_w_var_indexer")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1097,7 +1097,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_errors_w_var_indexer")
|
|||||||
CHECK(toString(result.errors[1]) == "Unknown type 'key'");
|
CHECK(toString(result.errors[1]) == "Unknown type 'key'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_union_type_indexer")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_union_type_indexer")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1113,7 +1113,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_union_type_indexe
|
|||||||
CHECK(toString(result.errors[0]) == "Property '\"a\" | \"d\"' does not exist on type 'MyObject'");
|
CHECK(toString(result.errors[0]) == "Property '\"a\" | \"d\"' does not exist on type 'MyObject'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_union_type_indexee")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_union_type_indexee")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1130,7 +1130,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_union_type_indexe
|
|||||||
CHECK(toString(result.errors[0]) == "Property '\"b\"' does not exist on type 'MyObject | MyObject2'");
|
CHECK(toString(result.errors[0]) == "Property '\"b\"' does not exist on type 'MyObject | MyObject2'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_index_metatables")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_index_metatables")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1150,7 +1150,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_index_metatables"
|
|||||||
CHECK(toString(result.errors[1]) == "Property '\"Bar\" | \"Foo\"' does not exist on type 'exampleClass3'");
|
CHECK(toString(result.errors[1]) == "Property '\"Bar\" | \"Foo\"' does not exist on type 'exampleClass3'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ClassFixture, "rawget_type_family_errors_w_classes")
|
TEST_CASE_FIXTURE(ClassFixture, "rawget_type_function_errors_w_classes")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
|
@ -32,6 +32,9 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any")
|
|||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK("any?" == toString(requireType("a")));
|
||||||
|
else
|
||||||
CHECK(builtinTypes->anyType == requireType("a"));
|
CHECK(builtinTypes->anyType == requireType("a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +53,9 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any2")
|
|||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK("any?" == toString(requireType("a")));
|
||||||
|
else
|
||||||
CHECK("any" == toString(requireType("a")));
|
CHECK("any" == toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +72,9 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any")
|
|||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK("any?" == toString(requireType("a")));
|
||||||
|
else
|
||||||
CHECK("any" == toString(requireType("a")));
|
CHECK("any" == toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +89,9 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any2")
|
|||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK("any?" == toString(requireType("a")));
|
||||||
|
else
|
||||||
CHECK("any" == toString(requireType("a")));
|
CHECK("any" == toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +108,9 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any_pack")
|
|||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK("any?" == toString(requireType("a")));
|
||||||
|
else
|
||||||
CHECK("any" == toString(requireType("a")));
|
CHECK("any" == toString(requireType("a")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,6 +306,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_comp
|
|||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK_EQ("any?", toString(requireType("b")));
|
||||||
|
else
|
||||||
CHECK_EQ("any", toString(requireType("b")));
|
CHECK_EQ("any", toString(requireType("b")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1424,7 +1424,7 @@ TEST_CASE_FIXTURE(Fixture, "missing_generic_type_parameter")
|
|||||||
REQUIRE(get<UnknownSymbol>(result.errors[1]));
|
REQUIRE(get<UnknownSymbol>(result.errors[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_type_families_work_in_subtyping")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_type_functions_work_in_subtyping")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
|
@ -745,7 +745,7 @@ TEST_CASE_FIXTURE(Fixture, "strict_binary_op_where_lhs_unknown")
|
|||||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
{
|
{
|
||||||
LUAU_REQUIRE_ERROR_COUNT(ops.size(), result);
|
LUAU_REQUIRE_ERROR_COUNT(ops.size(), result);
|
||||||
CHECK_EQ("Type family instance Add<a, b> depends on generic function parameters but does not appear in the function signature; this "
|
CHECK_EQ("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",
|
"construct cannot be type-checked at this time",
|
||||||
toString(result.errors[0]));
|
toString(result.errors[0]));
|
||||||
CHECK_EQ("Unknown type used in - operation; consider adding a type annotation to 'a'", toString(result.errors[1]));
|
CHECK_EQ("Unknown type used in - operation; consider adding a type annotation to 'a'", toString(result.errors[1]));
|
||||||
@ -1456,7 +1456,7 @@ return startsWith
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "add_type_family_works")
|
TEST_CASE_FIXTURE(Fixture, "add_type_function_works")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -1473,7 +1473,7 @@ TEST_CASE_FIXTURE(Fixture, "add_type_family_works")
|
|||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK(toString(requireType("a")) == "number");
|
CHECK(toString(requireType("a")) == "number");
|
||||||
CHECK(toString(requireType("b")) == "Add<string, string>");
|
CHECK(toString(requireType("b")) == "Add<string, string>");
|
||||||
CHECK(toString(result.errors[0]) == "Type family instance Add<string, string> is uninhabited");
|
CHECK(toString(result.errors[0]) == "Type function instance Add<string, string> is uninhabited");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "normalize_strings_comparison")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "normalize_strings_comparison")
|
||||||
|
@ -25,7 +25,7 @@ LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError)
|
|||||||
|
|
||||||
TEST_SUITE_BEGIN("TableTests");
|
TEST_SUITE_BEGIN("TableTests");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_shouldnt_seal_table_in_len_family_fn")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_shouldnt_seal_table_in_len_function_fn")
|
||||||
{
|
{
|
||||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
return;
|
return;
|
||||||
@ -4579,4 +4579,27 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_table_assertion_crash")
|
|||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "table::insert_should_not_report_errors_when_correct_overload_is_picked")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type cs = { GetTagged : (cs, string) -> any}
|
||||||
|
local destroyQueue: {any} = {} -- pair of (time, coin)
|
||||||
|
local tick : () -> any
|
||||||
|
local CS : cs
|
||||||
|
local DESTROY_DELAY
|
||||||
|
local function SpawnCoin()
|
||||||
|
local spawns = CS:GetTagged('CoinSpawner')
|
||||||
|
local n : any
|
||||||
|
local StartPos = spawns[n].CFrame
|
||||||
|
local Coin = script.Coin:Clone()
|
||||||
|
Coin.CFrame = StartPos
|
||||||
|
Coin.Parent = workspace.Coins
|
||||||
|
|
||||||
|
table.insert(destroyQueue, {tick() + DESTROY_DELAY, Coin})
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -39,7 +39,6 @@ Differ.negation
|
|||||||
FrontendTest.environments
|
FrontendTest.environments
|
||||||
FrontendTest.imported_table_modification_2
|
FrontendTest.imported_table_modification_2
|
||||||
FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded
|
FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded
|
||||||
FrontendTest.nocheck_cycle_used_by_checked
|
|
||||||
FrontendTest.trace_requires_in_nonstrict_mode
|
FrontendTest.trace_requires_in_nonstrict_mode
|
||||||
GenericsTests.apply_type_function_nested_generics1
|
GenericsTests.apply_type_function_nested_generics1
|
||||||
GenericsTests.better_mismatch_error_messages
|
GenericsTests.better_mismatch_error_messages
|
||||||
@ -54,7 +53,7 @@ GenericsTests.generic_argument_count_too_few
|
|||||||
GenericsTests.generic_argument_count_too_many
|
GenericsTests.generic_argument_count_too_many
|
||||||
GenericsTests.generic_factories
|
GenericsTests.generic_factories
|
||||||
GenericsTests.generic_functions_in_types
|
GenericsTests.generic_functions_in_types
|
||||||
GenericsTests.generic_type_families_work_in_subtyping
|
GenericsTests.generic_type_functions_work_in_subtyping
|
||||||
GenericsTests.generic_type_pack_parentheses
|
GenericsTests.generic_type_pack_parentheses
|
||||||
GenericsTests.generic_type_pack_unification1
|
GenericsTests.generic_type_pack_unification1
|
||||||
GenericsTests.generic_type_pack_unification2
|
GenericsTests.generic_type_pack_unification2
|
||||||
@ -246,18 +245,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
|
||||||
TypeFunctionTests.add_family_at_work
|
TypeFunctionTests.add_function_at_work
|
||||||
TypeFunctionTests.cyclic_add_family_at_work
|
TypeFunctionTests.cyclic_add_function_at_work
|
||||||
TypeFunctionTests.cyclic_concat_family_at_work
|
TypeFunctionTests.cyclic_concat_function_at_work
|
||||||
TypeFunctionTests.didnt_quite_exceed_distributivity_limits
|
TypeFunctionTests.didnt_quite_exceed_distributivity_limits
|
||||||
TypeFunctionTests.ensure_equivalence_with_distributivity
|
TypeFunctionTests.ensure_equivalence_with_distributivity
|
||||||
TypeFunctionTests.family_as_fn_arg
|
TypeFunctionTests.function_as_fn_arg
|
||||||
TypeFunctionTests.index_type_family_works_w_generic_types
|
TypeFunctionTests.index_type_function_works_w_generic_types
|
||||||
TypeFunctionTests.internal_families_raise_errors
|
TypeFunctionTests.internal_functions_raise_errors
|
||||||
TypeFunctionTests.keyof_oss_crash_gh1161
|
TypeFunctionTests.keyof_oss_crash_gh1161
|
||||||
TypeFunctionTests.mul_family_with_union_of_multiplicatives
|
TypeFunctionTests.mul_function_with_union_of_multiplicatives
|
||||||
TypeFunctionTests.mul_family_with_union_of_multiplicatives_2
|
TypeFunctionTests.mul_function_with_union_of_multiplicatives_2
|
||||||
TypeFunctionTests.unsolvable_family
|
TypeFunctionTests.unsolvable_function
|
||||||
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,12 +276,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.for_in_loop_iterator_is_any
|
|
||||||
TypeInferAnyError.for_in_loop_iterator_is_any2
|
|
||||||
TypeInferAnyError.for_in_loop_iterator_is_any_pack
|
|
||||||
TypeInferAnyError.for_in_loop_iterator_returns_any
|
|
||||||
TypeInferAnyError.for_in_loop_iterator_returns_any2
|
|
||||||
TypeInferAnyError.replace_every_free_type_when_unifying_a_complex_function_with_any
|
|
||||||
TypeInferClasses.callable_classes
|
TypeInferClasses.callable_classes
|
||||||
TypeInferClasses.cannot_unify_class_instance_with_primitive
|
TypeInferClasses.cannot_unify_class_instance_with_primitive
|
||||||
TypeInferClasses.class_type_mismatch_with_name_conflict
|
TypeInferClasses.class_type_mismatch_with_name_conflict
|
||||||
@ -370,7 +363,7 @@ TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2
|
|||||||
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon
|
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon
|
||||||
TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory
|
TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory
|
||||||
TypeInferOOP.promise_type_error_too_complex
|
TypeInferOOP.promise_type_error_too_complex
|
||||||
TypeInferOperators.add_type_family_works
|
TypeInferOperators.add_type_function_works
|
||||||
TypeInferOperators.cli_38355_recursive_union
|
TypeInferOperators.cli_38355_recursive_union
|
||||||
TypeInferOperators.compound_assign_result_must_be_compatible_with_var
|
TypeInferOperators.compound_assign_result_must_be_compatible_with_var
|
||||||
TypeInferOperators.concat_op_on_free_lhs_and_string_rhs
|
TypeInferOperators.concat_op_on_free_lhs_and_string_rhs
|
||||||
|
Loading…
Reference in New Issue
Block a user