Skip to content

Commit

Permalink
Sync to upstream/release/659 (#1637)
Browse files Browse the repository at this point in the history
## What's Changed

General performance improvements and bug fixes. `lua_clonetable` was
added too.

### General

## Runtime
- Improvements were made to Luau's performance, including a
`lua_clonetable` function and optimizations to string caching. Buffer
read/write operations were optimized for big-endian machines.
## New Solver
- Crashes related to duplicate keys in table literals, fragment AC
crashes, and potential hash collisions in the StringCache.
- We now handle user-defined type functions as opaque and track interior
free table types.
## Require By String
- Require-by-string path resolution was simplified.

**Full Changelog**:
0.658...0.659

---

Co-authored-by: Ariel Weiss <[email protected]>
Co-authored-by: Andy Friesen <[email protected]>
Co-authored-by: Hunter Goldstein <[email protected]>
Co-authored-by: Vyacheslav Egorov <[email protected]>
Co-authored-by: Varun Saini <[email protected]>
Co-authored-by: Vighnesh Vijay <[email protected]>
Co-authored-by: Yohoo Lin <[email protected]>

---------

Co-authored-by: Hunter Goldstein <[email protected]>
Co-authored-by: Varun Saini <[email protected]>
Co-authored-by: Alexander Youngblood <[email protected]>
  • Loading branch information
4 people authored Feb 1, 2025
1 parent c13b5b7 commit f8a1e01
Show file tree
Hide file tree
Showing 45 changed files with 864 additions and 894 deletions.
2 changes: 1 addition & 1 deletion Analysis/include/Luau/EqSatSimplificationImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ using EType = EqSat::Language<
struct StringCache
{
Allocator allocator;
DenseHashMap<size_t, StringId> strings{{}};
DenseHashMap<std::string_view, StringId> strings{{}};
std::vector<std::string_view> views;

StringId add(std::string_view s);
Expand Down
14 changes: 7 additions & 7 deletions Analysis/include/Luau/TypeChecker2.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void check(
NotNull<BuiltinTypes> builtinTypes,
NotNull<Simplifier> simplifier,
NotNull<TypeFunctionRuntime> typeFunctionRuntime,
NotNull<UnifierSharedState> sharedState,
NotNull<UnifierSharedState> unifierState,
NotNull<TypeCheckLimits> limits,
DcrLogger* logger,
const SourceModule& sourceModule,
Expand Down Expand Up @@ -116,14 +116,14 @@ struct TypeChecker2
std::optional<StackPusher> pushStack(AstNode* node);
void checkForInternalTypeFunction(TypeId ty, Location location);
TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location);
TypePackId lookupPack(AstExpr* expr);
TypePackId lookupPack(AstExpr* expr) const;
TypeId lookupType(AstExpr* expr);
TypeId lookupAnnotation(AstType* annotation);
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation);
TypeId lookupExpectedType(AstExpr* expr);
TypePackId lookupExpectedPack(AstExpr* expr, TypeArena& arena);
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation) const;
TypeId lookupExpectedType(AstExpr* expr) const;
TypePackId lookupExpectedPack(AstExpr* expr, TypeArena& arena) const;
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena);
Scope* findInnermostScope(Location location);
Scope* findInnermostScope(Location location) const;
void visit(AstStat* stat);
void visit(AstStatIf* ifStatement);
void visit(AstStatWhile* whileStatement);
Expand Down Expand Up @@ -160,7 +160,7 @@ struct TypeChecker2
void visit(AstExprVarargs* expr);
void visitCall(AstExprCall* call);
void visit(AstExprCall* call);
std::optional<TypeId> tryStripUnionFromNil(TypeId ty);
std::optional<TypeId> tryStripUnionFromNil(TypeId ty) const;
TypeId stripFromNilAndReport(TypeId ty, const Location& location);
void visitExprName(AstExpr* expr, Location location, const std::string& propName, ValueContext context, TypeId astIndexExprTy);
void visit(AstExprIndexName* indexName, ValueContext context);
Expand Down
2 changes: 1 addition & 1 deletion Analysis/include/Luau/TypeUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct InConditionalContext
TypeContext* typeContext;
TypeContext oldValue;

