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:
Vighnesh-V 2024-07-19 11:20:47 -07:00 committed by GitHub
parent 2874ca9e86
commit a7299c3f0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 463 additions and 384 deletions

View File

@ -371,7 +371,7 @@ private:
std::vector<std::optional<TypeId>> getExpectedCallTypesForFunctionOverloads(const TypeId fnType);
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.

View File

@ -91,8 +91,8 @@ struct ConstraintSolver
// A mapping from free types to the number of unresolved constraints that mention them.
DenseHashMap<TypeId, size_t> unresolvedConstraints{{}};
// Irreducible/uninhabited type families or type pack families.
DenseHashSet<const void*> uninhabitedTypeFamilies{{}};
// Irreducible/uninhabited type functions or type pack functions.
DenseHashSet<const void*> uninhabitedTypeFunctions{{}};
// The set of types that will definitely be unchanged by generalization.
DenseHashSet<TypeId> generalizedTypes_{nullptr};
@ -107,7 +107,7 @@ struct ConstraintSolver
DcrLogger* logger;
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,
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();
@ -345,7 +345,7 @@ public:
/**
* 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
**/
void reproduceConstraints(NotNull<Scope> scope, const Location& location, const Substitution& subst);

View File

@ -85,7 +85,7 @@ struct SolveResult
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.
SolveResult solveFunctionCall(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Normalizer> normalizer,

View File

@ -219,8 +219,8 @@ private:
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 TypeFunctionInstanceType* subFamilyInstance, const TypeId superTy);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFamilyInstance);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFunctionInstance, const TypeId superTy);
SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFunctionInstance);
bool bindGeneric(SubtypingEnvironment& env, TypeId subTp, TypeId superTp);
bool bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp);
@ -228,7 +228,7 @@ private:
template<typename T, typename Container>
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(TypePackId tp);

View File

