Skip to content

Commit

Permalink
Sync to upstream/release/530 (#517)
Browse files Browse the repository at this point in the history
* Run clang-format
* Contains a preliminary implementation of deferred constraint resolution
* Reduce stack usage by some recursive functions
* Fix a bug when smartCloning a BoundTypeVar
* Remove some GC related flags from VM
  • Loading branch information
rblanckaert committed Jun 3, 2022
1 parent edd071f commit 55a0268
Show file tree
Hide file tree
Showing 43 changed files with 1,875 additions and 295 deletions.
2 changes: 2 additions & 0 deletions Analysis/include/Luau/Clone.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState);
TypeId clone(TypeId tp, TypeArena& dest, CloneState& cloneState);
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState);

TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log);

} // namespace Luau
162 changes: 162 additions & 0 deletions Analysis/include/Luau/ConstraintGraphBuilder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details

#pragma once

#include <memory>
#include <vector>

#include "Luau/Ast.h"
#include "Luau/Module.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
// is the module-level scope).
Scope2* parent = nullptr;
// All the children of this scope.
std::vector<Scope2*> children;
std::unordered_map<Symbol, TypeId> bindings; // TODO: I think this can be a DenseHashMap
TypePackId returnType;
// All constraints belonging to this scope.
std::vector<ConstraintPtr> constraints;

std::optional<TypeId> lookup(Symbol sym);
};

struct ConstraintGraphBuilder
{
// A list of all the scopes in the module. This vector holds ownership of the
// scope pointers; the scopes themselves borrow pointers to other scopes to
// define the scope hierarchy.
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scopes;
SingletonTypes& singletonTypes;
TypeArena* const arena;
// The root scope of the module we're generating constraints for.
Scope2* rootScope;

explicit ConstraintGraphBuilder(TypeArena* arena);

/**
* Fabricates a new free type belonging to a given scope.
* @param scope the scope the free type belongs to. Must not be null.
*/
TypeId freshType(Scope2* scope);

/**
* Fabricates a new free type pack belonging to a given scope.
* @param scope the scope the free type pack belongs to. Must not be null.
*/
TypePackId freshTypePack(Scope2* scope);

/**
* Fabricates a scope that is a child of another scope.
* @param location the lexical extent of the scope in the source code.
* @param parent the parent scope of the new scope. Must not be null.
*/
Scope2* childScope(Location location, Scope2* parent);

/**
* 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.
*/
void addConstraint(Scope2* scope, ConstraintV cv);

/**
* Adds a constraint to a given scope.
* @param scope the scope to add the constraint to. Must not be null.
* @param c the constraint to add.
*/
void addConstraint(Scope2* scope, std::unique_ptr<Constraint> c);

/**
* The entry point to the ConstraintGraphBuilder. This will construct a set
* of scopes, constraints, and free types that can be solved later.
* @param block the root block to generate constraints for.
*/
void visit(AstStatBlock* block);

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);

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

TypeId check(Scope2* scope, AstExpr* expr);
};

std::vector<const Constraint*> collectConstraints(Scope2* rootScope);

} // namespace Luau
106 changes: 106 additions & 0 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details

#pragma once

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

#include <vector>

namespace Luau
{

// TypeId, TypePackId, or Constraint*. It is impossible to know which, but we
// never dereference this pointer.
using BlockedConstraintId = const void*;

struct ConstraintSolver
{
TypeArena* arena;
InternalErrorReporter iceReporter;
// The entire set of constraints that the solver is trying to resolve.
std::vector<const 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;

// 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;
// A mapping of type/pack pointers to the constraints they block.
std::unordered_map<BlockedConstraintId, std::vector<const Constraint*>> blocked;

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.
**/
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);

/**
* 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);

/**
* 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(TypeId progressed);
void unblock(TypePackId progressed);

/**
* 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);

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

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

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

} // namespace Luau
2 changes: 2 additions & 0 deletions Analysis/include/Luau/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ struct Frontend
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);

private:
ModulePtr check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope);

std::pair<SourceNode*, SourceModule*> getSourceNode(CheckResult& checkResult, const ModuleName& name);
SourceModule parse(const ModuleName& name, std::string_view src, const ParseOptions& parseOptions);

Expand Down
3 changes: 3 additions & 0 deletions Analysis/include/Luau/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct Module;

using ScopePtr = std::shared_ptr<struct Scope>;
using ModulePtr = std::shared_ptr<Module>;
struct Scope2;

/// Root of the AST of a parsed source file
struct SourceModule
Expand Down Expand Up @@ -65,6 +66,7 @@ struct Module
std::shared_ptr<AstNameTable> names;

std::vector<std::pair<Location, ScopePtr>> scopes; // never empty
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scope2s; // never empty

DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
DenseHashMap<const AstExpr*, TypeId> astExpectedTypes{nullptr};
Expand All @@ -78,6 +80,7 @@ struct Module
bool timeout = false;

ScopePtr getModuleScope() const;
Scope2* getModuleScope2() const;

// Once a module has been typechecked, we clone its public interface into a separate arena.
// This helps us to force TypeVar ownership into a DAG rather than a DCG.
Expand Down
75 changes: 75 additions & 0 deletions Analysis/include/Luau/NotNull.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include "Luau/Common.h"

#include <functional>

namespace Luau
{

/** A non-owning, non-null pointer to a T.
*
* A NotNull<T> is notionally identical to a T* with the added restriction that it
* can never store nullptr.
*
* The sole conversion rule from T* to NotNull<T> is the single-argument constructor, which
* is intentionally marked explicit. This constructor performs a runtime test to verify
* that the passed pointer is never nullptr.
*
* Pointer arithmetic, increment, decrement, and array indexing are all forbidden.
*
* An implicit coersion from NotNull<T> to T* is afforded, as are the pointer indirection and member
* access operators. (*p and p->prop)
*
* The explicit delete statement is permitted on a NotNull<T> through this implicit conversion.
*/
template <typename T>
struct NotNull
{
explicit NotNull(T* t)
: ptr(t)
{
LUAU_ASSERT(t);
}

explicit NotNull(std::nullptr_t) = delete;
void operator=(std::nullptr_t) = delete;

operator T*() const noexcept
{
return ptr;
}

T& operator*() const noexcept
{
return *ptr;
}

T* operator->() const noexcept
{
return ptr;
}

T& operator[](int) = delete;

T& operator+(int) = delete;
T& operator-(int) = delete;

T* ptr;
};

}

namespace std
{

template <typename T> struct hash<Luau::NotNull<T>>
{
size_t operator()(const Luau::NotNull<T>& p) const
{
return std::hash<T*>()(p.ptr);
}
};

}
3 changes: 3 additions & 0 deletions Analysis/include/Luau/Quantify.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
namespace Luau
{

struct Scope2;

void quantify(TypeId ty, TypeLevel level);
void quantify(TypeId ty, Scope2* scope);

} // namespace Luau
Loading

0 comments on commit 55a0268

Please sign in to comment.