InConditionalContext(TypeContext* c)
explicit InConditionalContext(TypeContext* c)
: typeContext(c)
, oldValue(*c)
{
Expand Down
48 changes: 22 additions & 26 deletions Analysis/src/BuiltinDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins2)
LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix)
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2)
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType)
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType2)
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)

namespace Luau
Expand Down Expand Up @@ -310,28 +309,25 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
addGlobalBinding(globals, "string", it->second.type(), "@luau");

// Setup 'vector' metatable
if (FFlag::LuauVectorDefinitionsExtra)
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end())
{
if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end())
{
TypeId vectorTy = it->second.type;
ClassType* vectorCls = getMutable<ClassType>(vectorTy);

vectorCls->metatable = arena.addType(TableType{{}, std::nullopt, TypeLevel{}, TableState::Sealed});
TableType* metatableTy = Luau::getMutable<TableType>(vectorCls->metatable);

metatableTy->props["__add"] = {makeFunction(arena, vectorTy, {vectorTy}, {vectorTy})};
metatableTy->props["__sub"] = {makeFunction(arena, vectorTy, {vectorTy}, {vectorTy})};
metatableTy->props["__unm"] = {makeFunction(arena, vectorTy, {}, {vectorTy})};

std::initializer_list<TypeId> mulOverloads{
makeFunction(arena, vectorTy, {vectorTy}, {vectorTy}),
makeFunction(arena, vectorTy, {builtinTypes->numberType}, {vectorTy}),
};
metatableTy->props["__mul"] = {makeIntersection(arena, mulOverloads)};
metatableTy->props["__div"] = {makeIntersection(arena, mulOverloads)};
metatableTy->props["__idiv"] = {makeIntersection(arena, mulOverloads)};
}
TypeId vectorTy = it->second.type;
ClassType* vectorCls = getMutable<ClassType>(vectorTy);

vectorCls->metatable = arena.addType(TableType{{}, std::nullopt, TypeLevel{}, TableState::Sealed});
TableType* metatableTy = Luau::getMutable<TableType>(vectorCls->metatable);

metatableTy->props["__add"] = {makeFunction(arena, vectorTy, {vectorTy}, {vectorTy})};
metatableTy->props["__sub"] = {makeFunction(arena, vectorTy, {vectorTy}, {vectorTy})};
metatableTy->props["__unm"] = {makeFunction(arena, vectorTy, {}, {vectorTy})};

std::initializer_list<TypeId> mulOverloads{
makeFunction(arena, vectorTy, {vectorTy}, {vectorTy}),
makeFunction(arena, vectorTy, {builtinTypes->numberType}, {vectorTy}),
};
metatableTy->props["__mul"] = {makeIntersection(arena, mulOverloads)};
metatableTy->props["__div"] = {makeIntersection(arena, mulOverloads)};
metatableTy->props["__idiv"] = {makeIntersection(arena, mulOverloads)};
}

// next<K, V>(t: Table<K, V>, i: K?) -> (K?, V)
Expand Down Expand Up @@ -453,7 +449,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
ttv->props["foreachi"].deprecated = true;

