Skip to content

Commit

Permalink
Sync to upstream/release/631 (#1299)
Browse files Browse the repository at this point in the history
### What's new

* Added lint warning for using redundant `@native` attributes on
functions inside a `--!native` module
* Improved typechecking speed in old solver for modules with large types

### New Solver

* Fixed the length type function sealing the table prematurely
* Fixed crashes caused by general table indexing expressions

### VM

* Added support for a specialized 3-argument fast-call instruction to
improve performance of `vector` constructor, `buffer` writes and a few
`bit32` methods

---

### Internal Contributors

Co-authored-by: Aaron Weiss <[email protected]>
Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Aviral Goel <[email protected]>
Co-authored-by: Vighnesh Vijay <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>

---------

Co-authored-by: Aaron Weiss <[email protected]>
Co-authored-by: Alexander McCord <[email protected]>
Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Vighnesh <[email protected]>
Co-authored-by: Aviral Goel <[email protected]>
Co-authored-by: David Cope <[email protected]>
Co-authored-by: Lily Brown <[email protected]>
  • Loading branch information
8 people authored Jun 20, 2024
1 parent 7d40330 commit caee04d
Show file tree
Hide file tree
Showing 63 changed files with 1,420 additions and 1,147 deletions.
2 changes: 1 addition & 1 deletion Analysis/include/Luau/ConstraintSolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ struct ConstraintSolver
* @returns a non-free type that generalizes the argument, or `std::nullopt` if one
* does not exist
*/
std::optional<TypeId> generalizeFreeType(NotNull<Scope> scope, TypeId type);
std::optional<TypeId> generalizeFreeType(NotNull<Scope> scope, TypeId type, bool avoidSealingTables = false);

/**
* Checks the existing set of constraints to see if there exist any that contain
Expand Down
4 changes: 2 additions & 2 deletions Analysis/include/Luau/Generalization.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
namespace Luau
{

std::optional<TypeId> generalize(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope, NotNull<DenseHashSet<TypeId>> bakedTypes, TypeId ty);

std::optional<TypeId> generalize(NotNull<TypeArena> arena, NotNull<BuiltinTypes> builtinTypes, NotNull<Scope> scope,
NotNull<DenseHashSet<TypeId>> bakedTypes, TypeId ty, /* avoid sealing tables*/ bool avoidSealingTables = false);
}
10 changes: 10 additions & 0 deletions Analysis/include/Luau/Instantiation.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ struct ReplaceGenerics : Substitution
{
}

void resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope,
const std::vector<TypeId>& generics, const std::vector<TypePackId>& genericPacks);

NotNull<BuiltinTypes> builtinTypes;

TypeLevel level;
Scope* scope;
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;

bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
Expand All @@ -48,13 +52,19 @@ struct Instantiation : Substitution
, builtinTypes(builtinTypes)
, level(level)
, scope(scope)
, reusableReplaceGenerics(log, arena, builtinTypes, level, scope, {}, {})
{
}

void resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope);

NotNull<BuiltinTypes> builtinTypes;

TypeLevel level;
Scope* scope;

ReplaceGenerics reusableReplaceGenerics;

bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
Expand Down
5 changes: 4 additions & 1 deletion Analysis/include/Luau/Substitution.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ struct Tarjan
TarjanResult visitRoot(TypeId ty);
TarjanResult visitRoot(TypePackId ty);

void clearTarjan();
// Used to reuse the object for a new operation
void clearTarjan(const TxnLog* log);

// Get/set the dirty bit for an index (grows the vector if needed)
bool getDirty(int index);
Expand Down Expand Up @@ -212,6 +213,8 @@ struct Substitution : Tarjan
std::optional<TypeId> substitute(TypeId ty);
std::optional<TypePackId> substitute(TypePackId tp);

void resetState(const TxnLog* log, TypeArena* arena);

TypeId replace(TypeId ty);
TypePackId replace(TypePackId tp);

Expand Down
4 changes: 2 additions & 2 deletions Analysis/include/Luau/TypeFamily.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ struct TypeFamilyReductionResult
};

template<typename T>
using ReducerFunction = std::function<TypeFamilyReductionResult<T>(T, const std::vector<TypeId>&, const std::vector<TypePackId>&,
NotNull<TypeFamilyContext>)>;
using ReducerFunction =
std::function<TypeFamilyReductionResult<T>(T, const std::vector<TypeId>&, const std::vector<TypePackId>&, NotNull<TypeFamilyContext>)>;