@ -540,27 +540,27 @@ struct ClassType
*/
struct TypeFunctionInstanceType
{
NotNull<const TypeFunction> family;
NotNull<const TypeFunction> function;
std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments;
TypeFunctionInstanceType(NotNull<const TypeFunction> family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
: family(family)
TypeFunctionInstanceType(NotNull<const TypeFunction> function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
: function(function)
, typeArguments(typeArguments)
, packArguments(packArguments)
{
}
TypeFunctionInstanceType(const TypeFunction& family, std::vector<TypeId> typeArguments)
: family{&family}
TypeFunctionInstanceType(const TypeFunction& function, std::vector<TypeId> typeArguments)
: function{&function}
, typeArguments(typeArguments)
, packArguments{}
{
}
TypeFunctionInstanceType(const TypeFunction& family, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
: family{&family}
TypeFunctionInstanceType(const TypeFunction& function, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
: function{&function}
, typeArguments(typeArguments)
, packArguments(packArguments)
{

View File

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

View File

@ -29,13 +29,13 @@ struct TypeFunctionReductionGuessResult
struct TypeFunctionInferenceResult
{
std::vector<TypeId> operandInference;
TypeId familyResultInference;
TypeId functionResultInference;
};
struct TypeFunctionReductionGuesser
{
// 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
DenseHashMap<TypeId, TypeId> substitutable{nullptr};
// List of instances to try progress
@ -57,14 +57,14 @@ private:
std::optional<TypeId> guessType(TypeId arg);
void dumpGuesses();
bool isNumericBinopFamily(const TypeFunctionInstanceType& instance);
bool isComparisonFamily(const TypeFunctionInstanceType& instance);
bool isOrAndFamily(const TypeFunctionInstanceType& instance);
bool isNotFamily(const TypeFunctionInstanceType& instance);
bool isLenFamily(const TypeFunctionInstanceType& instance);
bool isNumericBinopFunction(const TypeFunctionInstanceType& instance);
bool isComparisonFunction(const TypeFunctionInstanceType& instance);
bool isOrAndFunction(const TypeFunctionInstanceType& instance);
bool isNotFunction(const TypeFunctionInstanceType& instance);
bool isLenFunction(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);
std::optional<TypeId> tryAssignOperandType(TypeId ty);
@ -75,11 +75,11 @@ private:
bool isFunctionGenericsSaturated(const FunctionType& ftv, DenseHashSet<TypeId>& instanceArgs);
void inferTypeFunctionSubstitutions(TypeId ty, const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferNumericBinopFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferComparisonFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferOrAndFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferNotFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferLenFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferUnaryMinusFamily(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferNumericBinopFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferComparisonFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferOrAndFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferNotFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferLenFunction(const TypeFunctionInstanceType* instance);
TypeFunctionInferenceResult inferUnaryMinusFunction(const TypeFunctionInstanceType* instance);
};
} // namespace Luau

View File

@ -92,7 +92,7 @@ struct BlockedTypePack
*/
struct TypeFunctionInstanceTypePack
{
NotNull<const TypePackFunction> family;
NotNull<const TypePackFunction> function;
std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments;

View File

@ -49,11 +49,11 @@ struct Unifier2
std::vector<ConstraintV> incompleteSubtypes;
// 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,
DenseHashSet<const void*>* uninhabitedTypeFamilies);
DenseHashSet<const void*>* uninhabitedTypeFunctions);
/** Attempt to commit the subtype relation subTy <: superTy to the type
* graph.

View File

@ -15,6 +15,7 @@
#include "Luau/Simplify.h"
#include "Luau/StringUtils.h"
#include "Luau/TableLiteralInference.h"
#include "Luau/TimeTrace.h"
#include "Luau/Type.h"
#include "Luau/TypeFunction.h"
#include "Luau/TypePack.h"
@ -211,6 +212,8 @@ ConstraintGenerator::ConstraintGenerator(ModulePtr module, NotNull<Normalizer> n
void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
{
LUAU_TIMETRACE_SCOPE("ConstraintGenerator::visitModuleRoot", "Typechecking");
LUAU_ASSERT(scopes.empty());
LUAU_ASSERT(rootScope == nullptr);
ScopePtr scope = std::make_shared<Scope>(globalScope);
@ -3356,9 +3359,9 @@ std::vector<std::optional<TypeId>> ConstraintGenerator::getExpectedCallTypesForF
}
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});
return result;
}

View File

@ -366,6 +366,8 @@ void ConstraintSolver::randomize(unsigned seed)
void ConstraintSolver::run()
{
LUAU_TIMETRACE_SCOPE("ConstraintSolver::run", "Typechecking");
if (isDone())
return;
@ -489,11 +491,11 @@ void ConstraintSolver::run()
if (!unsolvedConstraints.empty())
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
// 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)
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
for (auto [t, constraint] : typeFamiliesToFinalize)
for (auto [t, constraint] : typeFunctionsToFinalize)
{
TypeId ty = follow(t);
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.
*
* 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,
* 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);
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);
}
}
@ -1975,17 +1977,17 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
ty = follow(ty);
// If we couldn't reduce this type function, stick it in the set!
if (get<TypeFunctionInstanceType>(ty))
typeFamiliesToFinalize[ty] = constraint;
typeFunctionsToFinalize[ty] = constraint;
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)
{
if (auto utf = get<UninhabitedTypeFunction>(error))
uninhabitedTypeFamilies.insert(utf->ty);
uninhabitedTypeFunctions.insert(utf->ty);
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 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)
{
if (auto utf = get<UninhabitedTypeFunction>(error))
uninhabitedTypeFamilies.insert(utf->ty);
uninhabitedTypeFunctions.insert(utf->ty);
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>
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);
@ -2735,7 +2737,7 @@ bool ConstraintSolver::isBlocked(TypeId ty)
ty = follow(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);
}
@ -2745,7 +2747,7 @@ bool ConstraintSolver::isBlocked(TypePackId tp)
tp = follow(tp);
if (auto tfitp = get<TypeFunctionInstanceTypePack>(tp))
return uninhabitedTypeFamilies.contains(tp) == false;
return uninhabitedTypeFunctions.contains(tp) == false;
return nullptr != get<BlockedTypePack>(tp);
}

View File

@ -5,6 +5,7 @@
#include "Luau/Def.h"
#include "Luau/Common.h"
#include "Luau/Error.h"
#include "Luau/TimeTrace.h"
#include <optional>
@ -136,6 +137,8 @@ bool DfgScope::canUpdateDefinition(DefId def, const std::string& key) const
DataFlowGraph DataFlowGraphBuilder::build(AstStatBlock* block, NotNull<InternalErrorReporter> handle)
{
LUAU_TIMETRACE_SCOPE("DataFlowGraphBuilder::build", "Typechecking");
LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution);
DataFlowGraphBuilder builder;

View File

@ -585,7 +585,7 @@ struct ErrorConverter
return "Unexpected type " + Luau::toString(e.ty) + " flagged as an uninhabited type function.";
// 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 ";
@ -593,8 +593,8 @@ struct ErrorConverter
{
result += "operand of type " + Luau::toString(tfit->typeArguments[0]);
if (tfit->family->name != "not")
result += "; there is no corresponding overload for __" + tfit->family->name;
if (tfit->function->name != "not")
result += "; there is no corresponding overload for __" + tfit->function->name;
}
else
{
@ -619,7 +619,7 @@ struct ErrorConverter
}
// 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 ";
@ -646,14 +646,14 @@ struct ErrorConverter
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;
}
// 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())
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";
}
if ("index" == tfit->family->name || "rawget" == tfit->family->name)
if ("index" == tfit->function->name || "rawget" == tfit->function->name)
{
if (tfit->typeArguments.size() != 2)
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
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`
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" +
"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
{
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
@ -718,7 +718,7 @@ struct ErrorConverter
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 "
"time";
}

View File

@ -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,
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>();
result->name = sourceModule.name;
result->humanReadableName = sourceModule.humanReadableName;

View File

@ -9,6 +9,7 @@
#include "Luau/Subtyping.h"
#include "Luau/Normalize.h"
#include "Luau/Error.h"
#include "Luau/TimeTrace.h"
#include "Luau/TypeArena.h"
#include "Luau/TypeFunction.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))
return instance;
@ -227,11 +228,11 @@ struct NonStrictTypeChecker
{
TypeId* ty = module->astTypes.find(expr);
if (ty)
return checkForFamilyInhabitance(follow(*ty), expr->location);
return checkForTypeFunctionInhabitance(follow(*ty), expr->location);
TypePackId* tp = module->astTypePacks.find(expr);
if (tp)
return checkForFamilyInhabitance(flattenPack(*tp), expr->location);
return checkForTypeFunctionInhabitance(flattenPack(*tp), expr->location);
return builtinTypes->anyType;
}
@ -728,6 +729,8 @@ private:
void checkNonStrict(NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> ice, NotNull<UnifierSharedState> unifierState,
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};
typeChecker.visit(sourceModule.root);
unfreeze(module->interfaceTypes);

View File

@ -127,7 +127,7 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
return dest.addType(NegationType{a.ty});
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));
}
else
@ -672,7 +672,7 @@ TypePackId Substitution::clone(TypePackId tp)
else if (const TypeFunctionInstanceTypePack* tfitp = get<TypeFunctionInstanceTypePack>(tp))
{
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.packArguments.assign(tfitp->packArguments.begin(), tfitp->packArguments.end());
return addTypePack(std::move(clone));

View File

@ -530,6 +530,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
}
else if (get<AnyType>(superTy))
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))
{
// any = unknown | error, so we rewrite this to match.
@ -1584,19 +1589,19 @@ bool Subtyping::bindGeneric(SubtypingEnvironment& env, TypeId subTy, TypeId supe
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
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
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
auto [ty, errors] = handleTypeFunctionReductionResult(superFamilyInstance);
auto [ty, errors] = handleTypeFunctionReductionResult(superFunctionInstance);
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))});
}
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}};
TypeId family = arena->addType(*familyInstance);
FunctionGraphReductionResult result = reduceTypeFunctions(family, {}, context, true);
TypeId function = arena->addType(*functionInstance);
FunctionGraphReductionResult result = reduceTypeFunctions(function, {}, context, true);
ErrorVec errors;
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};
}
if (result.reducedTypes.contains(family))
return {family, errors};
if (result.reducedTypes.contains(function))
return {function, errors};
return {builtinTypes->neverType, errors};
}

View File

@ -345,7 +345,7 @@ void StateDot::visitChildren(TypeId ty, int index)
}
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);
finishNode();

View File

@ -1033,7 +1033,7 @@ struct TypeStringifier
void operator()(TypeId, const TypeFunctionInstanceType& tfitv)
{
state.emit(tfitv.family->name);
state.emit(tfitv.function->name);
state.emit("<");
bool comma = false;
@ -1234,7 +1234,7 @@ struct TypePackStringifier
void operator()(TypePackId, const TypeFunctionInstanceTypePack& tfitp)
{
state.emit(tfitp.family->name);
state.emit(tfitp.function->name);
state.emit("<");
bool comma = false;

View File

@ -424,7 +424,7 @@ bool maybeSingleton(TypeId ty)
if (maybeSingleton(part)) // will i regret this?
return true;
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 false;
}

View File

@ -94,24 +94,24 @@ TypePackId TypeArena::addTypePack(TypePackVar tp)
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)

View File

@ -382,7 +382,7 @@ public:
}
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:
@ -456,7 +456,7 @@ public:
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:

View File

@ -13,6 +13,7 @@
#include "Luau/Normalize.h"
#include "Luau/OverloadResolution.h"
#include "Luau/Subtyping.h"
#include "Luau/TimeTrace.h"
#include "Luau/ToString.h"
#include "Luau/TxnLog.h"
#include "Luau/Type.h"
@ -95,7 +96,7 @@ static std::optional<std::string> getIdentifierOfBaseVar(AstExpr* node)
template<typename T>
bool areEquivalent(const T& a, const T& b)
{
if (a.family != b.family)
if (a.function != b.function)
return false;
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;
}
struct FamilyFinder : TypeOnceVisitor
struct TypeFunctionFinder : TypeOnceVisitor
{
DenseHashSet<TypeId> mentionedFamilies{nullptr};
DenseHashSet<TypePackId> mentionedFamilyPacks{nullptr};
DenseHashSet<TypeId> mentionedFunctions{nullptr};
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
bool visit(TypeId ty, const TypeFunctionInstanceType&) override
{
mentionedFamilies.insert(ty);
mentionedFunctions.insert(ty);
return true;
}
bool visit(TypePackId tp, const TypeFunctionInstanceTypePack&) override
{
mentionedFamilyPacks.insert(tp);
mentionedFunctionPacks.insert(tp);
return true;
}
};
struct InternalFamilyFinder : TypeOnceVisitor
struct InternalTypeFunctionFinder : TypeOnceVisitor
{
DenseHashSet<TypeId> internalFamilies{nullptr};
DenseHashSet<TypePackId> internalPackFamilies{nullptr};
DenseHashSet<TypeId> mentionedFamilies{nullptr};
DenseHashSet<TypePackId> mentionedFamilyPacks{nullptr};
DenseHashSet<TypeId> internalFunctions{nullptr};
DenseHashSet<TypePackId> internalPackFunctions{nullptr};
DenseHashSet<TypeId> mentionedFunctions{nullptr};
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
InternalFamilyFinder(std::vector<TypeId>& declStack)
InternalTypeFunctionFinder(std::vector<TypeId>& declStack)
{
FamilyFinder f;
TypeFunctionFinder f;
for (TypeId fn : declStack)
f.traverse(fn);
mentionedFamilies = std::move(f.mentionedFamilies);
mentionedFamilyPacks = std::move(f.mentionedFamilyPacks);
mentionedFunctions = std::move(f.mentionedFunctions);
mentionedFunctionPacks = std::move(f.mentionedFunctionPacks);
}
bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
@ -175,7 +176,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
if (hasGeneric)
{
for (TypeId mentioned : mentionedFamilies)
for (TypeId mentioned : mentionedFunctions)
{
const TypeFunctionInstanceType* mentionedTfit = get<TypeFunctionInstanceType>(mentioned);
LUAU_ASSERT(mentionedTfit);
@ -185,7 +186,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
}
}
internalFamilies.insert(ty);
internalFunctions.insert(ty);
}
return true;
@ -215,7 +216,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
if (hasGeneric)
{
for (TypePackId mentioned : mentionedFamilyPacks)
for (TypePackId mentioned : mentionedFunctionPacks)
{
const TypeFunctionInstanceTypePack* mentionedTfitp = get<TypeFunctionInstanceTypePack>(mentioned);
LUAU_ASSERT(mentionedTfitp);
@ -225,7 +226,7 @@ struct InternalFamilyFinder : TypeOnceVisitor
}
}
internalPackFamilies.insert(tp);
internalPackFunctions.insert(tp);
}
return true;
@ -423,19 +424,19 @@ struct TypeChecker2
return std::nullopt;
}
void checkForInternalFamily(TypeId ty, Location location)
void checkForInternalTypeFunction(TypeId ty, Location location)
{
InternalFamilyFinder finder(functionDeclStack);
InternalTypeFunctionFinder finder(functionDeclStack);
finder.traverse(ty);
for (TypeId internal : finder.internalFamilies)
for (TypeId internal : finder.internalFunctions)
reportError(WhereClauseNeeded{internal}, location);
for (TypePackId internal : finder.internalPackFamilies)
for (TypePackId internal : finder.internalPackFunctions)
reportError(PackWhereClauseNeeded{internal}, location);
}
TypeId checkForFamilyInhabitance(TypeId instance, Location location)
TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location)
{
if (seenTypeFunctionInstances.find(instance))
return instance;
@ -468,11 +469,11 @@ struct TypeChecker2
// allows us not to think about this very much in the actual typechecking logic.
TypeId* ty = module->astTypes.find(expr);
if (ty)
return checkForFamilyInhabitance(follow(*ty), expr->location);
return checkForTypeFunctionInhabitance(follow(*ty), expr->location);
TypePackId* tp = module->astTypePacks.find(expr);
if (tp)
return checkForFamilyInhabitance(flattenPack(*tp), expr->location);
return checkForTypeFunctionInhabitance(flattenPack(*tp), expr->location);
return builtinTypes->anyType;
}
@ -495,7 +496,7 @@ struct TypeChecker2
TypeId* ty = module->astResolvedTypes.find(annotation);
LUAU_ASSERT(ty);
return checkForFamilyInhabitance(follow(*ty), annotation->location);
return checkForTypeFunctionInhabitance(follow(*ty), annotation->location);
}
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation)
@ -1725,7 +1726,7 @@ struct TypeChecker2
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)
{
const FunctionType* inferredFtv = get<FunctionType>(normalizedFnTy->functions.parts.front());
@ -1855,7 +1856,7 @@ struct TypeChecker2
if (get<TypeFunctionInstanceType>(expectedResult))
{
checkForInternalFamily(expectedResult, expr->location);
checkForInternalTypeFunction(expectedResult, expr->location);
return expectedResult;
}
@ -2254,7 +2255,7 @@ struct TypeChecker2
{
TypeId* resolvedTy = module->astResolvedTypes.find(ty);
if (resolvedTy)
checkForFamilyInhabitance(follow(*resolvedTy), ty->location);
checkForTypeFunctionInhabitance(follow(*resolvedTy), ty->location);
if (auto t = ty->as<AstTypeReference>())
return visit(t);
@ -3057,6 +3058,8 @@ struct TypeChecker2
void check(NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> unifierState, NotNull<TypeCheckLimits> limits, DcrLogger* logger,
const SourceModule& sourceModule, Module* module)
{
LUAU_TIMETRACE_SCOPE("check", "Typechecking");
TypeChecker2 typeChecker{builtinTypes, unifierState, limits, logger, &sourceModule, module};
typeChecker.visit(sourceModule.root);

View File

@ -104,7 +104,7 @@ struct TypeFunctionReducer
VecDeque<TypeId> queuedTys;
VecDeque<TypePackId> queuedTps;
TypeOrTypePackIdSet shouldGuess;
std::vector<TypeId> cyclicTypeFamilies;
std::vector<TypeId> cyclicTypeFunctions;
TypeOrTypePackIdSet irreducible{nullptr};
FunctionGraphReductionResult result;
bool force = false;
@ -118,7 +118,7 @@ struct TypeFunctionReducer
, queuedTys(std::move(queuedTys))
, queuedTps(std::move(queuedTps))
, shouldGuess(std::move(shouldGuess))
, cyclicTypeFamilies(std::move(cyclicTypes))
, cyclicTypeFunctions(std::move(cyclicTypes))
, force(force)
, location(location)
{
@ -138,7 +138,7 @@ struct TypeFunctionReducer
if (is<TypeFunctionInstanceType>(ty))
{
for (auto t : cyclicTypeFamilies)
for (auto t : cyclicTypeFunctions)
{
if (ty == t)
return SkipTestResult::CyclicTypeFunction;
@ -339,7 +339,7 @@ struct TypeFunctionReducer
if (!testParameters(subject, tfit) && testCyclic != SkipTestResult::CyclicTypeFunction)
{
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;
}
@ -347,7 +347,7 @@ struct TypeFunctionReducer
if (tryGuessing(subject))
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);
}
}
@ -371,7 +371,7 @@ struct TypeFunctionReducer
if (tryGuessing(subject))
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);
}
}
@ -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)
{
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())
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);
}
@ -442,7 +442,7 @@ FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location
if (collector.tys.empty() && collector.tps.empty())
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);
}
@ -528,7 +528,7 @@ static std::optional<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
return std::nullopt;
}
TypeFunctionReductionResult<TypeId> notFamilyFn(
TypeFunctionReductionResult<TypeId> notTypeFunction(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{
if (typeParams.size() != 1 || !packParams.empty())
@ -545,14 +545,14 @@ TypeFunctionReductionResult<TypeId> notFamilyFn(
if (isPending(ty, ctx->solver))
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;
// `not` operates on anything and returns a `boolean` always.
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)
{
if (typeParams.size() != 1 || !packParams.empty())
@ -604,7 +604,7 @@ TypeFunctionReductionResult<TypeId> lenFamilyFn(
if (normTy->hasTopTable() || get<TableType>(normalizedOperand))
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;
// findMetatableEntry demands the ability to emit errors, so we must give it
@ -644,7 +644,7 @@ TypeFunctionReductionResult<TypeId> lenFamilyFn(
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)
{
if (typeParams.size() != 1 || !packParams.empty())
@ -689,7 +689,7 @@ TypeFunctionReductionResult<TypeId> unmFamilyFn(
if (normTy->isExactlyNumber())
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;
// findMetatableEntry demands the ability to emit errors, so we must give it
@ -744,7 +744,7 @@ NotNull<Constraint> TypeFunctionContext::pushConstraint(ConstraintV&& c)
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -787,7 +787,7 @@ TypeFunctionReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const
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> normRhsTy = ctx->normalizer->normalize(rhsTy);
@ -803,7 +803,7 @@ TypeFunctionReductionResult<TypeId> numericBinopFamilyFn(TypeId instance, const
if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber())
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;
// 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, {}, {}};
}
TypeFunctionReductionResult<TypeId> addFamilyFn(
TypeFunctionReductionResult<TypeId> addTypeFunction(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -856,10 +856,10 @@ TypeFunctionReductionResult<TypeId> addFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -868,10 +868,10 @@ TypeFunctionReductionResult<TypeId> subFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -880,10 +880,10 @@ TypeFunctionReductionResult<TypeId> mulFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -892,10 +892,10 @@ TypeFunctionReductionResult<TypeId> divFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -904,10 +904,10 @@ TypeFunctionReductionResult<TypeId> idivFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -916,10 +916,10 @@ TypeFunctionReductionResult<TypeId> powFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -928,10 +928,10 @@ TypeFunctionReductionResult<TypeId> modFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -987,7 +987,7 @@ TypeFunctionReductionResult<TypeId> concatFamilyFn(
if ((normLhsTy->isSubtypeOfString() || normLhsTy->isExactlyNumber()) && (normRhsTy->isSubtypeOfString() || normRhsTy->isExactlyNumber()))
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;
// findMetatableEntry demands the ability to emit errors, so we must give it
@ -1039,7 +1039,7 @@ TypeFunctionReductionResult<TypeId> concatFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -1090,7 +1090,7 @@ TypeFunctionReductionResult<TypeId> andFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -1141,7 +1141,7 @@ TypeFunctionReductionResult<TypeId> orFamilyFn(
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)
{
@ -1162,7 +1162,7 @@ static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, c
else if (isPending(rhsTy, ctx->solver))
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
// lt< 'a , never> -> continue
// lt< never, 'a> -> continue
@ -1173,7 +1173,7 @@ static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, c
bool rhsFree = get<FreeType>(rhsTy) != nullptr;
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 <t, number> implies t is number
if (lhsFree && isNumber(rhsTy))
@ -1238,7 +1238,7 @@ static TypeFunctionReductionResult<TypeId> comparisonFamilyFn(TypeId instance, c
if (normLhsTy->isExactlyNumber() && normRhsTy->isExactlyNumber())
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;
// 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, {}, {}};
}
TypeFunctionReductionResult<TypeId> ltFamilyFn(
TypeFunctionReductionResult<TypeId> ltTypeFunction(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -1289,10 +1289,10 @@ TypeFunctionReductionResult<TypeId> ltFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -1301,10 +1301,10 @@ TypeFunctionReductionResult<TypeId> leFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -1381,7 +1381,7 @@ TypeFunctionReductionResult<TypeId> eqFamilyFn(
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);
@ -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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -1520,7 +1520,7 @@ TypeFunctionReductionResult<TypeId> refineFamilyFn(
return {resultTy, false, {}, {}};
}
TypeFunctionReductionResult<TypeId> singletonFamilyFn(
TypeFunctionReductionResult<TypeId> singletonTypeFunction(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{
if (typeParams.size() != 1 || !packParams.empty())
@ -1557,7 +1557,7 @@ TypeFunctionReductionResult<TypeId> singletonFamilyFn(
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)
{
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)
{
if (!packParams.empty())
@ -1725,7 +1725,7 @@ bool computeKeysOf(TypeId ty, Set<std::string>& result, DenseHashSet<TypeId>& se
return false;
}
TypeFunctionReductionResult<TypeId> keyofFamilyImpl(
TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx, bool isRaw)
{
if (typeParams.size() != 1 || !packParams.empty())
@ -1842,7 +1842,7 @@ TypeFunctionReductionResult<TypeId> keyofFamilyImpl(
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)
{
if (typeParams.size() != 1 || !packParams.empty())
@ -1851,10 +1851,10 @@ TypeFunctionReductionResult<TypeId> keyofFamilyFn(
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)
{
if (typeParams.size() != 1 || !packParams.empty())
@ -1863,7 +1863,7 @@ TypeFunctionReductionResult<TypeId> rawkeyofFamilyFn(
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`
@ -1960,7 +1960,7 @@ bool tblIndexInto(TypeId indexer, TypeId indexee, DenseHashSet<TypeId>& result,
/* Vocabulary note: indexee refers to the type that contains the properties,
indexer refers to the type that is used to access indexee
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)
{
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, {}, {}};
}
TypeFunctionReductionResult<TypeId> indexFamilyFn(
TypeFunctionReductionResult<TypeId> indexTypeFunction(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFunctionContext> ctx)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -2073,10 +2073,10 @@ TypeFunctionReductionResult<TypeId> indexFamilyFn(
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)
{
if (typeParams.size() != 2 || !packParams.empty())
@ -2085,34 +2085,34 @@ TypeFunctionReductionResult<TypeId> rawgetFamilyFn(
LUAU_ASSERT(false);
}
return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ true);
return indexFunctionImpl(typeParams, packParams, ctx, /* isRaw */ true);
}
BuiltinTypeFunctions::BuiltinTypeFunctions()
: notFunc{"not", notFamilyFn}
, lenFunc{"len", lenFamilyFn}
, unmFunc{"unm", unmFamilyFn}
, addFunc{"add", addFamilyFn}
, subFunc{"sub", subFamilyFn}
, mulFunc{"mul", mulFamilyFn}
, divFunc{"div", divFamilyFn}
, idivFunc{"idiv", idivFamilyFn}
, powFunc{"pow", powFamilyFn}
, modFunc{"mod", modFamilyFn}
, concatFunc{"concat", concatFamilyFn}
, andFunc{"and", andFamilyFn}
, orFunc{"or", orFamilyFn}
, ltFunc{"lt", ltFamilyFn}
, leFunc{"le", leFamilyFn}
, eqFunc{"eq", eqFamilyFn}
, refineFunc{"refine", refineFamilyFn}
, singletonFunc{"singleton", singletonFamilyFn}
, unionFunc{"union", unionFamilyFn}
, intersectFunc{"intersect", intersectFamilyFn}
, keyofFunc{"keyof", keyofFamilyFn}
, rawkeyofFunc{"rawkeyof", rawkeyofFamilyFn}
, indexFunc{"index", indexFamilyFn}
, rawgetFunc{"rawget", rawgetFamilyFn}
: notFunc{"not", notTypeFunction}
, lenFunc{"len", lenTypeFunction}
, unmFunc{"unm", unmTypeFunction}
, addFunc{"add", addTypeFunction}
, subFunc{"sub", subTypeFunction}
, mulFunc{"mul", mulTypeFunction}
, divFunc{"div", divTypeFunction}
, idivFunc{"idiv", idivTypeFunction}
, powFunc{"pow", powTypeFunction}
, modFunc{"mod", modTypeFunction}
, concatFunc{"concat", concatTypeFunction}
, andFunc{"and", andTypeFunction}
, orFunc{"or", orTypeFunction}
, ltFunc{"lt", ltTypeFunction}
, leFunc{"le", leTypeFunction}
, eqFunc{"eq", eqTypeFunction}
, refineFunc{"refine", refineTypeFunction}
, singletonFunc{"singleton", singletonTypeFunction}
, unionFunc{"union", unionTypeFunction}
, intersectFunc{"intersect", intersectTypeFunction}
, keyofFunc{"keyof", keyofTypeFunction}
, rawkeyofFunc{"rawkeyof", rawkeyofTypeFunction}
, indexFunc{"index", indexTypeFunction}
, rawgetFunc{"rawget", rawgetTypeFunction}
{
}

View File

@ -83,7 +83,7 @@ bool TypeFunctionReductionGuesser::isFunctionGenericsSaturated(const FunctionTyp
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());
for (auto [t, t_] : substitutable)
printf("Substitute %s for %s\n", toString(t).c_str(), toString(t_).c_str());
@ -175,7 +175,7 @@ TypeFunctionReductionGuessResult TypeFunctionReductionGuesser::guessTypeFunction
toInfer.clear();
cyclicInstances.clear();
familyReducesTo.clear();
functionReducesTo.clear();
substitutable.clear();
return TypeFunctionReductionGuessResult{results, recommendedAnnotation};
@ -196,44 +196,44 @@ std::optional<TypeId> TypeFunctionReductionGuesser::guessType(TypeId arg)
}
if (get<TypeFunctionInstanceType>(t))
{
if (familyReducesTo.contains(t))
return familyReducesTo[t];
if (functionReducesTo.contains(t))
return functionReducesTo[t];
}
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" ||
instance.family->name == "idiv" || instance.family->name == "pow" || instance.family->name == "mod";
return instance.function->name == "add" || instance.function->name == "sub" || instance.function->name == "mul" || instance.function->name == "div" ||
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)
{
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)
{
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
if (auto tfit = get<TypeFunctionInstanceType>(ty))
{
if (familyReducesTo.contains(ty))
return {familyReducesTo[ty]};
if (functionReducesTo.contains(ty))
return {functionReducesTo[ty]};
}
// 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;
LUAU_ASSERT(instance);
// TODO: Make an inexhaustive version of this warn in the compiler?
if (isNumericBinopFamily(*instance))
result = inferNumericBinopFamily(instance);
else if (isComparisonFamily(*instance))
result = inferComparisonFamily(instance);
else if (isOrAndFamily(*instance))
result = inferOrAndFamily(instance);
else if (isNotFamily(*instance))
result = inferNotFamily(instance);
else if (isLenFamily(*instance))
result = inferLenFamily(instance);
if (isNumericBinopFunction(*instance))
result = inferNumericBinopFunction(instance);
else if (isComparisonFunction(*instance))
result = inferComparisonFunction(instance);
else if (isOrAndFunction(*instance))
result = inferOrAndFunction(instance);
else if (isNotFunction(*instance))
result = inferNotFunction(instance);
else if (isLenFunction(*instance))
result = inferLenFunction(instance);
else if (isUnaryMinus(*instance))
result = inferUnaryMinusFamily(instance);
result = inferUnaryMinusFunction(instance);
else
result = {{}, builtins->unknownType};
TypeId resultInference = follow(result.familyResultInference);
if (!familyReducesTo.contains(resultInference))
familyReducesTo[ty] = resultInference;
TypeId resultInference = follow(result.functionResultInference);
if (!functionReducesTo.contains(resultInference))
functionReducesTo[ty] = resultInference;
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]);
if (auto tfit = get<TypeFunctionInstanceType>(arg))
{
if (!familyReducesTo.contains(arg))
familyReducesTo.try_insert(arg, inference);
if (!functionReducesTo.contains(arg))
functionReducesTo.try_insert(arg, inference);
}
else if (auto gt = get<GenericType>(arg))
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);
TypeFunctionInferenceResult defaultNumericBinopInference{{builtins->numberType, builtins->numberType}, builtins->numberType};
return defaultNumericBinopInference;
}
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferComparisonFamily(const TypeFunctionInstanceType* instance)
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferComparisonFunction(const TypeFunctionInstanceType* instance)
{
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
TypeId lhsTy = follow(instance->typeArguments[0]);
@ -365,7 +365,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferComparisonFamily(
return comparisonInference(builtins->numberType);
}
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const TypeFunctionInstanceType* instance)
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFunction(const TypeFunctionInstanceType* instance)
{
LUAU_ASSERT(instance->typeArguments.size() == 2);
@ -384,7 +384,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const
bool lhsTruthy = lty ? lty->isTruthy() : false;
bool rhsTruthy = rty ? rty->isTruthy() : false;
// 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))
return defaultAndOrInference;
@ -398,7 +398,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const
return {{builtins->unknownType, rhsTy}, rhsTy};
}
if (instance->family->name == "and")
if (instance->function->name == "and")
{
if (operandIsAssignable(lhsTy) && operandIsAssignable(rhsTy))
@ -416,7 +416,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferOrAndFamily(const
return defaultAndOrInference;
}
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNotFamily(const TypeFunctionInstanceType* instance)
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNotFunction(const TypeFunctionInstanceType* instance)
{
LUAU_ASSERT(instance->typeArguments.size() == 1);
TypeId opTy = follow(instance->typeArguments[0]);
@ -425,7 +425,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferNotFamily(const T
return {{opTy}, builtins->booleanType};
}
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferLenFamily(const TypeFunctionInstanceType* instance)
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferLenFunction(const TypeFunctionInstanceType* instance)
{
LUAU_ASSERT(instance->typeArguments.size() == 1);
TypeId opTy = follow(instance->typeArguments[0]);
@ -434,7 +434,7 @@ TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferLenFamily(const T
return {{opTy}, builtins->numberType};
}
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferUnaryMinusFamily(const TypeFunctionInstanceType* instance)
TypeFunctionInferenceResult TypeFunctionReductionGuesser::inferUnaryMinusFunction(const TypeFunctionInstanceType* instance)
{
LUAU_ASSERT(instance->typeArguments.size() == 1);
TypeId opTy = follow(instance->typeArguments[0]);

View File

@ -92,19 +92,19 @@ Unifier2::Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes,
, ice(ice)
, limits(TypeCheckLimits{}) // TODO: typecheck limits in unifier2
, recursionLimit(FInt::LuauTypeInferRecursionLimit)
, uninhabitedTypeFamilies(nullptr)
, uninhabitedTypeFunctions(nullptr)
{
}
Unifier2::Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<InternalErrorReporter> ice,
DenseHashSet<const void*>* uninhabitedTypeFamilies)
DenseHashSet<const void*>* uninhabitedTypeFunctions)
: arena(arena)
, builtinTypes(builtinTypes)
, scope(scope)
, ice(ice)
, limits(TypeCheckLimits{}) // TODO: typecheck limits in unifier2
, recursionLimit(FInt::LuauTypeInferRecursionLimit)
, uninhabitedTypeFamilies(uninhabitedTypeFamilies)
, uninhabitedTypeFunctions(uninhabitedTypeFunctions)
{
}
@ -135,7 +135,7 @@ bool Unifier2::unify(TypeId subTy, TypeId superTy)
// - *blocked* <: unknown
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;
incompleteSubtypes.push_back(SubtypeConstraint{subTy, superTy});
@ -539,7 +539,7 @@ bool Unifier2::unify(TypePackId subTp, TypePackId superTp)
if (isIrresolvable(subTp) || isIrresolvable(superTp))
{
if (uninhabitedTypeFamilies && (uninhabitedTypeFamilies->contains(subTp) || uninhabitedTypeFamilies->contains(superTp)))
if (uninhabitedTypeFunctions && (uninhabitedTypeFunctions->contains(subTp) || uninhabitedTypeFunctions->contains(superTp)))
return true;
incompleteSubtypes.push_back(PackSubtypeConstraint{subTp, superTp});

View File

@ -11,7 +11,6 @@ Id UnionFind::makeSet()
Id id{parents.size()};
parents.push_back(id);
ranks.push_back(0);
return id;
}

View File

@ -36,7 +36,7 @@ local x: Account = 5
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;
@ -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]));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "unary_op_type_family_errors")
TEST_CASE_FIXTURE(BuiltinsFixture, "unary_op_type_function_errors")
{
frontend.options.retainFullTypeGraphs = false;

View File

@ -311,7 +311,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "nocheck_cycle_used_by_checked")
REQUIRE(bool(cExports));
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK_EQ("{ a: any, b: any }", toString(*cExports));
CHECK_EQ("{ a: { hello: any }, b: { hello: any } }", toString(*cExports));
else
CHECK_EQ("{| a: any, b: any |}", toString(*cExports));
}

