Skip to content

Commit

Permalink
Sync to upstream/release/532 (#545)
Browse files Browse the repository at this point in the history
  • Loading branch information
zeux committed Jun 17, 2022
1 parent 948f678 commit f1b46f4
Show file tree
Hide file tree
Showing 63 changed files with 2,170 additions and 721 deletions.
82 changes: 82 additions & 0 deletions Analysis/include/Luau/Constraint.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

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

#include <memory>
#include <vector>

namespace Luau
{

struct Scope2;
struct TypeVar;
using TypeId = const TypeVar*;

struct TypePackVar;
using TypePackId = const TypePackVar*;

// subType <: superType
struct SubtypeConstraint
{
TypeId subType;
TypeId superType;
};

// subPack <: superPack
struct PackSubtypeConstraint
{
TypePackId subPack;
TypePackId superPack;
};

// subType ~ gen superType
struct GeneralizationConstraint
{
TypeId generalizedType;
TypeId sourceType;
Scope2* scope;
};

// subType ~ inst superType
struct InstantiationConstraint
{
TypeId subType;
TypeId superType;
};

using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint>;
using ConstraintPtr = std::unique_ptr<struct Constraint>;

struct Constraint
{
Constraint(ConstraintV&& c, Location location);

Constraint(const Constraint&) = delete;
Constraint& operator=(const Constraint&) = delete;

ConstraintV c;
Location location;
std::vector<NotNull<Constraint>> dependencies;
};

inline Constraint& asMutable(const Constraint& c)
{
return const_cast<Constraint&>(c);
}

template<typename T>
T* getMutable(Constraint& c)
{
return ::Luau::get_if<T>(&c.c);
}

template<typename T>
const T* get(const Constraint& c)
{
return getMutable<T>(asMutable(c));
}

} // namespace Luau
113 changes: 46 additions & 67 deletions Analysis/include/Luau/ConstraintGraphBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,79 +4,19 @@

#include <memory>
#include <vector>
#include <unordered_map>

#include "Luau/Ast.h"
#include "Luau/Constraint.h"
#include "Luau/Module.h"
#include "Luau/NotNull.h"
#include "Luau/Symbol.h"
#include "Luau/TypeVar.h"
#include "Luau/Variant.h"

namespace Luau
{

struct Scope2;

// subType <: superType
struct SubtypeConstraint
{
TypeId subType;
TypeId superType;
};

// subPack <: superPack
struct PackSubtypeConstraint
{
TypePackId subPack;
TypePackId superPack;
};

// subType ~ gen superType
struct GeneralizationConstraint
{
TypeId subType;
TypeId superType;
Scope2* scope;
};

// subType ~ inst superType
struct InstantiationConstraint
{
TypeId subType;
TypeId superType;
};

using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint>;
using ConstraintPtr = std::unique_ptr<struct Constraint>;

struct Constraint
{
Constraint(ConstraintV&& c);
Constraint(ConstraintV&& c, std::vector<Constraint*> dependencies);

Constraint(const Constraint&) = delete;
Constraint& operator=(const Constraint&) = delete;

ConstraintV c;
std::vector<Constraint*> dependencies;
};

inline Constraint& asMutable(const Constraint& c)
{
return const_cast<Constraint&>(c);
}

template<typename T>
T* getMutable(Constraint& c)
{
return ::Luau::get_if<T>(&c.c);
}

template<typename T>
const T* get(const Constraint& c)
{
return getMutable<T>(asMutable(c));
}

struct Scope2
{
// The parent scope of this scope. Null if there is no parent (i.e. this
Expand All @@ -102,6 +42,11 @@ struct ConstraintGraphBuilder
TypeArena* const arena;
// The root scope of the module we're generating constraints for.
Scope2* rootScope;
// A mapping of AST node to TypeId.
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
// A mapping of AST node to TypePackId.
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};

explicit ConstraintGraphBuilder(TypeArena* arena);

Expand All @@ -128,8 +73,9 @@ struct ConstraintGraphBuilder
* Adds a new constraint with no dependencies to a given scope.
* @param scope the scope to add the constraint to. Must not be null.
* @param cv the constraint variant to add.
* @param location the location to attribute to the constraint.
*/
void addConstraint(Scope2* scope, ConstraintV cv);
void addConstraint(Scope2* scope, ConstraintV cv, Location location);

/**
* Adds a constraint to a given scope.
Expand All @@ -148,15 +94,48 @@ struct ConstraintGraphBuilder
void visit(Scope2* scope, AstStat* stat);
void visit(Scope2* scope, AstStatBlock* block);
void visit(Scope2* scope, AstStatLocal* local);
void visit(Scope2* scope, AstStatLocalFunction* local);
void visit(Scope2* scope, AstStatReturn* local);
void visit(Scope2* scope, AstStatLocalFunction* function);
void visit(Scope2* scope, AstStatFunction* function);
void visit(Scope2* scope, AstStatReturn* ret);
void visit(Scope2* scope, AstStatAssign* assign);
void visit(Scope2* scope, AstStatIf* ifStatement);

TypePackId checkExprList(Scope2* scope, const AstArray<AstExpr*>& exprs);

TypePackId checkPack(Scope2* scope, AstArray<AstExpr*> exprs);
TypePackId checkPack(Scope2* scope, AstExpr* expr);

/**
* Checks an expression that is expected to evaluate to one type.
* @param scope the scope the expression is contained within.
* @param expr the expression to check.
* @return the type of the expression.
*/
TypeId check(Scope2* scope, AstExpr* expr);

TypeId checkExprTable(Scope2* scope, AstExprTable* expr);
TypeId check(Scope2* scope, AstExprIndexName* indexName);

std::pair<TypeId, Scope2*> checkFunctionSignature(Scope2* parent, AstExprFunction* fn);

/**
* Checks the body of a function expression.
* @param scope the interior scope of the body of the function.
* @param fn the function expression to check.
*/
void checkFunctionBody(Scope2* scope, AstExprFunction* fn);
};

std::vector<const Constraint*> collectConstraints(Scope2* rootScope);
/**
* Collects a vector of borrowed constraints from the scope and all its child
* scopes. It is important to only call this function when you're done adding
* constraints to the scope or its descendants, lest the borrowed pointers
* become invalid due to a container reallocation.
* @param rootScope the root scope of the scope graph to collect constraints
* from.
* @return a list of pointers to constraints contained within the scope graph.
* None of these pointers should be null.
*/
std::vector<NotNull<Constraint>> collectConstraints(Scope2* rootScope);

} // namespace Luau
91 changes: 52 additions & 39 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

