Skip to content

Commit

Permalink
Sync to upstream/release/589 (#1000)
Browse files Browse the repository at this point in the history
* Progress toward a diffing algorithm for types. We hope that this will
be useful for writing clearer error messages.
* Add a missing recursion limiter in `Unifier::tryUnifyTables`. This was
causing a crash in certain situations.
* Luau heap graph enumeration improvements:
    * Weak references are not reported
    * Added tag as a fallback name of non-string table links
* Included top Luau function information in thread name to understand
where thread might be suspended
* Constant folding for `math.pi` and `math.huge` at -O2
* Optimize `string.format` and `%*`
* This change makes string interpolation 1.5x-2x faster depending on the
number and type of formatted components, assuming a few are using
primitive types, and reduces associated GC pressure.

New type checker:

* Initial work toward tracking the upper and lower bounds of types
accurately.

Native code generation (JIT):

* Add IrCmd::CHECK_TRUTHY for improved assert fast-calls
* Do not compute type map for modules without types
* Capture metatable+readonly state for NEW_TABLE IR instructions
* Replace JUMP_CMP_ANY with CMP_ANY and existing JUMP_EQ_INT
* Add support for exits to VM with reentry lock in VmExit

---------

Co-authored-by: Arseny Kapoulkine <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>
  • Loading branch information
3 people authored Aug 4, 2023
1 parent fff897a commit 0b2755f
Show file tree
Hide file tree
Showing 78 changed files with 3,162 additions and 419 deletions.
5 changes: 4 additions & 1 deletion Analysis/include/Luau/ConstraintGraphBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "Luau/Type.h"
#include "Luau/TypeUtils.h"
#include "Luau/Variant.h"
#include "Normalize.h"

#include <memory>
#include <vector>
Expand Down Expand Up @@ -86,6 +87,8 @@ struct ConstraintGraphBuilder
// It is pretty uncommon for constraint generation to itself produce errors, but it can happen.
std::vector<TypeError> errors;

// Needed to be able to enable error-suppression preservation for immediate refinements.
NotNull<Normalizer> normalizer;
// Needed to resolve modules to make 'require' import types properly.
NotNull<ModuleResolver> moduleResolver;
// Occasionally constraint generation needs to produce an ICE.
Expand All @@ -98,7 +101,7 @@ struct ConstraintGraphBuilder

DcrLogger* logger;

ConstraintGraphBuilder(ModulePtr module, TypeArena* arena, NotNull<ModuleResolver> moduleResolver, NotNull<BuiltinTypes> builtinTypes,
ConstraintGraphBuilder(ModulePtr module, NotNull<Normalizer> normalizer, NotNull<ModuleResolver> moduleResolver, NotNull<BuiltinTypes> builtinTypes,
NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope, std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
DcrLogger* logger, NotNull<DataFlowGraph> dfg, std::vector<RequireCycle> requireCycles);

Expand Down
4 changes: 2 additions & 2 deletions Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,15 +204,15 @@ struct ConstraintSolver
* @param subType the sub-type to unify.
* @param superType the super-type to unify.
*/
ErrorVec unify(TypeId subType, TypeId superType, NotNull<Scope> scope);
ErrorVec unify(NotNull<Scope> scope, Location location, TypeId subType, TypeId superType);

/**
* Creates a new Unifier and performs a single unification operation. Commits
* the result.
* @param subPack the sub-type pack to unify.
* @param superPack the super-type pack to unify.
*/
ErrorVec unify(TypePackId subPack, TypePackId superPack, NotNull<Scope> scope);
ErrorVec unify(NotNull<Scope> scope, Location location, TypePackId subPack, TypePackId superPack);

/** Pushes a new solver constraint to the solver.
* @param cv the body of the constraint.
Expand Down
26 changes: 25 additions & 1 deletion Analysis/include/Luau/Differ.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "Luau/DenseHash.h"
#include "Luau/Type.h"
#include "Luau/UnifierSharedState.h"
#include <optional>
#include <string>
#include <unordered_map>
Expand Down Expand Up @@ -151,8 +152,31 @@ struct DifferEnvironment
{
TypeId rootLeft;
TypeId rootRight;

DenseHashMap<TypeId, TypeId> genericMatchedPairs;
DenseHashMap<TypePackId, TypePackId> genericTpMatchedPairs;

DifferEnvironment(TypeId rootLeft, TypeId rootRight)
: rootLeft(rootLeft)
, rootRight(rootRight)
, genericMatchedPairs(nullptr)
, genericTpMatchedPairs(nullptr)
{
}

bool isProvenEqual(TypeId left, TypeId right) const;
bool isAssumedEqual(TypeId left, TypeId right) const;
void recordProvenEqual(TypeId left, TypeId right);
void pushVisiting(TypeId left, TypeId right);
void popVisiting();
std::vector<std::pair<TypeId, TypeId>>::const_reverse_iterator visitingBegin() const;
std::vector<std::pair<TypeId, TypeId>>::const_reverse_iterator visitingEnd() const;

private:
// TODO: consider using DenseHashSet
std::unordered_set<std::pair<TypeId, TypeId>, TypeIdPairHash> provenEqual;
// Ancestors of current types
std::unordered_set<std::pair<TypeId, TypeId>, TypeIdPairHash> visiting;
std::vector<std::pair<TypeId, TypeId>> visitingStack;
};
DifferResult diff(TypeId ty1, TypeId ty2);

Expand Down
31 changes: 28 additions & 3 deletions Analysis/include/Luau/Instantiation.h
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once

#include "Luau/NotNull.h"
#include "Luau/Substitution.h"
#include "Luau/Type.h"
#include "Luau/Unifiable.h"

namespace Luau
{

struct TypeArena;
struct BuiltinTypes;
struct TxnLog;
struct TypeArena;
struct TypeCheckLimits;

// A substitution which replaces generic types in a given set by free types.
struct ReplaceGenerics : Substitution
{
ReplaceGenerics(const TxnLog* log, TypeArena* arena, TypeLevel level, Scope* scope, const std::vector<TypeId>& generics,
ReplaceGenerics(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope, const std::vector<TypeId>& generics,
const std::vector<TypePackId>& genericPacks)
: Substitution(log, arena)
, builtinTypes(builtinTypes)
, level(level)
, scope(scope)
, generics(generics)
, genericPacks(genericPacks)
{
}

NotNull<BuiltinTypes> builtinTypes;

TypeLevel level;
Scope* scope;
std::vector<TypeId> generics;
Expand All @@ -38,13 +44,16 @@ struct ReplaceGenerics : Substitution
// A substitution which replaces generic functions by monomorphic functions
struct Instantiation : Substitution
{
Instantiation(const TxnLog* log, TypeArena* arena, TypeLevel level, Scope* scope)
Instantiation(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
: Substitution(log, arena)
, builtinTypes(builtinTypes)
, level(level)
, scope(scope)
{
}

NotNull<BuiltinTypes> builtinTypes;

TypeLevel level;
Scope* scope;
bool ignoreChildren(TypeId ty) override;
Expand All @@ -54,4 +63,20 @@ struct Instantiation : Substitution
TypePackId clean(TypePackId tp) override;
};

/** Attempt to instantiate a type. Only used under local type inference.
*
* When given a generic function type, instantiate() will return a copy with the
* generics replaced by fresh types. Instantiation will return the same TypeId
* back if the function does not have any generics.
*
* All higher order generics are left as-is. For example, instantiation of
* <X>(<Y>(Y) -> (X, Y)) -> (X, Y) is (<Y>(Y) -> ('x, Y)) -> ('x, Y)
*
* We substitute the generic X for the free 'x, but leave the generic Y alone.
*
* Instantiation fails only when processing the type causes internal recursion
* limits to be exceeded.
*/
std::optional<TypeId> instantiate(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, NotNull<TypeCheckLimits> limits, NotNull<Scope> scope, TypeId ty);

} // namespace Luau
10 changes: 9 additions & 1 deletion Analysis/include/Luau/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@ using TypeId = const Type*;

using Name = std::string;

// A free type var is one whose exact shape has yet to be fully determined.
// A free type is one whose exact shape has yet to be fully determined.
struct FreeType
{
explicit FreeType(TypeLevel level);
explicit FreeType(Scope* scope);
FreeType(Scope* scope, TypeLevel level);

FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound);

int index;
TypeLevel level;
Scope* scope = nullptr;
Expand All @@ -92,6 +94,10 @@ struct FreeType
// recursive type alias whose definitions haven't been
// resolved yet.
bool forwardedTypeAlias = false;

// Only used under local type inference
TypeId lowerBound = nullptr;
TypeId upperBound = nullptr;
};