/// Represents a type function that may be applied to map a series of types and
/// type packs to a single output type.
Expand Down
3 changes: 3 additions & 0 deletions Analysis/include/Luau/TypeInfer.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "Luau/Anyification.h"
#include "Luau/ControlFlow.h"
#include "Luau/Error.h"
#include "Luau/Instantiation.h"
#include "Luau/Module.h"
#include "Luau/Predicate.h"
#include "Luau/Substitution.h"
Expand Down Expand Up @@ -362,6 +363,8 @@ struct TypeChecker
UnifierSharedState unifierState;
Normalizer normalizer;

Instantiation reusableInstantiation;

std::vector<RequireCycle> requireCycles;

// Type inference limits
Expand Down
49 changes: 36 additions & 13 deletions Analysis/src/BuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,21 +256,44 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC

TypeId tableMetaMT = arena.addType(MetatableType{tabTy, genericMT});

// getmetatable : <MT>({ @metatable MT, {+ +} }) -> MT
addGlobalBinding(globals, "getmetatable", makeFunction(arena, std::nullopt, {genericMT}, {}, {tableMetaMT}, {genericMT}), "@luau");

// clang-format off
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
addGlobalBinding(globals, "setmetatable",
arena.addType(
FunctionType{
{genericMT},
{},
arena.addTypePack(TypePack{{tabTy, genericMT}}),
arena.addTypePack(TypePack{{tableMetaMT}})
}
), "@luau"
);
// clang-format on
if (FFlag::DebugLuauDeferredConstraintResolution)
{
TypeId genericT = arena.addType(GenericType{"T"});
TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT});

// clang-format off
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
addGlobalBinding(globals, "setmetatable",
arena.addType(
FunctionType{
{genericT, genericMT},
{},
arena.addTypePack(TypePack{{genericT, genericMT}}),
arena.addTypePack(TypePack{{tMetaMT}})
}
), "@luau"
);
// clang-format on
}
else
{
// clang-format off
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
addGlobalBinding(globals, "setmetatable",
arena.addType(
FunctionType{
{genericMT},
{},
arena.addTypePack(TypePack{{tabTy, genericMT}}),
arena.addTypePack(TypePack{{tableMetaMT}})
}
), "@luau"
);
// clang-format on
}

for (const auto& pair : globals.globalScope->bindings)
{
Expand Down
6 changes: 3 additions & 3 deletions Analysis/src/ConstraintGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat

auto uc = addConstraint(scope, statLocal->location, UnpackConstraint{valueTypes, rvaluePack});

for (TypeId t: valueTypes)
for (TypeId t : valueTypes)
getMutable<BlockedType>(t)->setOwner(uc);
}

Expand Down Expand Up @@ -920,7 +920,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
auto iterable = addConstraint(
loopScope, getLocation(forIn->values), IterableConstraint{iterator, variableTypes, forIn->values.data[0], &module->astForInNextTypes});

for (TypeId var: variableTypes)
for (TypeId var : variableTypes)
{
auto bt = getMutable<BlockedType>(var);
LUAU_ASSERT(bt);
Expand Down Expand Up @@ -1171,7 +1171,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatAssign* ass

auto uc = addConstraint(scope, assign->location, UnpackConstraint{valueTypes, resultPack});

for (TypeId t: valueTypes)
for (TypeId t : valueTypes)
getMutable<BlockedType>(t)->setOwner(uc);
}

Expand Down
39 changes: 17 additions & 22 deletions Analysis/src/ConstraintSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
}

for (TypeId ty : c.interiorTypes)
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty);
generalize(NotNull{arena}, builtinTypes, constraint->scope, generalizedTypes, ty, /* avoidSealingTables */ false);

return true;
}
Expand Down Expand Up @@ -769,13 +769,8 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
{
TypeId keyTy = freshType(arena, builtinTypes, constraint->scope);
TypeId valueTy = freshType(arena, builtinTypes, constraint->scope);
TypeId tableTy = arena->addType(TableType{
TableType::Props{},
TableIndexer{keyTy, valueTy},
TypeLevel{},
constraint->scope,
TableState::Free
});
TypeId tableTy =
arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free});

unify(constraint, nextTy, tableTy);

Expand Down Expand Up @@ -1022,13 +1017,10 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
const TableType* tfTable = getTableType(tf->type);

//clang-format off
bool needsClone =
follow(tf->type) == target ||
(tfTable != nullptr && tfTable == getTableType(target)) ||
std::any_of(typeArguments.begin(), typeArguments.end(), [&](const auto& other) {
return other == target;
}
);
bool needsClone = follow(tf->type) == target || (tfTable != nullptr && tfTable == getTableType(target)) ||
std::any_of(typeArguments.begin(), typeArguments.end(), [&](const auto& other) {
return other == target;
});
//clang-format on