View File

@ -28,7 +28,7 @@ end
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"(
function fib(n)

View File

@ -404,8 +404,16 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "error_suppression")
CHECK(!isSubtype(any, str));
CHECK(isSubtype(str, any));
CHECK(!isSubtype(any, unk));
CHECK(isSubtype(unk, 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(err, str));
CHECK(!isSubtype(str, err));

View File

@ -470,9 +470,11 @@ TEST_CASE_FIXTURE(SubtypeFixture, "variadic_subpath_in_pack")
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")
@ -990,39 +992,51 @@ TEST_CASE_FIXTURE(SubtypeFixture, "semantic_subtyping_disj")
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <: t2 where t2 = {trim: (t2) -> string}")
{
TypeId t1 = cyclicTable([&](TypeId ty, TableType* tt) {
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
});
TypeId t1 = cyclicTable(
[&](TypeId ty, TableType* tt)
{
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
});
TypeId t2 = cyclicTable([&](TypeId ty, TableType* tt) {
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
});
TypeId t2 = cyclicTable(
[&](TypeId ty, TableType* tt)
{
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
});
CHECK_IS_SUBTYPE(t1, t2);
}
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <!: t2 where t2 = {trim: (t2) -> t2}")
{
TypeId t1 = cyclicTable([&](TypeId ty, TableType* tt) {
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
});
TypeId t1 = cyclicTable(
[&](TypeId ty, TableType* tt)
{
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
});
TypeId t2 = cyclicTable([&](TypeId ty, TableType* tt) {
tt->props["trim"] = fn({ty}, {ty});
});
TypeId t2 = cyclicTable(
[&](TypeId ty, TableType* tt)
{
tt->props["trim"] = fn({ty}, {ty});
});
CHECK_IS_NOT_SUBTYPE(t1, t2);
}
TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> t1} <!: t2 where t2 = {trim: (t2) -> string}")
{
TypeId t1 = cyclicTable([&](TypeId ty, TableType* tt) {
tt->props["trim"] = fn({ty}, {ty});
});
TypeId t1 = cyclicTable(
[&](TypeId ty, TableType* tt)
{
tt->props["trim"] = fn({ty}, {ty});
});
TypeId t2 = cyclicTable([&](TypeId ty, TableType* tt) {
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
});
TypeId t2 = cyclicTable(
[&](TypeId ty, TableType* tt)
{
tt->props["trim"] = fn({ty}, {builtinTypes->stringType});
});
CHECK_IS_NOT_SUBTYPE(t1, t2);
}
@ -1286,7 +1300,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "<T>({ x: T }) -> T <: ({ method: <T>({ x: T }
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,
builtinTypes->numberType, builtinTypes->stringType, builtinTypes->tableType, builtinTypes->threadType}});
@ -1323,8 +1337,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "table_property")
CHECK(!result.isSubtype);
REQUIRE(result.reasoning.size() == 1);
CHECK(*result.reasoning.begin() == SubtypingReasoning{/* subPath */ Path(TypePath::Property::read("X")),
/* superPath */ Path(TypePath::Property::read("X")),
/* variance */ SubtypingVariance::Invariant});
/* superPath */ Path(TypePath::Property::read("X")),
/* variance */ SubtypingVariance::Invariant});
}
TEST_CASE_FIXTURE(SubtypeFixture, "table_indexers")
@ -1397,9 +1411,9 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets")
CHECK(!result.isSubtype);
REQUIRE(result.reasoning.size() == 1);
CHECK(*result.reasoning.begin() == SubtypingReasoning{
/* subPath */ TypePath::PathBuilder().rets().index(0).build(),
/* superPath */ TypePath::PathBuilder().rets().index(0).build(),
});
/* subPath */ TypePath::PathBuilder().rets().index(0).build(),
/* superPath */ TypePath::PathBuilder().rets().index(0).build(),
});
}
TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets_tail")
@ -1411,9 +1425,9 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets_tail")
CHECK(!result.isSubtype);
REQUIRE(result.reasoning.size() == 1);
CHECK(*result.reasoning.begin() == SubtypingReasoning{
/* subPath */ TypePath::PathBuilder().rets().tail().variadic().build(),
/* superPath */ TypePath::PathBuilder().rets().tail().variadic().build(),
});
/* subPath */ TypePath::PathBuilder().rets().tail().variadic().build(),
/* superPath */ TypePath::PathBuilder().rets().tail().variadic().build(),
});
}
TEST_CASE_FIXTURE(SubtypeFixture, "nested_table_properties")
@ -1425,10 +1439,10 @@ TEST_CASE_FIXTURE(SubtypeFixture, "nested_table_properties")
CHECK(!result.isSubtype);
REQUIRE(result.reasoning.size() == 1);
CHECK(*result.reasoning.begin() == SubtypingReasoning{
/* subPath */ TypePath::PathBuilder().readProp("X").readProp("Y").readProp("Z").build(),
/* superPath */ TypePath::PathBuilder().readProp("X").readProp("Y").readProp("Z").build(),
/* variance */ SubtypingVariance::Invariant,
});
/* subPath */ TypePath::PathBuilder().readProp("X").readProp("Y").readProp("Z").build(),
/* superPath */ TypePath::PathBuilder().readProp("X").readProp("Y").readProp("Z").build(),
/* variance */ SubtypingVariance::Invariant,
});
}
TEST_CASE_FIXTURE(SubtypeFixture, "string_table_mt")