attachMagicFunction(ttv->props["pack"].type(), std::make_shared<MagicPack>());
if (FFlag::LuauTableCloneClonesType)
if (FFlag::LuauTableCloneClonesType2)
attachMagicFunction(ttv->props["clone"].type(), std::make_shared<MagicClone>());
if (FFlag::LuauTypestateBuiltins2)
attachMagicFunction(ttv->props["freeze"].type(), std::make_shared<MagicFreeze>());
Expand Down Expand Up @@ -1405,7 +1401,7 @@ std::optional<WithPredicate<TypePackId>> MagicClone::handleOldSolver(
WithPredicate<TypePackId> withPredicate
)
{
LUAU_ASSERT(FFlag::LuauTableCloneClonesType);
LUAU_ASSERT(FFlag::LuauTableCloneClonesType2);

auto [paramPack, _predicates] = withPredicate;

Expand All @@ -1429,7 +1425,7 @@ std::optional<WithPredicate<TypePackId>> MagicClone::handleOldSolver(

bool MagicClone::infer(const MagicFunctionCallContext& context)
{
LUAU_ASSERT(FFlag::LuauTableCloneClonesType);
LUAU_ASSERT(FFlag::LuauTableCloneClonesType2);

TypeArena* arena = context.solver->arena;

Expand Down
160 changes: 61 additions & 99 deletions Analysis/src/ConstraintGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,9 @@ LUAU_FASTINT(LuauCheckRecursionLimit)
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauTypestateBuiltins2)
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)

LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunExportedAndLocal)
LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses)
LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunNoExtraConstraint)
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope)

LUAU_FASTFLAGVARIABLE(InferGlobalTypes)
Expand Down Expand Up @@ -743,12 +739,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
continue;
}

if (!FFlag::LuauUserTypeFunExportedAndLocal && scope->parent != globalScope)
{
reportError(function->location, GenericError{"Local user-defined functions are not supported yet"});
continue;
}

ScopePtr defnScope = childScope(function, scope);

// Create TypeFunctionInstanceType
Expand All @@ -774,11 +764,8 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc

UserDefinedFunctionData udtfData;

if (FFlag::LuauUserTypeFunExportedAndLocal)
{
udtfData.owner = module;
udtfData.definition = function;
}
udtfData.owner = module;
udtfData.definition = function;

TypeId typeFunctionTy = arena->addType(
TypeFunctionInstanceType{NotNull{&builtinTypeFunctions().userFunc}, std::move(typeParams), {}, function->name, udtfData}
Expand All @@ -787,7 +774,7 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
TypeFun typeFunction{std::move(quantifiedTypeParams), typeFunctionTy};

// Set type bindings and definition locations for this user-defined type function
if (FFlag::LuauUserTypeFunExportedAndLocal && function->exported)
if (function->exported)
scope->exportedTypeBindings[function->name.value] = std::move(typeFunction);
else
scope->privateTypeBindings[function->name.value] = std::move(typeFunction);
Expand Down Expand Up @@ -822,77 +809,74 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
}
}