// Only tables have the properties we're trying to set.
Expand Down Expand Up @@ -1446,6 +1438,8 @@ bool ConstraintSolver::tryDispatchHasIndexer(
bind(constraint, resultType, tbl->indexer->indexResultType);
return true;
}
else if (auto mt = get<MetatableType>(follow(ft->upperBound)))
return tryDispatchHasIndexer(recursionDepth, constraint, mt->table, indexType, resultType, seen);

FreeType freeResult{ft->scope, builtinTypes->neverType, builtinTypes->unknownType};
emplace<FreeType>(constraint, resultType, freeResult);
Expand All @@ -1461,11 +1455,11 @@ bool ConstraintSolver::tryDispatchHasIndexer(
if (auto indexer = tt->indexer)
{
unify(constraint, indexType, indexer->indexType);

bind(constraint, resultType, indexer->indexResultType);
return true;
}
else if (tt->state == TableState::Unsealed)

if (tt->state == TableState::Unsealed)
{
// FIXME this is greedy.

Expand Down Expand Up @@ -2067,7 +2061,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
}

auto unpack = [&](TypeId ty) {
for (TypeId varTy: c.variables)
for (TypeId varTy : c.variables)
{
LUAU_ASSERT(get<BlockedType>(varTy));
LUAU_ASSERT(varTy != ty);
Expand Down Expand Up @@ -2228,11 +2222,12 @@ bool ConstraintSolver::tryDispatchIterableFunction(
return true;
}

NotNull<const Constraint> ConstraintSolver::unpackAndAssign(const std::vector<TypeId> destTypes, TypePackId srcTypes, NotNull<const Constraint> constraint)
NotNull<const Constraint> ConstraintSolver::unpackAndAssign(
const std::vector<TypeId> destTypes, TypePackId srcTypes, NotNull<const Constraint> constraint)
{
auto c = pushConstraint(constraint->scope, constraint->location, UnpackConstraint{destTypes, srcTypes});

for (TypeId t: destTypes)
for (TypeId t : destTypes)
{
BlockedType* bt = getMutable<BlockedType>(t);
LUAU_ASSERT(bt);
Expand Down Expand Up @@ -2834,7 +2829,7 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
targetRefs += count;
}

std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope, TypeId type)
std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope, TypeId type, bool avoidSealingTables)
{
TypeId t = follow(type);
if (get<FreeType>(t))
Expand All @@ -2849,7 +2844,7 @@ std::optional<TypeId> ConstraintSolver::generalizeFreeType(NotNull<Scope> scope,
// that until all constraint generation is complete.
}

return generalize(NotNull{arena}, builtinTypes, scope, generalizedTypes, type);
return generalize(NotNull{arena}, builtinTypes, scope, generalizedTypes, type, avoidSealingTables);
}

bool ConstraintSolver::hasUnresolvedConstraints(TypeId ty)
Expand Down
16 changes: 5 additions & 11 deletions Analysis/src/Error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,15 @@ namespace Luau
{

// this list of binary operator type families is used for better stringification of type families errors
static const std::unordered_map<std::string, const char*> kBinaryOps{
{"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"idiv", "//"}, {"pow", "^"}, {"mod", "%"}, {"concat", ".."}, {"and", "and"},
{"or", "or"}, {"lt", "< or >="}, {"le", "<= or >"}, {"eq", "== or ~="}
};
static const std::unordered_map<std::string, const char*> kBinaryOps{{"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"idiv", "//"},
{"pow", "^"}, {"mod", "%"}, {"concat", ".."}, {"and", "and"}, {"or", "or"}, {"lt", "< or >="}, {"le", "<= or >"}, {"eq", "== or ~="}};

// this list of unary operator type families is used for better stringification of type families errors
static const std::unordered_map<std::string, const char*> kUnaryOps{
{"unm", "-"}, {"len", "#"}, {"not", "not"}
};
static const std::unordered_map<std::string, const char*> kUnaryOps{{"unm", "-"}, {"len", "#"}, {"not", "not"}};

// this list of type families will receive a special error indicating that the user should file a bug on the GitHub repository
// putting a type family in this list indicates that it is expected to _always_ reduce
static const std::unordered_set<std::string> kUnreachableTypeFamilies{
"refine", "singleton", "union", "intersect"
};
static const std::unordered_set<std::string> kUnreachableTypeFamilies{"refine", "singleton", "union", "intersect"};

struct ErrorConverter
{
Expand Down Expand Up @@ -682,7 +676,7 @@ struct ErrorConverter
if (kUnreachableTypeFamilies.count(tfit->family->name))
{
return "Type family 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";
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";
}

// Everything should be specialized above to report a more descriptive error that hopefully does not mention "type families" explicitly.
Expand Down
Loading

0 comments on commit caee04d

Please sign in to comment.