View File

@ -15,14 +15,14 @@ using namespace Luau;
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
struct FamilyFixture : Fixture
struct TypeFunctionFixture : Fixture
{
TypeFunction swapFamily;
TypeFunction swapFunction;
FamilyFixture()
TypeFunctionFixture()
: Fixture(true, false)
{
swapFamily = TypeFunction{/* name */ "Swap",
swapFunction = TypeFunction{/* name */ "Swap",
/* reducer */
[](TypeId instance, const std::vector<TypeId>& tys, const std::vector<TypePackId>& tps,
NotNull<TypeFunctionContext> ctx) -> TypeFunctionReductionResult<TypeId> {
@ -54,14 +54,14 @@ struct FamilyFixture : Fixture
ScopePtr globalScope = frontend.globals.globalScope;
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);
}
};
TEST_SUITE_BEGIN("TypeFunctionTests");
TEST_CASE_FIXTURE(FamilyFixture, "basic_type_family")
TEST_CASE_FIXTURE(TypeFunctionFixture, "basic_type_function")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -83,7 +83,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "basic_type_family")
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)
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]));
}
TEST_CASE_FIXTURE(FamilyFixture, "family_as_fn_arg")
TEST_CASE_FIXTURE(TypeFunctionFixture, "function_as_fn_arg")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
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]));
}
TEST_CASE_FIXTURE(FamilyFixture, "resolve_deep_families")
TEST_CASE_FIXTURE(TypeFunctionFixture, "resolve_deep_functions")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -134,7 +134,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "resolve_deep_families")
CHECK("number" == toString(requireType("x")));
}
TEST_CASE_FIXTURE(FamilyFixture, "unsolvable_family")
TEST_CASE_FIXTURE(TypeFunctionFixture, "unsolvable_function")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
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)
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");
}
TEST_CASE_FIXTURE(FamilyFixture, "function_internal_families")
TEST_CASE_FIXTURE(TypeFunctionFixture, "function_internal_functions")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -193,7 +193,7 @@ TEST_CASE_FIXTURE(FamilyFixture, "function_internal_families")
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)
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");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_add_family_at_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_add_function_at_work")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -229,7 +229,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_add_family_at_work")
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)
return;
@ -252,7 +252,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "mul_family_with_union_of_multiplicatives")
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)
return;
@ -272,7 +272,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "mul_family_with_union_of_multiplicatives_2")
CHECK(toString(requireTypeAlias("T")) == "Vec3");
}
TEST_CASE_FIXTURE(Fixture, "internal_families_raise_errors")
TEST_CASE_FIXTURE(Fixture, "internal_functions_raise_errors")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -288,7 +288,7 @@ TEST_CASE_FIXTURE(Fixture, "internal_families_raise_errors")
"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)
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>");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "type_families_inhabited_with_normalization")
TEST_CASE_FIXTURE(BuiltinsFixture, "type_functions_inhabited_with_normalization")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -331,7 +331,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_families_inhabited_with_normalization")
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -352,7 +352,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_works")
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)
return;
@ -375,7 +375,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_works_with_metatables")
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)
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");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_string_indexer")
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -421,7 +421,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_string_indexer")
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)
return;
@ -442,7 +442,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_common_subset_if_union_of_
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)
return;
@ -457,7 +457,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_family_never_for_empty_table")
CHECK(toString(requireType("foo")) == "never");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_works")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -478,7 +478,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_works")
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)
return;
@ -501,7 +501,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_ignores_metatables")
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)
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");
}
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)
return;
@ -540,7 +540,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_common_subset_if_union_
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)
return;
@ -555,7 +555,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_family_never_for_empty_table")
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)
return;
@ -575,7 +575,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_works_on_classes")
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)
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");
}
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)
return;
@ -606,7 +606,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_family_common_subset_if_union_of_dif
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)
return;
@ -699,7 +699,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_oss_crash_gh1161")
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"(
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)
return;
@ -827,7 +827,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "ensure_equivalence_with_distributivity")
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)
return;
@ -857,7 +857,7 @@ end
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -880,7 +880,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works")
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)
return;
@ -895,7 +895,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_array")
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)
return;
@ -917,7 +917,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_generic_types")
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)
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'");
}
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)
return;
@ -950,7 +950,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_errors_w_var_indexer")
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)
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'");
}
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)
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'");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_rfc_alternative_section")
TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_rfc_alternative_section")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
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'");
}
TEST_CASE_FIXTURE(ClassFixture, "index_type_family_works_on_classes")
TEST_CASE_FIXTURE(ClassFixture, "index_type_function_works_on_classes")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -1019,7 +1019,7 @@ TEST_CASE_FIXTURE(ClassFixture, "index_type_family_works_on_classes")
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)
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'");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -1067,7 +1067,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works")
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)
return;
@ -1081,7 +1081,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_array")
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)
return;
@ -1097,7 +1097,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_errors_w_var_indexer")
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)
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'");
}
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)
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'");
}
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)
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'");
}
TEST_CASE_FIXTURE(ClassFixture, "rawget_type_family_errors_w_classes")
TEST_CASE_FIXTURE(ClassFixture, "rawget_type_function_errors_w_classes")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;