if (FFlag::LuauUserTypeFunExportedAndLocal)
// Additional pass for user-defined type functions to fill in their environments completely
for (AstStat* stat : block->body)
{
// Additional pass for user-defined type functions to fill in their environments completely
for (AstStat* stat : block->body)
if (auto function = stat->as<AstStatTypeFunction>())
{
if (auto function = stat->as<AstStatTypeFunction>())
{
// Find the type function we have already created
TypeFunctionInstanceType* mainTypeFun = nullptr;
// Find the type function we have already created
TypeFunctionInstanceType* mainTypeFun = nullptr;

if (auto it = scope->privateTypeBindings.find(function->name.value); it != scope->privateTypeBindings.end())
if (auto it = scope->privateTypeBindings.find(function->name.value); it != scope->privateTypeBindings.end())
mainTypeFun = getMutable<TypeFunctionInstanceType>(it->second.type);

if (!mainTypeFun)
{
if (auto it = scope->exportedTypeBindings.find(function->name.value); it != scope->exportedTypeBindings.end())
mainTypeFun = getMutable<TypeFunctionInstanceType>(it->second.type);
}

if (!mainTypeFun)
{
if (auto it = scope->exportedTypeBindings.find(function->name.value); it != scope->exportedTypeBindings.end())
mainTypeFun = getMutable<TypeFunctionInstanceType>(it->second.type);
}
// Fill it with all visible type functions
if (mainTypeFun)
{
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
size_t level = 0;

// Fill it with all visible type functions
if (FFlag::LuauUserTypeFunUpdateAllEnvs && mainTypeFun)
for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
{
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
size_t level = 0;

for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
for (auto& [name, tf] : curr->privateTypeBindings)
{
for (auto& [name, tf] : curr->privateTypeBindings)
{
if (userFuncData.environment.find(name))
continue;
if (userFuncData.environment.find(name))
continue;

if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
}

for (auto& [name, tf] : curr->exportedTypeBindings)
{
if (userFuncData.environment.find(name))
continue;
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
}

if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
}
for (auto& [name, tf] : curr->exportedTypeBindings)
{
if (userFuncData.environment.find(name))
continue;

level++;
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
}

level++;
}
else if (mainTypeFun)
}
else if (mainTypeFun)
{
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;

for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
{
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
for (auto& [name, tf] : curr->privateTypeBindings)
{
if (userFuncData.environment_DEPRECATED.find(name))
continue;

for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
userFuncData.environment_DEPRECATED[name] = ty->userFuncData.definition;
}

for (auto& [name, tf] : curr->exportedTypeBindings)
{
for (auto& [name, tf] : curr->privateTypeBindings)
{
if (userFuncData.environment_DEPRECATED.find(name))
continue;

if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
userFuncData.environment_DEPRECATED[name] = ty->userFuncData.definition;
}

for (auto& [name, tf] : curr->exportedTypeBindings)
{
if (userFuncData.environment_DEPRECATED.find(name))
continue;

if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
userFuncData.environment_DEPRECATED[name] = ty->userFuncData.definition;
}
if (userFuncData.environment_DEPRECATED.find(name))
continue;

if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
userFuncData.environment_DEPRECATED[name] = ty->userFuncData.definition;
}
}
}
Expand Down Expand Up @@ -1622,24 +1606,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeAlias*

ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunction* function)
{
if (!FFlag::LuauUserTypeFunNoExtraConstraint)
{
// If a type function with the same name was already defined, we skip over
auto bindingIt = scope->privateTypeBindings.find(function->name.value);
if (bindingIt == scope->privateTypeBindings.end())
return ControlFlow::None;

TypeFun typeFunction = bindingIt->second;

// Adding typeAliasExpansionConstraint on user-defined type function for the constraint solver
if (auto typeFunctionTy = get<TypeFunctionInstanceType>(follow(typeFunction.type)))
{
TypeId expansionTy =
arena->addType(PendingExpansionType{{}, function->name, typeFunctionTy->typeArguments, typeFunctionTy->packArguments});
addConstraint(scope, function->location, TypeAliasExpansionConstraint{/* target */ expansionTy});
}
}

return ControlFlow::None;
}

Expand Down Expand Up @@ -2785,15 +2751,12 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExpr* expr, Type
visitLValue(scope, e, rhsType);
else if (auto e = expr->as<AstExprError>())
{
if (FFlag::LuauNewSolverVisitErrorExprLvalues)
// If we end up with some sort of error expression in an lvalue
// position, at least go and check the expressions so that when
// we visit them later, there aren't any invalid assumptions.
for (auto subExpr : e->expressions)
{
// If we end up with some sort of error expression in an lvalue
// position, at least go and check the expressions so that when
// we visit them later, there aren't any invalid assumptions.
for (auto subExpr : e->expressions)
{
check(scope, subExpr);
}
check(scope, subExpr);
}
}
else
Expand Down Expand Up @@ -3255,8 +3218,7 @@ TypeId ConstraintGenerator::resolveReferenceType(
if (alias.has_value())
{
// If the alias is not generic, we don't need to set up a blocked type and an instantiation constraint
if (alias.has_value() && alias->typeParams.empty() && alias->typePackParams.empty() &&
(!FFlag::LuauUserTypeFunNoExtraConstraint || !ref->hasParameterList))
if (alias.has_value() && alias->typeParams.empty() && alias->typePackParams.empty() && !ref->hasParameterList)
{
result = alias->type;
}
Expand Down
Loading

0 comments on commit f8a1e01

Please sign in to comment.