#include "Luau/Error.h"
#include "Luau/Variant.h"
#include "Luau/ConstraintGraphBuilder.h"
#include "Luau/Constraint.h"
#include "Luau/ConstraintSolverLogger.h"
#include "Luau/TypeVar.h"

#include <vector>
Expand All @@ -20,85 +21,97 @@ struct ConstraintSolver
{
TypeArena* arena;
InternalErrorReporter iceReporter;
// The entire set of constraints that the solver is trying to resolve.
std::vector<const Constraint*> constraints;
// The entire set of constraints that the solver is trying to resolve. It
// is important to not add elements to this vector, lest the underlying
// storage that we retain pointers to be mutated underneath us.
const std::vector<NotNull<Constraint>> constraints;
Scope2* rootScope;
std::vector<TypeError> errors;

// This includes every constraint that has not been fully solved.
// A constraint can be both blocked and unsolved, for instance.
std::unordered_set<const Constraint*> unsolvedConstraints;
std::vector<NotNull<const Constraint>> unsolvedConstraints;

// A mapping of constraint pointer to how many things the constraint is
// blocked on. Can be empty or 0 for constraints that are not blocked on
// anything.
std::unordered_map<const Constraint*, size_t> blockedConstraints;
std::unordered_map<NotNull<const Constraint>, size_t> blockedConstraints;
// A mapping of type/pack pointers to the constraints they block.
std::unordered_map<BlockedConstraintId, std::vector<const Constraint*>> blocked;
std::unordered_map<BlockedConstraintId, std::vector<NotNull<const Constraint>>> blocked;

ConstraintSolverLogger logger;

explicit ConstraintSolver(TypeArena* arena, Scope2* rootScope);

/**
* Attempts to dispatch all pending constraints and reach a type solution
* that satisfies all of the constraints, recording any errors that are
* encountered.
* that satisfies all of the constraints.
**/
void run();

bool done();

bool tryDispatch(const Constraint* c);
bool tryDispatch(const SubtypeConstraint& c);
bool tryDispatch(const PackSubtypeConstraint& c);
bool tryDispatch(const GeneralizationConstraint& c);
bool tryDispatch(const InstantiationConstraint& c, const Constraint* constraint);
bool tryDispatch(NotNull<const Constraint> c, bool force);
bool tryDispatch(const SubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const InstantiationConstraint& c, NotNull<const Constraint> constraint, bool force);

void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
/**
* Marks a constraint as being blocked on a type or type pack. The constraint
* solver will not attempt to dispatch blocked constraints until their
* dependencies have made progress.
* @param target the type or type pack pointer that the constraint is blocked on.
* @param constraint the constraint to block.
**/
void block_(BlockedConstraintId target, const Constraint* constraint);
void block(const Constraint* target, const Constraint* constraint);
void block(TypeId target, const Constraint* constraint);
void block(TypePackId target, const Constraint* constraint);
* Block a constraint on the resolution of a TypeVar.
* @returns false always. This is just to allow tryDispatch to return the result of block()
*/
bool block(TypeId target, NotNull<const Constraint> constraint);
bool block(TypePackId target, NotNull<const Constraint> constraint);

/**
* Informs the solver that progress has been made on a type or type pack. The
* solver will wake up all constraints that are blocked on the type or type pack,
* and will resume attempting to dispatch them.
* @param progressed the type or type pack pointer that has progressed.
**/
void unblock_(BlockedConstraintId progressed);
void unblock(const Constraint* progressed);
void unblock(NotNull<const Constraint> progressed);
void unblock(TypeId progressed);
void unblock(TypePackId progressed);

/**
* @returns true if the TypeId is in a blocked state.
*/
bool isBlocked(TypeId ty);

/**
* Returns whether the constraint is blocked on anything.
* @param constraint the constraint to check.
*/
bool isBlocked(const Constraint* constraint);

void reportErrors(const std::vector<TypeError>& errors);
bool isBlocked(NotNull<const Constraint> constraint);

/**
* Creates a new Unifier and performs a single unification operation. Commits
* the result and reports errors if necessary.
* the result.
* @param subType the sub-type to unify.
* @param superType the super-type to unify.
*/
void unify(TypeId subType, TypeId superType);
void unify(TypeId subType, TypeId superType, Location location);

/**
* Creates a new Unifier and performs a single unification operation. Commits
* the result and reports errors if necessary.
* the result.
* @param subPack the sub-type pack to unify.
* @param superPack the super-type pack to unify.
*/
void unify(TypePackId subPack, TypePackId superPack);
void unify(TypePackId subPack, TypePackId superPack, Location location);

private:
/**
* Marks a constraint as being blocked on a type or type pack. The constraint
* solver will not attempt to dispatch blocked constraints until their
* dependencies have made progress.
* @param target the type or type pack pointer that the constraint is blocked on.
* @param constraint the constraint to block.
**/
void block_(BlockedConstraintId target, NotNull<const Constraint> constraint);

/**
* Informs the solver that progress has been made on a type or type pack. The
* solver will wake up all constraints that are blocked on the type or type pack,
* and will resume attempting to dispatch them.
* @param progressed the type or type pack pointer that has progressed.
**/
void unblock_(BlockedConstraintId progressed);
};

void dump(Scope2* rootScope, struct ToStringOptions& opts);
Expand Down
Loading

0 comments on commit f1b46f4

Please sign in to comment.