View File

@ -32,7 +32,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any")
LUAU_REQUIRE_NO_ERRORS(result);
CHECK(builtinTypes->anyType == requireType("a"));
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK("any?" == toString(requireType("a")));
else
CHECK(builtinTypes->anyType == requireType("a"));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any2")
@ -50,7 +53,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any2")
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("any" == toString(requireType("a")));
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK("any?" == toString(requireType("a")));
else
CHECK("any" == toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any")
@ -66,7 +72,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any")
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("any" == toString(requireType("a")));
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK("any?" == toString(requireType("a")));
else
CHECK("any" == toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any2")
@ -80,7 +89,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any2")
end
)");
CHECK("any" == toString(requireType("a")));
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK("any?" == toString(requireType("a")));
else
CHECK("any" == toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any_pack")
@ -96,7 +108,10 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any_pack")
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("any" == toString(requireType("a")));
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK("any?" == toString(requireType("a")));
else
CHECK("any" == toString(requireType("a")));
}
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error")
@ -291,7 +306,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_comp
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("b")));
if (FFlag::DebugLuauDeferredConstraintResolution)
CHECK_EQ("any?", toString(requireType("b")));
else
CHECK_EQ("any", toString(requireType("b")));
}
TEST_CASE_FIXTURE(Fixture, "call_to_any_yields_any")

