Skip to content

Commit

Permalink
Sync to upstream/release/635 (#1337)
Browse files Browse the repository at this point in the history
# 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>
  • Loading branch information
9 people authored Jul 19, 2024
1 parent 2874ca9 commit a7299c3
Show file tree
Hide file tree
Showing 38 changed files with 463 additions and 384 deletions.
2 changes: 1 addition & 1 deletion Analysis/include/Luau/ConstraintGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ struct ConstraintGenerator
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.
Expand Down
12 changes: 6 additions & 6 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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,
Expand All @@ -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();

Expand Down Expand Up @@ -345,7 +345,7 @@ struct ConstraintSolver

/**
* 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);
Expand Down
2 changes: 1 addition & 1 deletion Analysis/include/Luau/OverloadResolution.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions Analysis/include/Luau/Subtyping.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,16 +219,16 @@ struct Subtyping
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);

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);
Expand Down
14 changes: 7 additions & 7 deletions Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
8 changes: 4 additions & 4 deletions Analysis/include/Luau/TypeArena.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 14 additions & 14 deletions Analysis/include/Luau/TypeFunctionReductionGuesser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -57,14 +57,14 @@ struct TypeFunctionReductionGuesser
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);

Expand All @@ -75,11 +75,11 @@ struct TypeFunctionReductionGuesser

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
2 changes: 1 addition & 1 deletion Analysis/include/Luau/TypePack.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ struct BlockedTypePack
*/
struct TypeFunctionInstanceTypePack
{
NotNull<const TypePackFunction> family;
NotNull<const TypePackFunction> function;

std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments;
Expand Down
4 changes: 2 additions & 2 deletions Analysis/include/Luau/Unifier2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 5 additions & 2 deletions Analysis/src/ConstraintGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
36 changes: 19 additions & 17 deletions Analysis/src/ConstraintSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ void ConstraintSolver::randomize(unsigned seed)

void ConstraintSolver::run()
{
LUAU_TIMETRACE_SCOPE("ConstraintSolver::run", "Typechecking");

if (isDone())
return;

Expand Down Expand Up @@ -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);
Expand All @@ -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))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions Analysis/src/DataFlowGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Luau/Def.h"
#include "Luau/Common.h"
#include "Luau/Error.h"
#include "Luau/TimeTrace.h"

#include <optional>

Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit a7299c3

Please sign in to comment.