struct GenericType
Expand Down Expand Up @@ -994,6 +1000,8 @@ struct TypeIterator
}
};

TypeId freshType(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, Scope* scope);

using TypeIdPredicate = std::function<std::optional<TypeId>(TypeId)>;
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);

Expand Down
6 changes: 4 additions & 2 deletions Analysis/include/Luau/TypeChecker2.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
namespace Luau
{

struct DcrLogger;
struct BuiltinTypes;
struct DcrLogger;
struct TypeCheckLimits;
struct UnifierSharedState;

void check(NotNull<BuiltinTypes> builtinTypes, NotNull<struct UnifierSharedState> sharedState, DcrLogger* logger, const SourceModule& sourceModule,
void check(NotNull<BuiltinTypes> builtinTypes, NotNull<UnifierSharedState> sharedState, NotNull<TypeCheckLimits> limits, DcrLogger* logger, const SourceModule& sourceModule,
Module* module);

} // namespace Luau
75 changes: 75 additions & 0 deletions Analysis/include/Luau/Unifier2.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/DenseHash.h"
#include "Luau/NotNull.h"

#include <optional>
#include <vector>
#include <utility>

namespace Luau
{

using TypeId = const struct Type*;
using TypePackId = const struct TypePackVar*;

struct BuiltinTypes;
struct InternalErrorReporter;
struct Scope;
struct TypeArena;

enum class OccursCheckResult
{
Pass,
Fail
};

struct Unifier2
{
NotNull<TypeArena> arena;
NotNull<BuiltinTypes> builtinTypes;
NotNull<InternalErrorReporter> ice;

int recursionCount = 0;
int recursionLimit = 0;

Unifier2(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> ice);

/** Attempt to commit the subtype relation subTy <: superTy to the type
* graph.
*
* @returns true if successful.
*
* Note that incoherent types can and will successfully be unified. We stop
* when we *cannot know* how to relate the provided types, not when doing so
* would narrow something down to never or broaden it to unknown.
*
* Presently, the only way unification can fail is if we attempt to bind one
* free TypePack to another and encounter an occurs check violation.
*/
bool unify(TypeId subTy, TypeId superTy);

// TODO think about this one carefully. We don't do unions or intersections of type packs
bool unify(TypePackId subTp, TypePackId superTp);

std::optional<TypeId> generalize(NotNull<Scope> scope, TypeId ty);
private:

/**
* @returns simplify(left | right)
*/
TypeId mkUnion(TypeId left, TypeId right);

/**
* @returns simplify(left & right)
*/
TypeId mkIntersection(TypeId left, TypeId right);

// Returns true if needle occurs within haystack already. ie if we bound
// needle to haystack, would a cyclic TypePack result?
OccursCheckResult occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
};

}
6 changes: 3 additions & 3 deletions Analysis/include/Luau/Variant.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// 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 <initializer_list>
#include <new>
#include <type_traits>
#include <initializer_list>
#include <stddef.h>
#include <utility>

#include <stddef.h>

namespace Luau
{

Expand Down
12 changes: 6 additions & 6 deletions Analysis/src/Autocomplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

LUAU_FASTFLAG(DebugLuauReadWriteProperties)
LUAU_FASTFLAGVARIABLE(LuauDisableCompletionOutsideQuotes, false)
LUAU_FASTFLAGVARIABLE(LuauAnonymousAutofilled, false);
LUAU_FASTFLAGVARIABLE(LuauAnonymousAutofilled1, false);
LUAU_FASTFLAGVARIABLE(LuauAutocompleteLastTypecheck, false)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteHideSelfArg, false)

Expand Down Expand Up @@ -618,7 +618,7 @@ std::optional<TypeId> getLocalTypeInScopeAt(const Module& module, Position posit
template <typename T>
static std::optional<std::string> tryToStringDetailed(const ScopePtr& scope, T ty, bool functionTypeArguments)
{
LUAU_ASSERT(FFlag::LuauAnonymousAutofilled);
LUAU_ASSERT(FFlag::LuauAnonymousAutofilled1);
ToStringOptions opts;
opts.useLineBreaks = false;
opts.hideTableKind = true;
Expand All @@ -637,7 +637,7 @@ static std::optional<Name> tryGetTypeNameInScope(ScopePtr scope, TypeId ty, bool
if (!canSuggestInferredType(scope, ty))
return std::nullopt;

if (FFlag::LuauAnonymousAutofilled)
if (FFlag::LuauAnonymousAutofilled1)
{
return tryToStringDetailed(scope, ty, functionTypeArguments);
}
Expand Down Expand Up @@ -1419,7 +1419,7 @@ static AutocompleteResult autocompleteWhileLoopKeywords(std::vector<AstNode*> an

static std::string makeAnonymous(const ScopePtr& scope, const FunctionType& funcTy)
{
LUAU_ASSERT(FFlag::LuauAnonymousAutofilled);
LUAU_ASSERT(FFlag::LuauAnonymousAutofilled1);
std::string result = "function(";

auto [args, tail] = Luau::flatten(funcTy.argTypes);
Expand Down Expand Up @@ -1485,7 +1485,7 @@ static std::string makeAnonymous(const ScopePtr& scope, const FunctionType& func

static std::optional<AutocompleteEntry> makeAnonymousAutofilled(const ModulePtr& module, Position position, const AstNode* node, const std::vector<AstNode*>& ancestry)
{
LUAU_ASSERT(FFlag::LuauAnonymousAutofilled);
LUAU_ASSERT(FFlag::LuauAnonymousAutofilled1);
const AstExprCall* call = node->as<AstExprCall>();
if (!call && ancestry.size() > 1)
call = ancestry[ancestry.size() - 2]->as<AstExprCall>();
Expand Down Expand Up @@ -1803,7 +1803,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M

if (node->asExpr())
{
if (FFlag::LuauAnonymousAutofilled)
if (FFlag::LuauAnonymousAutofilled1)
{
AutocompleteResult ret = autocompleteExpression(sourceModule, *module, builtinTypes, typeArena, ancestry, position);
if (std::optional<AutocompleteEntry> generated = makeAnonymousAutofilled(module, position, node, ancestry))
Expand Down
15 changes: 13 additions & 2 deletions Analysis/src/Clone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
LUAU_FASTFLAG(DebugLuauCopyBeforeNormalizing)
LUAU_FASTFLAG(DebugLuauReadWriteProperties)

LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTINTVARIABLE(LuauTypeCloneRecursionLimit, 300)
LUAU_FASTFLAGVARIABLE(LuauCloneCyclicUnions, false)

Expand Down Expand Up @@ -204,7 +205,14 @@ void TypeCloner::defaultClone(const T& t)

void TypeCloner::operator()(const FreeType& t)
{
defaultClone(t);
if (FFlag::DebugLuauDeferredConstraintResolution)
{
FreeType ft{t.scope, clone(t.lowerBound, dest, cloneState), clone(t.upperBound, dest, cloneState)};
TypeId res = dest.addType(ft);
seenTypes[typeId] = res;
}
else
defaultClone(t);
}

void TypeCloner::operator()(const GenericType& t)
Expand Down Expand Up @@ -363,7 +371,10 @@ void TypeCloner::operator()(const UnionType& t)
{
if (FFlag::LuauCloneCyclicUnions)
{
TypeId result = dest.addType(FreeType{nullptr});
// We're just using this FreeType as a placeholder until we've finished
// cloning the parts of this union so it is okay that its bounds are
// nullptr. We'll never indirect them.
TypeId result = dest.addType(FreeType{nullptr, /*lowerBound*/nullptr, /*upperBound*/nullptr});
seenTypes[typeId] = result;

std::vector<TypeId> options;
Expand Down
Loading

0 comments on commit 0b2755f

Please sign in to comment.