View File

@ -1424,7 +1424,7 @@ TEST_CASE_FIXTURE(Fixture, "missing_generic_type_parameter")
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)
return;

View File

@ -745,7 +745,7 @@ TEST_CASE_FIXTURE(Fixture, "strict_binary_op_where_lhs_unknown")
if (FFlag::DebugLuauDeferredConstraintResolution)
{
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",
toString(result.errors[0]));
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);
}
TEST_CASE_FIXTURE(Fixture, "add_type_family_works")
TEST_CASE_FIXTURE(Fixture, "add_type_function_works")
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return;
@ -1473,7 +1473,7 @@ TEST_CASE_FIXTURE(Fixture, "add_type_family_works")
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(requireType("a")) == "number");
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")

View File

@ -25,7 +25,7 @@ LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError)
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)
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();

View File

@ -39,7 +39,6 @@ Differ.negation
FrontendTest.environments
FrontendTest.imported_table_modification_2
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
GenericsTests.apply_type_function_nested_generics1
GenericsTests.better_mismatch_error_messages
@ -54,7 +53,7 @@ GenericsTests.generic_argument_count_too_few
GenericsTests.generic_argument_count_too_many
GenericsTests.generic_factories
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_unification1
GenericsTests.generic_type_pack_unification2
@ -246,18 +245,18 @@ TypeAliases.report_shadowed_aliases
TypeAliases.type_alias_local_mutation
TypeAliases.type_alias_local_rename
TypeAliases.type_alias_of_an_imported_recursive_generic_type
TypeFunctionTests.add_family_at_work
TypeFunctionTests.cyclic_add_family_at_work
TypeFunctionTests.cyclic_concat_family_at_work
TypeFunctionTests.add_function_at_work
TypeFunctionTests.cyclic_add_function_at_work
TypeFunctionTests.cyclic_concat_function_at_work
TypeFunctionTests.didnt_quite_exceed_distributivity_limits
TypeFunctionTests.ensure_equivalence_with_distributivity
TypeFunctionTests.family_as_fn_arg
TypeFunctionTests.index_type_family_works_w_generic_types
TypeFunctionTests.internal_families_raise_errors
TypeFunctionTests.function_as_fn_arg
TypeFunctionTests.index_type_function_works_w_generic_types
TypeFunctionTests.internal_functions_raise_errors
TypeFunctionTests.keyof_oss_crash_gh1161
TypeFunctionTests.mul_family_with_union_of_multiplicatives
TypeFunctionTests.mul_family_with_union_of_multiplicatives_2
TypeFunctionTests.unsolvable_family
TypeFunctionTests.mul_function_with_union_of_multiplicatives
TypeFunctionTests.mul_function_with_union_of_multiplicatives_2
TypeFunctionTests.unsolvable_function
TypeInfer.be_sure_to_use_active_txnlog_when_evaluating_a_variadic_overload
TypeInfer.check_type_infer_recursion_count
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_normalizer
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.cannot_unify_class_instance_with_primitive
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.inferring_hundreds_of_self_calls_should_not_suffocate_memory
TypeInferOOP.promise_type_error_too_complex
TypeInferOperators.add_type_family_works
TypeInferOperators.add_type_function_works
TypeInferOperators.cli_38355_recursive_union
TypeInferOperators.compound_assign_result_must_be_compatible_with_var
TypeInferOperators.concat_op_on_free_lhs_and_string_rhs