mirror of
https://github.com/luau-lang/luau.git
synced 2025-03-03 18:51:42 +00:00
Sync to upstream/release/659
This commit is contained in:
parent
23241e2afc
commit
5f0bd2fcdd
45 changed files with 860 additions and 890 deletions
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
@ -116,14 +116,14 @@ private:
|
|||
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);
|
||||
|
@ -160,7 +160,7 @@ private:
|
|||
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);
|
||||
|
|
|
@ -40,7 +40,7 @@ struct InConditionalContext
|
|||
TypeContext* typeContext;
|
||||
TypeContext oldValue;
|
||||
|
||||
InConditionalContext(TypeContext* c)
|
||||
explicit InConditionalContext(TypeContext* c)
|
||||
: typeContext(c)
|
||||
, oldValue(*c)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
@ -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);
|
||||
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);
|
||||
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})};
|
||||
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)};
|
||||
}
|
||||
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)
|
||||
|
@ -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>());
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
if (auto it = scope->privateTypeBindings.find(function->name.value); it != scope->privateTypeBindings.end())
|
||||
mainTypeFun = getMutable<TypeFunctionInstanceType>(it->second.type);
|
||||
|
||||
if (!mainTypeFun)
|
||||
{
|
||||
// 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->exportedTypeBindings.find(function->name.value); it != scope->exportedTypeBindings.end())
|
||||
mainTypeFun = getMutable<TypeFunctionInstanceType>(it->second.type);
|
||||
}
|
||||
|
||||
if (!mainTypeFun)
|
||||
// Fill it with all visible type functions
|
||||
if (mainTypeFun)
|
||||
{
|
||||
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
||||
size_t level = 0;
|
||||
|
||||
for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
|
||||
{
|
||||
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 (FFlag::LuauUserTypeFunUpdateAllEnvs && mainTypeFun)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
level++;
|
||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||
userFuncData.environment[name] = std::make_pair(ty->userFuncData.definition, level);
|
||||
}
|
||||
}
|
||||
else if (mainTypeFun)
|
||||
{
|
||||
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
||||
|
||||
for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
|
||||
for (auto& [name, tf] : curr->exportedTypeBindings)
|
||||
{
|
||||
for (auto& [name, tf] : curr->privateTypeBindings)
|
||||
{
|
||||
if (userFuncData.environment_DEPRECATED.find(name))
|
||||
continue;
|
||||
if (userFuncData.environment.find(name))
|
||||
continue;
|
||||
|
||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||
userFuncData.environment_DEPRECATED[name] = ty->userFuncData.definition;
|
||||
}
|
||||
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_DEPRECATED.find(name))
|
||||
continue;
|
||||
level++;
|
||||
}
|
||||
}
|
||||
else if (mainTypeFun)
|
||||
{
|
||||
UserDefinedFunctionData& userFuncData = mainTypeFun->userFuncData;
|
||||
|
||||
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition)
|
||||
userFuncData.environment_DEPRECATED[name] = ty->userFuncData.definition;
|
||||
}
|
||||
for (Scope* curr = scope.get(); curr; curr = curr->parent.get())
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -35,9 +35,9 @@ LUAU_FASTFLAGVARIABLE(LuauRemoveNotAnyHack)
|
|||
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunNoExtraConstraint)
|
||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -823,6 +823,9 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
|
|||
TypeId tableTy =
|
||||
arena->addType(TableType{TableType::Props{}, TableIndexer{keyTy, valueTy}, TypeLevel{}, constraint->scope, TableState::Free});
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
||||
trackInteriorFreeType(constraint->scope, tableTy);
|
||||
|
||||
unify(constraint, nextTy, tableTy);
|
||||
|
||||
auto it = begin(c.variables);
|
||||
|
@ -959,16 +962,6 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
|||
if (auto typeFn = get<TypeFunctionInstanceType>(follow(tf->type)))
|
||||
pushConstraint(NotNull(constraint->scope.get()), constraint->location, ReduceConstraint{tf->type});
|
||||
|
||||
if (!FFlag::LuauUserTypeFunNoExtraConstraint)
|
||||
{
|
||||
// If there are no parameters to the type function we can just use the type directly
|
||||
if (tf->typeParams.empty() && tf->typePackParams.empty())
|
||||
{
|
||||
bindResult(tf->type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Due to how pending expansion types and TypeFun's are created
|
||||
// If this check passes, we have created a cyclic / corecursive type alias
|
||||
// of size 0
|
||||
|
@ -981,14 +974,11 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
|||
return true;
|
||||
}
|
||||
|
||||
if (FFlag::LuauUserTypeFunNoExtraConstraint)
|
||||
// If there are no parameters to the type function we can just use the type directly
|
||||
if (tf->typeParams.empty() && tf->typePackParams.empty())
|
||||
{
|
||||
// If there are no parameters to the type function we can just use the type directly
|
||||
if (tf->typeParams.empty() && tf->typePackParams.empty())
|
||||
{
|
||||
bindResult(tf->type);
|
||||
return true;
|
||||
}
|
||||
bindResult(tf->type);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto [typeArguments, packArguments] = saturateArguments(arena, builtinTypes, *tf, petv->typeArguments, petv->packArguments);
|
||||
|
@ -1854,6 +1844,10 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
|||
else
|
||||
{
|
||||
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
||||
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||
|
||||
TableType* upperTable = getMutable<TableType>(newUpperBound);
|
||||
LUAU_ASSERT(upperTable);
|
||||
|
||||
|
@ -2637,6 +2631,10 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
|||
NotNull<Scope> scope{ft->scope};
|
||||
|
||||
const TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, scope});
|
||||
|
||||
if (FFlag::LuauTrackInteriorFreeTypesOnScope && FFlag::LuauTrackInteriorFreeTablesOnScope)
|
||||
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||
|
||||
TableType* tt = getMutable<TableType>(newUpperBound);
|
||||
LUAU_ASSERT(tt);
|
||||
TypeId propType = freshType(arena, builtinTypes, scope);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
#include "Luau/BuiltinDefinitions.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitionsExtra)
|
||||
LUAU_FASTFLAG(LuauBufferBitMethods2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMathMapDefinition)
|
||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
||||
|
@ -488,58 +487,6 @@ declare buffer: {
|
|||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionVectorSrc_NoExtra_NoVector2Ctor_DEPRECATED = R"BUILTIN_SRC(
|
||||
|
||||
-- TODO: this will be replaced with a built-in primitive type
|
||||
declare class vector end
|
||||
|
||||
declare vector: {
|
||||
create: @checked (x: number, y: number, z: number) -> vector,
|
||||
magnitude: @checked (vec: vector) -> number,
|
||||
normalize: @checked (vec: vector) -> vector,
|
||||
cross: @checked (vec1: vector, vec2: vector) -> vector,
|
||||
dot: @checked (vec1: vector, vec2: vector) -> number,
|
||||
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
|
||||
floor: @checked (vec: vector) -> vector,
|
||||
ceil: @checked (vec: vector) -> vector,
|
||||
abs: @checked (vec: vector) -> vector,
|
||||
sign: @checked (vec: vector) -> vector,
|
||||
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
|
||||
max: @checked (vector, ...vector) -> vector,
|
||||
min: @checked (vector, ...vector) -> vector,
|
||||
|
||||
zero: vector,
|
||||
one: vector,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionVectorSrc_NoExtra_DEPRECATED = R"BUILTIN_SRC(
|
||||
|
||||
-- TODO: this will be replaced with a built-in primitive type
|
||||
declare class vector end
|
||||
|
||||
declare vector: {
|
||||
create: @checked (x: number, y: number, z: number?) -> vector,
|
||||
magnitude: @checked (vec: vector) -> number,
|
||||
normalize: @checked (vec: vector) -> vector,
|
||||
cross: @checked (vec1: vector, vec2: vector) -> vector,
|
||||
dot: @checked (vec1: vector, vec2: vector) -> number,
|
||||
angle: @checked (vec1: vector, vec2: vector, axis: vector?) -> number,
|
||||
floor: @checked (vec: vector) -> vector,
|
||||
ceil: @checked (vec: vector) -> vector,
|
||||
abs: @checked (vec: vector) -> vector,
|
||||
sign: @checked (vec: vector) -> vector,
|
||||
clamp: @checked (vec: vector, min: vector, max: vector) -> vector,
|
||||
max: @checked (vector, ...vector) -> vector,
|
||||
min: @checked (vector, ...vector) -> vector,
|
||||
|
||||
zero: vector,
|
||||
one: vector,
|
||||
}
|
||||
|
||||
)BUILTIN_SRC";
|
||||
|
||||
static const std::string kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED = R"BUILTIN_SRC(
|
||||
|
||||
-- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties
|
||||
|
@ -617,20 +564,10 @@ std::string getBuiltinDefinitionSource()
|
|||
|
||||
result += FFlag::LuauBufferBitMethods2 ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED;
|
||||
|
||||
if (FFlag::LuauVectorDefinitionsExtra)
|
||||
{
|
||||
if (FFlag::LuauVector2Constructor)
|
||||
result += kBuiltinDefinitionVectorSrc;
|
||||
else
|
||||
result += kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED;
|
||||
}
|
||||
if (FFlag::LuauVector2Constructor)
|
||||
result += kBuiltinDefinitionVectorSrc;
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauVector2Constructor)
|
||||
result += kBuiltinDefinitionVectorSrc_NoExtra_DEPRECATED;
|
||||
else
|
||||
result += kBuiltinDefinitionVectorSrc_NoExtra_NoVector2Ctor_DEPRECATED;
|
||||
}
|
||||
result += kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -92,18 +92,24 @@ size_t TTable::Hash::operator()(const TTable& value) const
|
|||
return hash;
|
||||
}
|
||||
|
||||
uint32_t StringCache::add(std::string_view s)
|
||||
StringId StringCache::add(std::string_view s)
|
||||
{
|
||||
size_t hash = std::hash<std::string_view>()(s);
|
||||
if (uint32_t* it = strings.find(hash))
|
||||
/* Important subtlety: This use of DenseHashMap<std::string_view, StringId>
|
||||
* is okay because std::hash<std::string_view> works solely on the bytes
|
||||
* referred by the string_view.
|
||||
*
|
||||
* In other words, two string views which contain the same bytes will have
|
||||
* the same hash whether or not their addresses are the same.
|
||||
*/
|
||||
if (StringId* it = strings.find(s))
|
||||
return *it;
|
||||
|
||||
char* storage = static_cast<char*>(allocator.allocate(s.size()));
|
||||
memcpy(storage, s.data(), s.size());
|
||||
|
||||
uint32_t result = uint32_t(views.size());
|
||||
StringId result = StringId(views.size());
|
||||
views.emplace_back(storage, s.size());
|
||||
strings[hash] = result;
|
||||
strings[s] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -390,6 +396,16 @@ Id toId(
|
|||
{
|
||||
LUAU_ASSERT(tfun->packArguments.empty());
|
||||
|
||||
if (tfun->userFuncName) {
|
||||
// TODO: User defined type functions are pseudo-effectful: error
|
||||
// reporting is done via the `print` statement, so running a
|
||||
// UDTF multiple times may end up double erroring. egraphs
|
||||
// currently may induce type functions to be reduced multiple
|
||||
// times. We should probably opt _not_ to process user defined
|
||||
// type functions at all.
|
||||
return egraph.add(TOpaque{ty});
|
||||
}
|
||||
|
||||
std::vector<Id> parts;
|
||||
parts.reserve(tfun->typeArguments.size());
|
||||
for (TypeId part : tfun->typeArguments)
|
||||
|
|
|
@ -39,7 +39,6 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
|||
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreCommentsForDefinitionFiles)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
|
||||
|
@ -52,6 +51,7 @@ LUAU_FASTFLAG(StudioReportLuauAny2)
|
|||
LUAU_FASTFLAGVARIABLE(LuauStoreSolverTypeOnModule)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauReferenceAllocatorInNewSolver)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelectivelyRetainDFGArena)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -138,7 +138,7 @@ static ParseResult parseSourceForModule(std::string_view source, Luau::SourceMod
|
|||
sourceModule.root = parseResult.root;
|
||||
sourceModule.mode = Mode::Definition;
|
||||
|
||||
if (FFlag::LuauStoreCommentsForDefinitionFiles && options.captureComments)
|
||||
if (options.captureComments)
|
||||
{
|
||||
sourceModule.hotcomments = parseResult.hotcomments;
|
||||
sourceModule.commentLocations = parseResult.commentLocations;
|
||||
|
@ -1049,6 +1049,11 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
|
|||
freeze(module->interfaceTypes);
|
||||
|
||||
module->internalTypes.clear();
|
||||
if (FFlag::LuauSelectivelyRetainDFGArena)
|
||||
{
|
||||
module->defArena.allocator.clear();
|
||||
module->keyArena.allocator.clear();
|
||||
}
|
||||
|
||||
module->astTypes.clear();
|
||||
module->astTypePacks.clear();
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant)
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000);
|
||||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
|
||||
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNormalizationTracksCyclicPairsThroughInhabitance);
|
||||
LUAU_FASTFLAGVARIABLE(LuauIntersectNormalsNeedsToTrackResourceLimits);
|
||||
LUAU_FASTFLAGVARIABLE(LuauNormalizationTracksCyclicPairsThroughInhabitance)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -3043,12 +3042,9 @@ NormalizationResult Normalizer::intersectTyvarsWithTy(
|
|||
// See above for an explaination of `ignoreSmallerTyvars`.
|
||||
NormalizationResult Normalizer::intersectNormals(NormalizedType& here, const NormalizedType& there, int ignoreSmallerTyvars)
|
||||
{
|
||||
if (FFlag::LuauIntersectNormalsNeedsToTrackResourceLimits)
|
||||
{
|
||||
RecursionCounter _rc(&sharedState->counters.recursionCount);
|
||||
if (!withinResourceLimits())
|
||||
return NormalizationResult::HitLimits;
|
||||
}
|
||||
RecursionCounter _rc(&sharedState->counters.recursionCount);
|
||||
if (!withinResourceLimits())
|
||||
return NormalizationResult::HitLimits;
|
||||
|
||||
if (!get<NeverType>(there.tops))
|
||||
{
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/Unifier2.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauDontInPlaceMutateTableType)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -236,6 +238,8 @@ TypeId matchLiteralType(
|
|||
return exprType;
|
||||
}
|
||||
|
||||
DenseHashSet<AstExprConstantString*> keysToDelete{nullptr};
|
||||
|
||||
for (const AstExprTable::Item& item : exprTable->items)
|
||||
{
|
||||
if (isRecord(item))
|
||||
|
@ -280,7 +284,10 @@ TypeId matchLiteralType(
|
|||
else
|
||||
tableTy->indexer = TableIndexer{expectedTableTy->indexer->indexType, matchedType};
|
||||
|
||||
tableTy->props.erase(keyStr);
|
||||
if (FFlag::LuauDontInPlaceMutateTableType)
|
||||
keysToDelete.insert(item.key->as<AstExprConstantString>());
|
||||
else
|
||||
tableTy->props.erase(keyStr);
|
||||
}
|
||||
|
||||
// If it's just an extra property and the expected type
|
||||
|
@ -387,6 +394,16 @@ TypeId matchLiteralType(
|
|||
LUAU_ASSERT(!"Unexpected");
|
||||
}
|
||||
|
||||
if (FFlag::LuauDontInPlaceMutateTableType)
|
||||
{
|
||||
for (const auto& key: keysToDelete)
|
||||
{
|
||||
const AstArray<char>& s = key->value;
|
||||
std::string keyStr{s.data, s.data + s.size};
|
||||
tableTy->props.erase(keyStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Keys that the expectedType says we should have, but that aren't
|
||||
// specified by the AST fragment.
|
||||
//
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "Luau/DcrLogger.h"
|
||||
#include "Luau/DenseHash.h"
|
||||
#include "Luau/Error.h"
|
||||
#include "Luau/InsertionOrderedMap.h"
|
||||
#include "Luau/Instantiation.h"
|
||||
#include "Luau/Metamethods.h"
|
||||
#include "Luau/Normalize.h"
|
||||
|
@ -27,8 +26,6 @@
|
|||
#include "Luau/VisitType.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
|
@ -176,7 +173,7 @@ struct InternalTypeFunctionFinder : TypeOnceVisitor
|
|||
DenseHashSet<TypeId> mentionedFunctions{nullptr};
|
||||
DenseHashSet<TypePackId> mentionedFunctionPacks{nullptr};
|
||||
|
||||
InternalTypeFunctionFinder(std::vector<TypeId>& declStack)
|
||||
explicit InternalTypeFunctionFinder(std::vector<TypeId>& declStack)
|
||||
{
|
||||
TypeFunctionFinder f;
|
||||
for (TypeId fn : declStack)
|
||||
|
@ -507,7 +504,7 @@ TypeId TypeChecker2::checkForTypeFunctionInhabitance(TypeId instance, Location l
|
|||
return instance;
|
||||
}
|
||||
|
||||
TypePackId TypeChecker2::lookupPack(AstExpr* expr)
|
||||
TypePackId TypeChecker2::lookupPack(AstExpr* expr) const
|
||||
{
|
||||
// If a type isn't in the type graph, it probably means that a recursion limit was exceeded.
|
||||
// We'll just return anyType in these cases. Typechecking against any is very fast and this
|
||||
|
@ -557,7 +554,7 @@ TypeId TypeChecker2::lookupAnnotation(AstType* annotation)
|
|||
return checkForTypeFunctionInhabitance(follow(*ty), annotation->location);
|
||||
}
|
||||
|
||||
std::optional<TypePackId> TypeChecker2::lookupPackAnnotation(AstTypePack* annotation)
|
||||
std::optional<TypePackId> TypeChecker2::lookupPackAnnotation(AstTypePack* annotation) const
|
||||
{
|
||||
TypePackId* tp = module->astResolvedTypePacks.find(annotation);
|
||||
if (tp != nullptr)
|
||||
|
@ -565,7 +562,7 @@ std::optional<TypePackId> TypeChecker2::lookupPackAnnotation(AstTypePack* annota
|
|||
return {};
|
||||
}
|
||||
|
||||
TypeId TypeChecker2::lookupExpectedType(AstExpr* expr)
|
||||
TypeId TypeChecker2::lookupExpectedType(AstExpr* expr) const
|
||||
{
|
||||
if (TypeId* ty = module->astExpectedTypes.find(expr))
|
||||
return follow(*ty);
|
||||
|
@ -573,7 +570,7 @@ TypeId TypeChecker2::lookupExpectedType(AstExpr* expr)
|
|||
return builtinTypes->anyType;
|
||||
}
|
||||
|
||||
TypePackId TypeChecker2::lookupExpectedPack(AstExpr* expr, TypeArena& arena)
|
||||
TypePackId TypeChecker2::lookupExpectedPack(AstExpr* expr, TypeArena& arena) const
|
||||
{
|
||||
if (TypeId* ty = module->astExpectedTypes.find(expr))
|
||||
return arena.addTypePack(TypePack{{follow(*ty)}, std::nullopt});
|
||||
|
@ -597,7 +594,7 @@ TypePackId TypeChecker2::reconstructPack(AstArray<AstExpr*> exprs, TypeArena& ar
|
|||
return arena.addTypePack(TypePack{head, tail});
|
||||
}
|
||||
|
||||
Scope* TypeChecker2::findInnermostScope(Location location)
|
||||
Scope* TypeChecker2::findInnermostScope(Location location) const
|
||||
{
|
||||
Scope* bestScope = module->getModuleScope().get();
|
||||
|
||||
|
@ -1564,7 +1561,7 @@ void TypeChecker2::visit(AstExprCall* call)
|
|||
visitCall(call);
|
||||
}
|
||||
|
||||
std::optional<TypeId> TypeChecker2::tryStripUnionFromNil(TypeId ty)
|
||||
std::optional<TypeId> TypeChecker2::tryStripUnionFromNil(TypeId ty) const
|
||||
{
|
||||
if (const UnionType* utv = get<UnionType>(ty))
|
||||
{
|
||||
|
|
|
@ -47,10 +47,7 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
|
|||
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunPrintToError)
|
||||
LUAU_FASTFLAG(LuauRemoveNotAnyHack)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunExportedAndLocal)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunUpdateAllEnvs)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -221,11 +218,8 @@ struct TypeFunctionReducer
|
|||
template<typename T>
|
||||
void handleTypeFunctionReduction(T subject, TypeFunctionReductionResult<T> reduction)
|
||||
{
|
||||
if (FFlag::LuauUserTypeFunPrintToError)
|
||||
{
|
||||
for (auto& message : reduction.messages)
|
||||
result.messages.emplace_back(location, UserDefinedTypeFunctionError{std::move(message)});
|
||||
}
|
||||
for (auto& message : reduction.messages)
|
||||
result.messages.emplace_back(location, UserDefinedTypeFunctionError{std::move(message)});
|
||||
|
||||
if (reduction.result)
|
||||
replace(subject, *reduction.result);
|
||||
|
@ -617,27 +611,16 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
{
|
||||
auto typeFunction = getMutable<TypeFunctionInstanceType>(instance);
|
||||
|
||||
if (FFlag::LuauUserTypeFunExportedAndLocal)
|
||||
if (typeFunction->userFuncData.owner.expired())
|
||||
{
|
||||
if (typeFunction->userFuncData.owner.expired())
|
||||
{
|
||||
ctx->ice->ice("user-defined type function module has expired");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
|
||||
if (!typeFunction->userFuncName || !typeFunction->userFuncData.definition)
|
||||
{
|
||||
ctx->ice->ice("all user-defined type functions must have an associated function definition");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
ctx->ice->ice("user-defined type function module has expired");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
else
|
||||
|
||||
if (!typeFunction->userFuncName || !typeFunction->userFuncData.definition)
|
||||
{
|
||||
if (!ctx->userFuncName)
|
||||
{
|
||||
ctx->ice->ice("all user-defined type functions must have an associated function definition");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
ctx->ice->ice("all user-defined type functions must have an associated function definition");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
|
||||
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
|
||||
|
@ -653,36 +636,19 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
return {std::nullopt, Reduction::MaybeOk, {ty}, {}};
|
||||
}
|
||||
|
||||
if (FFlag::LuauUserTypeFunExportedAndLocal && FFlag::LuauUserTypeFunUpdateAllEnvs)
|
||||
// Ensure that whole type function environment is registered
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
||||
{
|
||||
// Ensure that whole type function environment is registered
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
||||
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first))
|
||||
{
|
||||
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first))
|
||||
{
|
||||
// Failure to register at this point means that original definition had to error out and should not have been present in the
|
||||
// environment
|
||||
ctx->ice->ice("user-defined type function reference cannot be registered");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauUserTypeFunExportedAndLocal)
|
||||
{
|
||||
// Ensure that whole type function environment is registered
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environment_DEPRECATED)
|
||||
{
|
||||
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition))
|
||||
{
|
||||
// Failure to register at this point means that original definition had to error out and should not have been present in the
|
||||
// environment
|
||||
ctx->ice->ice("user-defined type function reference cannot be registered");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
// Failure to register at this point means that original definition had to error out and should not have been present in the
|
||||
// environment
|
||||
ctx->ice->ice("user-defined type function reference cannot be registered");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
}
|
||||
|
||||
AstName name = FFlag::LuauUserTypeFunExportedAndLocal ? typeFunction->userFuncData.definition->name : *ctx->userFuncName;
|
||||
AstName name = typeFunction->userFuncData.definition->name;
|
||||
|
||||
lua_State* global = ctx->typeFunctionRuntime->state.get();
|
||||
|
||||
|
@ -693,62 +659,15 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
lua_State* L = lua_newthread(global);
|
||||
LuauTempThreadPopper popper(global);
|
||||
|
||||
if (FFlag::LuauUserTypeFunExportedAndLocal && FFlag::LuauUserTypeFunUpdateAllEnvs)
|
||||
// Build up the environment table of each function we have visible
|
||||
for (auto& [_, curr] : typeFunction->userFuncData.environment)
|
||||
{
|
||||
// Build up the environment table of each function we have visible
|
||||
for (auto& [_, curr] : typeFunction->userFuncData.environment)
|
||||
{
|
||||
// Environment table has to be filled only once in the current execution context
|
||||
if (ctx->typeFunctionRuntime->initialized.find(curr.first))
|
||||
continue;
|
||||
ctx->typeFunctionRuntime->initialized.insert(curr.first);
|
||||
// Environment table has to be filled only once in the current execution context
|
||||
if (ctx->typeFunctionRuntime->initialized.find(curr.first))
|
||||
continue;
|
||||
ctx->typeFunctionRuntime->initialized.insert(curr.first);
|
||||
|
||||
lua_pushlightuserdata(L, curr.first);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_isfunction(L, -1))
|
||||
{
|
||||
ctx->ice->ice("user-defined type function reference cannot be found in the registry");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
|
||||
// Build up the environment of the current function, where some might not be visible
|
||||
lua_getfenv(L, -1);
|
||||
lua_setreadonly(L, -1, false);
|
||||
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
||||
{
|
||||
// Filter visibility based on original scope depth
|
||||
if (definition.second >= curr.second)
|
||||
{
|
||||
lua_pushlightuserdata(L, definition.first);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_isfunction(L, -1))
|
||||
break; // Don't have to report an error here, we will visit each function in outer loop
|
||||
|
||||
lua_setfield(L, -2, name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
// Fetch the function we want to evaluate
|
||||
lua_pushlightuserdata(L, typeFunction->userFuncData.definition);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_isfunction(L, -1))
|
||||
{
|
||||
ctx->ice->ice("user-defined type function reference cannot be found in the registry");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauUserTypeFunExportedAndLocal)
|
||||
{
|
||||
// Fetch the function we want to evaluate
|
||||
lua_pushlightuserdata(L, typeFunction->userFuncData.definition);
|
||||
lua_pushlightuserdata(L, curr.first);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_isfunction(L, -1))
|
||||
|
@ -757,31 +676,37 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
|
||||
// Build up the environment
|
||||
// Build up the environment of the current function, where some might not be visible
|
||||
lua_getfenv(L, -1);
|
||||
lua_setreadonly(L, -1, false);
|
||||
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environment_DEPRECATED)
|
||||
for (auto& [name, definition] : typeFunction->userFuncData.environment)
|
||||
{
|
||||
lua_pushlightuserdata(L, definition);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_isfunction(L, -1))
|
||||
// Filter visibility based on original scope depth
|
||||
if (definition.second >= curr.second)
|
||||
{
|
||||
ctx->ice->ice("user-defined type function reference cannot be found in the registry");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
lua_pushlightuserdata(L, definition.first);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
lua_setfield(L, -2, name.c_str());
|
||||
if (!lua_isfunction(L, -1))
|
||||
break; // Don't have to report an error here, we will visit each function in outer loop
|
||||
|
||||
lua_setfield(L, -2, name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
lua_setreadonly(L, -1, true);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
else
|
||||
|
||||
// Fetch the function we want to evaluate
|
||||
lua_pushlightuserdata(L, typeFunction->userFuncData.definition);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_isfunction(L, -1))
|
||||
{
|
||||
lua_getglobal(global, name.value);
|
||||
lua_xmove(global, L, 1);
|
||||
ctx->ice->ice("user-defined type function reference cannot be found in the registry");
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}};
|
||||
}
|
||||
|
||||
resetTypeFunctionState(L);
|
||||
|
@ -816,31 +741,22 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
throw UserCancelError(ctx->ice->moduleName);
|
||||
};
|
||||
|
||||
if (FFlag::LuauUserTypeFunPrintToError)
|
||||
ctx->typeFunctionRuntime->messages.clear();
|
||||
ctx->typeFunctionRuntime->messages.clear();
|
||||
|
||||
if (auto error = checkResultForError(L, name.value, lua_pcall(L, int(typeParams.size()), 1, 0)))
|
||||
{
|
||||
if (FFlag::LuauUserTypeFunPrintToError)
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}, error, ctx->typeFunctionRuntime->messages};
|
||||
else
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}, error};
|
||||
}
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}, error, ctx->typeFunctionRuntime->messages};
|
||||
|
||||
// If the return value is not a type userdata, return with error message
|
||||
if (!isTypeUserData(L, 1))
|
||||
{
|
||||
if (FFlag::LuauUserTypeFunPrintToError)
|
||||
return {
|
||||
std::nullopt,
|
||||
Reduction::Erroneous,
|
||||
{},
|
||||
{},
|
||||
format("'%s' type function: returned a non-type value", name.value),
|
||||
ctx->typeFunctionRuntime->messages
|
||||
};
|
||||
else
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}, format("'%s' type function: returned a non-type value", name.value)};
|
||||
return {
|
||||
std::nullopt,
|
||||
Reduction::Erroneous,
|
||||
{},
|
||||
{},
|
||||
format("'%s' type function: returned a non-type value", name.value),
|
||||
ctx->typeFunctionRuntime->messages
|
||||
};
|
||||
}
|
||||
|
||||
TypeFunctionTypeId retTypeFunctionTypeId = getTypeUserData(L, 1);
|
||||
|
@ -852,17 +768,9 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
|
|||
|
||||
// At least 1 error occurred while deserializing
|
||||
if (runtimeBuilder->errors.size() > 0)
|
||||
{
|
||||
if (FFlag::LuauUserTypeFunPrintToError)
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}, runtimeBuilder->errors.front(), ctx->typeFunctionRuntime->messages};
|
||||
else
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}, runtimeBuilder->errors.front()};
|
||||
}
|
||||
return {std::nullopt, Reduction::Erroneous, {}, {}, runtimeBuilder->errors.front(), ctx->typeFunctionRuntime->messages};
|
||||
|
||||
if (FFlag::LuauUserTypeFunPrintToError)
|
||||
return {retTypeId, Reduction::MaybeOk, {}, {}, std::nullopt, ctx->typeFunctionRuntime->messages};
|
||||
else
|
||||
return {retTypeId, Reduction::MaybeOk, {}, {}};
|
||||
return {retTypeId, Reduction::MaybeOk, {}, {}, std::nullopt, ctx->typeFunctionRuntime->messages};
|
||||
}
|
||||
|
||||
TypeFunctionReductionResult<TypeId> notTypeFunction(
|
||||
|
@ -1098,21 +1006,18 @@ std::optional<std::string> TypeFunctionRuntime::registerFunction(AstStatTypeFunc
|
|||
|
||||
lua_State* global = state.get();
|
||||
|
||||
if (FFlag::LuauUserTypeFunExportedAndLocal)
|
||||
// Fetch to check if function is already registered
|
||||
lua_pushlightuserdata(global, function);
|
||||
lua_gettable(global, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_isnil(global, -1))
|
||||
{
|
||||
// Fetch to check if function is already registered
|
||||
lua_pushlightuserdata(global, function);
|
||||
lua_gettable(global, LUA_REGISTRYINDEX);
|
||||
|
||||
if (!lua_isnil(global, -1))
|
||||
{
|
||||
lua_pop(global, 1);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
lua_pop(global, 1);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
lua_pop(global, 1);
|
||||
|
||||
AstName name = function->name;
|
||||
|
||||
// Construct ParseResult containing the type function
|
||||
|
@ -1166,19 +1071,10 @@ std::optional<std::string> TypeFunctionRuntime::registerFunction(AstStatTypeFunc
|
|||
return format("Could not find '%s' type function in the global scope", name.value);
|
||||
}
|
||||
|
||||
if (FFlag::LuauUserTypeFunExportedAndLocal)
|
||||
{
|
||||
// Store resulting function in the registry
|
||||
lua_pushlightuserdata(global, function);
|
||||
lua_xmove(L, global, 1);
|
||||
lua_settable(global, LUA_REGISTRYINDEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Store resulting function in the global environment
|
||||
lua_xmove(L, global, 1);
|
||||
lua_setglobal(global, name.value);
|
||||
}
|
||||
// Store resulting function in the registry
|
||||
lua_pushlightuserdata(global, function);
|
||||
lua_xmove(L, global, 1);
|
||||
lua_settable(global, LUA_REGISTRYINDEX);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
|
||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixInner)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunPrintToError)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixNoReadWrite)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunThreadBuffer)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunGenerics)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunCloneTail)
|
||||
|
||||
|
@ -137,11 +135,9 @@ static std::string getTag(lua_State* L, TypeFunctionTypeId ty)
|
|||
return "number";
|
||||
else if (auto s = get<TypeFunctionPrimitiveType>(ty); s && s->type == TypeFunctionPrimitiveType::Type::String)
|
||||
return "string";
|
||||
else if (auto s = get<TypeFunctionPrimitiveType>(ty);
|
||||
FFlag::LuauUserTypeFunThreadBuffer && s && s->type == TypeFunctionPrimitiveType::Type::Thread)
|
||||
else if (auto s = get<TypeFunctionPrimitiveType>(ty); s && s->type == TypeFunctionPrimitiveType::Type::Thread)
|
||||
return "thread";
|
||||
else if (auto s = get<TypeFunctionPrimitiveType>(ty);
|
||||
FFlag::LuauUserTypeFunThreadBuffer && s && s->type == TypeFunctionPrimitiveType::Type::Buffer)
|
||||
else if (auto s = get<TypeFunctionPrimitiveType>(ty); s && s->type == TypeFunctionPrimitiveType::Type::Buffer)
|
||||
return "buffer";
|
||||
else if (get<TypeFunctionUnknownType>(ty))
|
||||
return "unknown";
|
||||
|
@ -1759,8 +1755,8 @@ void registerTypesLibrary(lua_State* L)
|
|||
{"boolean", createBoolean},
|
||||
{"number", createNumber},
|
||||
{"string", createString},
|
||||
{FFlag::LuauUserTypeFunThreadBuffer ? "thread" : nullptr, FFlag::LuauUserTypeFunThreadBuffer ? createThread : nullptr},
|
||||
{FFlag::LuauUserTypeFunThreadBuffer ? "buffer" : nullptr, FFlag::LuauUserTypeFunThreadBuffer ? createBuffer : nullptr},
|
||||
{"thread", createThread},
|
||||
{"buffer", createBuffer},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
|
@ -1941,29 +1937,16 @@ void setTypeFunctionEnvironment(lua_State* L)
|
|||
luaopen_base(L);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (FFlag::LuauUserTypeFunPrintToError)
|
||||
// Remove certain global functions from the base library
|
||||
static const char* unavailableGlobals[] = {"gcinfo", "getfenv", "newproxy", "setfenv", "pcall", "xpcall"};
|
||||
for (auto& name : unavailableGlobals)
|
||||
{
|
||||
// Remove certain global functions from the base library
|
||||
static const char* unavailableGlobals[] = {"gcinfo", "getfenv", "newproxy", "setfenv", "pcall", "xpcall"};
|
||||
for (auto& name : unavailableGlobals)
|
||||
{
|
||||
lua_pushcfunction(L, unsupportedFunction, name);
|
||||
lua_setglobal(L, name);
|
||||
}
|
||||
lua_pushcfunction(L, unsupportedFunction, name);
|
||||
lua_setglobal(L, name);
|
||||
}
|
||||
|
||||
lua_pushcfunction(L, print, "print");
|
||||
lua_setglobal(L, "print");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove certain global functions from the base library
|
||||
static const std::string unavailableGlobals[] = {"gcinfo", "getfenv", "newproxy", "setfenv", "pcall", "xpcall"};
|
||||
for (auto& name : unavailableGlobals)
|
||||
{
|
||||
lua_pushcfunction(L, unsupportedFunction, "Removing global function from type function environment");
|
||||
lua_setglobal(L, name.c_str());
|
||||
}
|
||||
}
|
||||
lua_pushcfunction(L, print, "print");
|
||||
lua_setglobal(L, "print");
|
||||
}
|
||||
|
||||
void resetTypeFunctionState(lua_State* L)
|
||||
|
@ -2495,12 +2478,10 @@ private:
|
|||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::String));
|
||||
break;
|
||||
case TypeFunctionPrimitiveType::Thread:
|
||||
if (FFlag::LuauUserTypeFunThreadBuffer)
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Thread));
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Thread));
|
||||
break;
|
||||
case TypeFunctionPrimitiveType::Buffer:
|
||||
if (FFlag::LuauUserTypeFunThreadBuffer)
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Buffer));
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Buffer));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
// currently, controls serialization, deserialization, and `type.copy`
|
||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
|
||||
|
||||
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunGenerics)
|
||||
|
||||
namespace Luau
|
||||
|
@ -161,26 +160,10 @@ private:
|
|||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::String));
|
||||
break;
|
||||
case PrimitiveType::Thread:
|
||||
if (FFlag::LuauUserTypeFunThreadBuffer)
|
||||
{
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Thread));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string error = format("Argument of primitive type %s is not currently serializable by type functions", toString(ty).c_str());
|
||||
state->errors.push_back(error);
|
||||
}
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Thread));
|
||||
break;
|
||||
case PrimitiveType::Buffer:
|
||||
if (FFlag::LuauUserTypeFunThreadBuffer)
|
||||
{
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string error = format("Argument of primitive type %s is not currently serializable by type functions", toString(ty).c_str());
|
||||
state->errors.push_back(error);
|
||||
}
|
||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::Buffer));
|
||||
break;
|
||||
case PrimitiveType::Function:
|
||||
case PrimitiveType::Table:
|
||||
|
@ -670,16 +653,10 @@ private:
|
|||
target = state->ctx->builtins->stringType;
|
||||
break;
|
||||
case TypeFunctionPrimitiveType::Type::Thread:
|
||||
if (FFlag::LuauUserTypeFunThreadBuffer)
|
||||
target = state->ctx->builtins->threadType;
|
||||
else
|
||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
||||
target = state->ctx->builtins->threadType;
|
||||
break;
|
||||
case TypeFunctionPrimitiveType::Type::Buffer:
|
||||
if (FFlag::LuauUserTypeFunThreadBuffer)
|
||||
target = state->ctx->builtins->bufferType;
|
||||
else
|
||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
||||
target = state->ctx->builtins->bufferType;
|
||||
break;
|
||||
default:
|
||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
std::optional<std::string> getCurrentWorkingDirectory();
|
||||
|
||||
std::string normalizePath(std::string_view path);
|
||||
std::string resolvePath(std::string_view relativePath, std::string_view baseFilePath);
|
||||
std::optional<std::string> resolvePath(std::string_view relativePath, std::string_view baseFilePath);
|
||||
|
||||
std::optional<std::string> readFile(const std::string& name);
|
||||
std::optional<std::string> readStdin();
|
||||
|
@ -23,7 +23,7 @@ bool isDirectory(const std::string& path);
|
|||
bool traverseDirectory(const std::string& path, const std::function<void(const std::string& name)>& callback);
|
||||
|
||||
std::vector<std::string_view> splitPath(std::string_view path);
|
||||
std::string joinPaths(const std::string& lhs, const std::string& rhs);
|
||||
std::optional<std::string> getParentPath(const std::string& path);
|
||||
std::string joinPaths(std::string_view lhs, std::string_view rhs);
|
||||
std::optional<std::string> getParentPath(std::string_view path);
|
||||
|
||||
std::vector<std::string> getSourceFiles(int argc, char** argv);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::wstring fromUtf8(const std::string& path)
|
||||
|
@ -90,108 +91,76 @@ std::optional<std::string> getCurrentWorkingDirectory()
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Returns the normal/canonical form of a path (e.g. "../subfolder/../module.luau" -> "../module.luau")
|
||||
std::string normalizePath(std::string_view path)
|
||||
{
|
||||
return resolvePath(path, "");
|
||||
}
|
||||
const std::vector<std::string_view> components = splitPath(path);
|
||||
std::vector<std::string_view> normalizedComponents;
|
||||
|
||||
// Takes a path that is relative to the file at baseFilePath and returns the path explicitly rebased onto baseFilePath.
|
||||
// For absolute paths, baseFilePath will be ignored, and this function will resolve the path to a canonical path:
|
||||
// (e.g. "/Users/.././Users/johndoe" -> "/Users/johndoe").
|
||||
std::string resolvePath(std::string_view path, std::string_view baseFilePath)
|
||||
{
|
||||
std::vector<std::string_view> pathComponents;
|
||||
std::vector<std::string_view> baseFilePathComponents;
|
||||
const bool isAbsolute = isAbsolutePath(path);
|
||||
|
||||
// Dependent on whether the final resolved path is absolute or relative
|
||||
// - if relative (when path and baseFilePath are both relative), resolvedPathPrefix remains empty
|
||||
// - if absolute (if either path or baseFilePath are absolute), resolvedPathPrefix is "C:\", "/", etc.
|
||||
std::string resolvedPathPrefix;
|
||||
bool isResolvedPathRelative = false;
|
||||
|
||||
if (isAbsolutePath(path))
|
||||
{
|
||||
// path is absolute, we use path's prefix and ignore baseFilePath
|
||||
size_t afterPrefix = path.find_first_of("\\/") + 1;
|
||||
resolvedPathPrefix = path.substr(0, afterPrefix);
|
||||
pathComponents = splitPath(path.substr(afterPrefix));
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t afterPrefix = baseFilePath.find_first_of("\\/") + 1;
|
||||
baseFilePathComponents = splitPath(baseFilePath.substr(afterPrefix));
|
||||
if (isAbsolutePath(baseFilePath))
|
||||
{
|
||||
// path is relative and baseFilePath is absolute, we use baseFilePath's prefix
|
||||
resolvedPathPrefix = baseFilePath.substr(0, afterPrefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
// path and baseFilePath are both relative, we do not set a prefix (resolved path will be relative)
|
||||
isResolvedPathRelative = true;
|
||||
}
|
||||
pathComponents = splitPath(path);
|
||||
}
|
||||
|
||||
// Remove filename from components
|
||||
if (!baseFilePathComponents.empty())
|
||||
baseFilePathComponents.pop_back();
|
||||
|
||||
// Resolve the path by applying pathComponents to baseFilePathComponents
|
||||
int numPrependedParents = 0;
|
||||
for (std::string_view component : pathComponents)
|
||||
// 1. Normalize path components
|
||||
const size_t startIndex = isAbsolute ? 1 : 0;
|
||||
for (size_t i = startIndex; i < components.size(); i++)
|
||||
{
|
||||
std::string_view component = components[i];
|
||||
if (component == "..")
|
||||
{
|
||||
if (baseFilePathComponents.empty())
|
||||
if (normalizedComponents.empty())
|
||||
{
|
||||
if (isResolvedPathRelative)
|
||||
numPrependedParents++; // "../" will later be added to the beginning of the resolved path
|
||||
if (!isAbsolute)
|
||||
{
|
||||
normalizedComponents.emplace_back("..");
|
||||
}
|
||||
}
|
||||
else if (baseFilePathComponents.back() != "..")
|
||||
else if (normalizedComponents.back() == "..")
|
||||
{
|
||||
baseFilePathComponents.pop_back(); // Resolve cases like "folder/subfolder/../../file" to "file"
|
||||
normalizedComponents.emplace_back("..");
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedComponents.pop_back();
|
||||
}
|
||||
}
|
||||
else if (component != "." && !component.empty())
|
||||
else if (!component.empty() && component != ".")
|
||||
{
|
||||
baseFilePathComponents.push_back(component);
|
||||
normalizedComponents.emplace_back(component);
|
||||
}
|
||||
}
|
||||
|
||||
// Create resolved path prefix for relative paths
|
||||
if (isResolvedPathRelative)
|
||||
std::string normalizedPath;
|
||||
|
||||
// 2. Add correct prefix to formatted path
|
||||
if (isAbsolute)
|
||||
{
|
||||
if (numPrependedParents > 0)
|
||||
{
|
||||
resolvedPathPrefix.reserve(numPrependedParents * 3);
|
||||
for (int i = 0; i < numPrependedParents; i++)
|
||||
{
|
||||
resolvedPathPrefix += "../";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resolvedPathPrefix = "./";
|
||||
}
|
||||
normalizedPath += components[0];
|
||||
normalizedPath += "/";
|
||||
}
|
||||
else if (normalizedComponents.empty() || normalizedComponents[0] != "..")
|
||||
{
|
||||
normalizedPath += "./";
|
||||
}
|
||||
|
||||
// Join baseFilePathComponents to form the resolved path
|
||||
std::string resolvedPath = resolvedPathPrefix;
|
||||
for (auto iter = baseFilePathComponents.begin(); iter != baseFilePathComponents.end(); ++iter)
|
||||
// 3. Join path components to form the normalized path
|
||||
for (auto iter = normalizedComponents.begin(); iter != normalizedComponents.end(); ++iter)
|
||||
{
|
||||
if (iter != baseFilePathComponents.begin())
|
||||
resolvedPath += "/";
|
||||
if (iter != normalizedComponents.begin())
|
||||
normalizedPath += "/";
|
||||
|
||||
resolvedPath += *iter;
|
||||
normalizedPath += *iter;
|
||||
}
|
||||
if (resolvedPath.size() > resolvedPathPrefix.size() && resolvedPath.back() == '/')
|
||||
{
|
||||
// Remove trailing '/' if present
|
||||
resolvedPath.pop_back();
|
||||
}
|
||||
return resolvedPath;
|
||||
if (normalizedPath.size() >= 2 && normalizedPath[normalizedPath.size() - 1] == '.' && normalizedPath[normalizedPath.size() - 2] == '.')
|
||||
normalizedPath += "/";
|
||||
|
||||
return normalizedPath;
|
||||
}
|
||||
|
||||
std::optional<std::string> resolvePath(std::string_view path, std::string_view baseFilePath)
|
||||
{
|
||||
std::optional<std::string> baseFilePathParent = getParentPath(baseFilePath);
|
||||
if (!baseFilePathParent)
|
||||
return std::nullopt;
|
||||
|
||||
return normalizePath(joinPaths(*baseFilePathParent, path));
|
||||
}
|
||||
|
||||
bool hasFileExtension(std::string_view name, const std::vector<std::string>& extensions)
|
||||
|
@ -416,16 +385,16 @@ std::vector<std::string_view> splitPath(std::string_view path)
|
|||
return components;
|
||||
}
|
||||
|
||||
std::string joinPaths(const std::string& lhs, const std::string& rhs)
|
||||
std::string joinPaths(std::string_view lhs, std::string_view rhs)
|
||||
{
|
||||
std::string result = lhs;
|
||||
std::string result = std::string(lhs);
|
||||
if (!result.empty() && result.back() != '/' && result.back() != '\\')
|
||||
result += '/';
|
||||
result += rhs;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::string> getParentPath(const std::string& path)
|
||||
std::optional<std::string> getParentPath(std::string_view path)
|
||||
{
|
||||
if (path == "" || path == "." || path == "/")
|
||||
return std::nullopt;
|
||||
|
@ -441,7 +410,7 @@ std::optional<std::string> getParentPath(const std::string& path)
|
|||
return "/";
|
||||
|
||||
if (slash != std::string::npos)
|
||||
return path.substr(0, slash);
|
||||
return std::string(path.substr(0, slash));
|
||||
|
||||
return "";
|
||||
}
|
||||
|
@ -471,10 +440,12 @@ std::vector<std::string> getSourceFiles(int argc, char** argv)
|
|||
if (argv[i][0] == '-' && argv[i][1] != '\0')
|
||||
continue;
|
||||
|
||||
if (isDirectory(argv[i]))
|
||||
std::string normalized = normalizePath(argv[i]);
|
||||
|
||||
if (isDirectory(normalized))
|
||||
{
|
||||
traverseDirectory(
|
||||
argv[i],
|
||||
normalized,
|
||||
[&](const std::string& name)
|
||||
{
|
||||
std::string ext = getExtension(name);
|
||||
|
@ -486,7 +457,7 @@ std::vector<std::string> getSourceFiles(int argc, char** argv)
|
|||
}
|
||||
else
|
||||
{
|
||||
files.push_back(argv[i]);
|
||||
files.push_back(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -141,8 +141,17 @@ bool RequireResolver::resolveAndStoreDefaultPaths()
|
|||
return false;
|
||||
|
||||
// resolvePath automatically sanitizes/normalizes the paths
|
||||
resolvedRequire.identifier = resolvePath(pathToResolve, identifierContext);
|
||||
resolvedRequire.absolutePath = resolvePath(pathToResolve, *absolutePathContext);
|
||||
std::optional<std::string> identifier = resolvePath(pathToResolve, identifierContext);
|
||||
std::optional<std::string> absolutePath = resolvePath(pathToResolve, *absolutePathContext);
|
||||
|
||||
if (!identifier || !absolutePath)
|
||||
{
|
||||
errorHandler.reportError("could not resolve require path");
|
||||
return false;
|
||||
}
|
||||
|
||||
resolvedRequire.identifier = std::move(*identifier);
|
||||
resolvedRequire.absolutePath = std::move(*absolutePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -181,7 +190,7 @@ std::optional<std::string> RequireResolver::getRequiringContextAbsolute()
|
|||
else
|
||||
{
|
||||
// Require statement is being executed in a file, must resolve relative to CWD
|
||||
requiringFile = resolvePath(requireContext.getPath(), joinPaths(*cwd, "stdin"));
|
||||
requiringFile = normalizePath(joinPaths(*cwd, requireContext.getPath()));
|
||||
}
|
||||
}
|
||||
std::replace(requiringFile.begin(), requiringFile.end(), '\\', '/');
|
||||
|
@ -190,7 +199,7 @@ std::optional<std::string> RequireResolver::getRequiringContextAbsolute()
|
|||
|
||||
std::string RequireResolver::getRequiringContextRelative()
|
||||
{
|
||||
return requireContext.isStdin() ? "" : requireContext.getPath();
|
||||
return requireContext.isStdin() ? "./" : requireContext.getPath();
|
||||
}
|
||||
|
||||
bool RequireResolver::substituteAliasIfPresent(std::string& path)
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "lgc.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||
LUAU_FASTFLAG(LuauCodeGenVectorDeadStoreElim)
|
||||
LUAU_FASTFLAG(LuauCodeGenLerp)
|
||||
|
||||
namespace Luau
|
||||
|
@ -501,7 +500,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
|||
build.fcvt(temp4, temp3);
|
||||
build.str(temp4, AddressA64(addr.base, addr.data + 8));
|
||||
|
||||
if (FFlag::LuauCodeGenVectorDeadStoreElim && inst.e.kind != IrOpKind::None)
|
||||
if (inst.e.kind != IrOpKind::None)
|
||||
{
|
||||
RegisterA64 temp = regs.allocTemp(KindA64::w);
|
||||
build.mov(temp, tagOp(inst.e));
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "lgc.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||
LUAU_FASTFLAG(LuauCodeGenVectorDeadStoreElim)
|
||||
LUAU_FASTFLAG(LuauCodeGenLerp)
|
||||
|
||||
namespace Luau
|
||||
|
@ -301,7 +300,7 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
|||
storeDoubleAsFloat(luauRegValueVector(vmRegOp(inst.a), 1), inst.c);
|
||||
storeDoubleAsFloat(luauRegValueVector(vmRegOp(inst.a), 2), inst.d);
|
||||
|
||||
if (FFlag::LuauCodeGenVectorDeadStoreElim && inst.e.kind != IrOpKind::None)
|
||||
if (inst.e.kind != IrOpKind::None)
|
||||
build.mov(luauRegTag(vmRegOp(inst.a)), tagOp(inst.e));
|
||||
break;
|
||||
case IrCmd::STORE_TVALUE:
|
||||
|
|
|
@ -22,7 +22,6 @@ LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
|
|||
LUAU_FASTINTVARIABLE(LuauCodeGenLiveSlotReuseLimit, 8)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
|
||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCodeGenArithOpt)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCodeGenLimitLiveSlotReuse)
|
||||
|
||||
namespace Luau
|
||||
|
@ -1314,17 +1313,12 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
|||
break;
|
||||
case IrCmd::ADD_NUM:
|
||||
case IrCmd::SUB_NUM:
|
||||
if (FFlag::LuauCodeGenArithOpt)
|
||||
if (std::optional<double> k = function.asDoubleOp(inst.b.kind == IrOpKind::Constant ? inst.b : state.tryGetValue(inst.b)))
|
||||
{
|
||||
if (std::optional<double> k = function.asDoubleOp(inst.b.kind == IrOpKind::Constant ? inst.b : state.tryGetValue(inst.b)))
|
||||
{
|
||||
// a + 0.0 and a - (-0.0) can't be folded since the behavior is different for negative zero
|
||||
// however, a - 0.0 and a + (-0.0) can be folded into a
|
||||
if (*k == 0.0 && bool(signbit(*k)) == (inst.cmd == IrCmd::ADD_NUM))
|
||||
substitute(function, inst, inst.a);
|
||||
else
|
||||
state.substituteOrRecord(inst, index);
|
||||
}
|
||||
// a + 0.0 and a - (-0.0) can't be folded since the behavior is different for negative zero
|
||||
// however, a - 0.0 and a + (-0.0) can be folded into a
|
||||
if (*k == 0.0 && bool(signbit(*k)) == (inst.cmd == IrCmd::ADD_NUM))
|
||||
substitute(function, inst, inst.a);
|
||||
else
|
||||
state.substituteOrRecord(inst, index);
|
||||
}
|
||||
|
@ -1332,19 +1326,14 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
|||
state.substituteOrRecord(inst, index);
|
||||
break;
|
||||
case IrCmd::MUL_NUM:
|
||||
if (FFlag::LuauCodeGenArithOpt)
|
||||
if (std::optional<double> k = function.asDoubleOp(inst.b.kind == IrOpKind::Constant ? inst.b : state.tryGetValue(inst.b)))
|
||||
{
|
||||
if (std::optional<double> k = function.asDoubleOp(inst.b.kind == IrOpKind::Constant ? inst.b : state.tryGetValue(inst.b)))
|
||||
{
|
||||
if (*k == 1.0) // a * 1.0 = a
|
||||
substitute(function, inst, inst.a);
|
||||
else if (*k == 2.0) // a * 2.0 = a + a
|
||||
replace(function, block, index, {IrCmd::ADD_NUM, inst.a, inst.a});
|
||||
else if (*k == -1.0) // a * -1.0 = -a
|
||||
replace(function, block, index, {IrCmd::UNM_NUM, inst.a});
|
||||
else
|
||||
state.substituteOrRecord(inst, index);
|
||||
}
|
||||
if (*k == 1.0) // a * 1.0 = a
|
||||
substitute(function, inst, inst.a);
|
||||
else if (*k == 2.0) // a * 2.0 = a + a
|
||||
replace(function, block, index, {IrCmd::ADD_NUM, inst.a, inst.a});
|
||||
else if (*k == -1.0) // a * -1.0 = -a
|
||||
replace(function, block, index, {IrCmd::UNM_NUM, inst.a});
|
||||
else
|
||||
state.substituteOrRecord(inst, index);
|
||||
}
|
||||
|
@ -1352,19 +1341,14 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
|
|||
state.substituteOrRecord(inst, index);
|
||||
break;
|
||||
case IrCmd::DIV_NUM:
|
||||
if (FFlag::LuauCodeGenArithOpt)
|
||||
if (std::optional<double> k = function.asDoubleOp(inst.b.kind == IrOpKind::Constant ? inst.b : state.tryGetValue(inst.b)))
|
||||
{
|
||||
if (std::optional<double> k = function.asDoubleOp(inst.b.kind == IrOpKind::Constant ? inst.b : state.tryGetValue(inst.b)))
|
||||
{
|
||||
if (*k == 1.0) // a / 1.0 = a
|
||||
substitute(function, inst, inst.a);
|
||||
else if (*k == -1.0) // a / -1.0 = -a
|
||||
replace(function, block, index, {IrCmd::UNM_NUM, inst.a});
|
||||
else if (int exp = 0; frexp(*k, &exp) == 0.5 && exp >= -1000 && exp <= 1000) // a / 2^k = a * 2^-k
|
||||
replace(function, block, index, {IrCmd::MUL_NUM, inst.a, build.constDouble(1.0 / *k)});
|
||||
else
|
||||
state.substituteOrRecord(inst, index);
|
||||
}
|
||||
if (*k == 1.0) // a / 1.0 = a
|
||||
substitute(function, inst, inst.a);
|
||||
else if (*k == -1.0) // a / -1.0 = -a
|
||||
replace(function, block, index, {IrCmd::UNM_NUM, inst.a});
|
||||
else if (int exp = 0; frexp(*k, &exp) == 0.5 && exp >= -1000 && exp <= 1000) // a / 2^k = a * 2^-k
|
||||
replace(function, block, index, {IrCmd::MUL_NUM, inst.a, build.constDouble(1.0 / *k)});
|
||||
else
|
||||
state.substituteOrRecord(inst, index);
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
#include "lobject.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCodeGenVectorDeadStoreElim)
|
||||
|
||||
// TODO: optimization can be improved by knowing which registers are live in at each VM exit
|
||||
|
||||
namespace Luau
|
||||
|
@ -326,27 +324,19 @@ static bool tryReplaceTagWithFullStore(
|
|||
// And value store has to follow, as the pre-DSO code would not allow GC to observe an incomplete stack variable
|
||||
if (tag != LUA_TNIL && regInfo.valueInstIdx != ~0u)
|
||||
{
|
||||
if (FFlag::LuauCodeGenVectorDeadStoreElim)
|
||||
{
|
||||
IrInst& prevValueInst = function.instructions[regInfo.valueInstIdx];
|
||||
IrInst& prevValueInst = function.instructions[regInfo.valueInstIdx];
|
||||
|
||||
if (prevValueInst.cmd == IrCmd::STORE_VECTOR)
|
||||
{
|
||||
CODEGEN_ASSERT(prevValueInst.e.kind == IrOpKind::None);
|
||||
IrOp prevValueX = prevValueInst.b;
|
||||
IrOp prevValueY = prevValueInst.c;
|
||||
IrOp prevValueZ = prevValueInst.d;
|
||||
replace(function, block, instIndex, IrInst{IrCmd::STORE_VECTOR, targetOp, prevValueX, prevValueY, prevValueZ, tagOp});
|
||||
}
|
||||
else
|
||||
{
|
||||
IrOp prevValueOp = prevValueInst.b;
|
||||
replace(function, block, instIndex, IrInst{IrCmd::STORE_SPLIT_TVALUE, targetOp, tagOp, prevValueOp});
|
||||
}
|
||||
if (prevValueInst.cmd == IrCmd::STORE_VECTOR)
|
||||
{
|
||||
CODEGEN_ASSERT(prevValueInst.e.kind == IrOpKind::None);
|
||||
IrOp prevValueX = prevValueInst.b;
|
||||
IrOp prevValueY = prevValueInst.c;
|
||||
IrOp prevValueZ = prevValueInst.d;
|
||||
replace(function, block, instIndex, IrInst{IrCmd::STORE_VECTOR, targetOp, prevValueX, prevValueY, prevValueZ, tagOp});
|
||||
}
|
||||
else
|
||||
{
|
||||
IrOp prevValueOp = function.instructions[regInfo.valueInstIdx].b;
|
||||
IrOp prevValueOp = prevValueInst.b;
|
||||
replace(function, block, instIndex, IrInst{IrCmd::STORE_SPLIT_TVALUE, targetOp, tagOp, prevValueOp});
|
||||
}
|
||||
}
|
||||
|
@ -385,7 +375,7 @@ static bool tryReplaceTagWithFullStore(
|
|||
state.hasGcoToClear |= regInfo.maybeGco;
|
||||
return true;
|
||||
}
|
||||
else if (FFlag::LuauCodeGenVectorDeadStoreElim && prev.cmd == IrCmd::STORE_VECTOR)
|
||||
else if (prev.cmd == IrCmd::STORE_VECTOR)
|
||||
{
|
||||
// If the 'nil' is stored, we keep 'STORE_TAG Rn, tnil' as it writes the 'full' TValue
|
||||
if (tag != LUA_TNIL)
|
||||
|
@ -455,7 +445,7 @@ static bool tryReplaceValueWithFullStore(
|
|||
regInfo.tvalueInstIdx = instIndex;
|
||||
return true;
|
||||
}
|
||||
else if (FFlag::LuauCodeGenVectorDeadStoreElim && prev.cmd == IrCmd::STORE_VECTOR)
|
||||
else if (prev.cmd == IrCmd::STORE_VECTOR)
|
||||
{
|
||||
IrOp prevTagOp = prev.e;
|
||||
CODEGEN_ASSERT(prevTagOp.kind != IrOpKind::None);
|
||||
|
@ -483,8 +473,6 @@ static bool tryReplaceVectorValueWithFullStore(
|
|||
StoreRegInfo& regInfo
|
||||
)
|
||||
{
|
||||
CODEGEN_ASSERT(FFlag::LuauCodeGenVectorDeadStoreElim);
|
||||
|
||||
// If the tag+value pair is established, we can mark both as dead and use a single split TValue store
|
||||
if (regInfo.tagInstIdx != ~0u && regInfo.valueInstIdx != ~0u)
|
||||
{
|
||||
|
@ -631,29 +619,22 @@ static void markDeadStoresInInst(RemoveDeadStoreState& state, IrBuilder& build,
|
|||
case IrCmd::STORE_VECTOR:
|
||||
if (inst.a.kind == IrOpKind::VmReg)
|
||||
{
|
||||
if (FFlag::LuauCodeGenVectorDeadStoreElim)
|
||||
{
|
||||
int reg = vmRegOp(inst.a);
|
||||
int reg = vmRegOp(inst.a);
|
||||
|
||||
if (function.cfg.captured.regs.test(reg))
|
||||
return;
|
||||
if (function.cfg.captured.regs.test(reg))
|
||||
return;
|
||||
|
||||
StoreRegInfo& regInfo = state.info[reg];
|
||||
StoreRegInfo& regInfo = state.info[reg];
|
||||
|
||||
if (tryReplaceVectorValueWithFullStore(state, build, function, block, index, regInfo))
|
||||
break;
|
||||
if (tryReplaceVectorValueWithFullStore(state, build, function, block, index, regInfo))
|
||||
break;
|
||||
|
||||
// Partial value store can be removed by a new one if the tag is known
|
||||
if (regInfo.knownTag != kUnknownTag)
|
||||
state.killValueStore(regInfo);
|
||||
// Partial value store can be removed by a new one if the tag is known
|
||||
if (regInfo.knownTag != kUnknownTag)
|
||||
state.killValueStore(regInfo);
|
||||
|
||||
regInfo.valueInstIdx = index;
|
||||
regInfo.maybeGco = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.useReg(vmRegOp(inst.a));
|
||||
}
|
||||
regInfo.valueInstIdx = index;
|
||||
regInfo.maybeGco = false;
|
||||
}
|
||||
break;
|
||||
case IrCmd::STORE_TVALUE:
|
||||
|
|
|
@ -14,7 +14,7 @@ inline bool isFlagExperimental(const char* flag)
|
|||
"LuauInstantiateInSubtyping", // requires some fixes to lua-apps code
|
||||
"LuauFixIndexerSubtypingOrdering", // requires some small fixes to lua-apps code since this fixes a false negative
|
||||
"StudioReportLuauAny2", // takes telemetry data for usage of any types
|
||||
"LuauTableCloneClonesType", // requires fixes in lua-apps code, terrifyingly
|
||||
"LuauTableCloneClonesType2", // requires fixes in lua-apps code, terrifyingly
|
||||
"LuauSolverV2",
|
||||
// makes sure we always have at least one entry
|
||||
nullptr,
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include <array>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileDisabledBuiltins)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileMathLerp)
|
||||
|
||||
namespace Luau
|
||||
|
@ -297,36 +296,33 @@ struct BuiltinVisitor : AstVisitor
|
|||
, options(options)
|
||||
, names(names)
|
||||
{
|
||||
if (FFlag::LuauCompileDisabledBuiltins)
|
||||
builtinIsDisabled.fill(false);
|
||||
|
||||
if (const char* const* ptr = options.disabledBuiltins)
|
||||
{
|
||||
builtinIsDisabled.fill(false);
|
||||
|
||||
if (const char* const* ptr = options.disabledBuiltins)
|
||||
for (; *ptr; ++ptr)
|
||||
{
|
||||
for (; *ptr; ++ptr)
|
||||
if (const char* dot = strchr(*ptr, '.'))
|
||||
{
|
||||
if (const char* dot = strchr(*ptr, '.'))
|
||||
AstName library = names.getWithType(*ptr, dot - *ptr).first;
|
||||
AstName name = names.get(dot + 1);
|
||||
|
||||
if (library.value && name.value && getGlobalState(globals, name) == Global::Default)
|
||||
{
|
||||
AstName library = names.getWithType(*ptr, dot - *ptr).first;
|
||||
AstName name = names.get(dot + 1);
|
||||
Builtin builtin = Builtin{library, name};
|
||||
|
||||
if (library.value && name.value && getGlobalState(globals, name) == Global::Default)
|
||||
{
|
||||
Builtin builtin = Builtin{library, name};
|
||||
|
||||
if (int bfid = getBuiltinFunctionId(builtin, options); bfid >= 0)
|
||||
builtinIsDisabled[bfid] = true;
|
||||
}
|
||||
if (int bfid = getBuiltinFunctionId(builtin, options); bfid >= 0)
|
||||
builtinIsDisabled[bfid] = true;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AstName name = names.get(*ptr); name.value && getGlobalState(globals, name) == Global::Default)
|
||||
{
|
||||
if (AstName name = names.get(*ptr); name.value && getGlobalState(globals, name) == Global::Default)
|
||||
{
|
||||
Builtin builtin = Builtin{AstName(), name};
|
||||
Builtin builtin = Builtin{AstName(), name};
|
||||
|
||||
if (int bfid = getBuiltinFunctionId(builtin, options); bfid >= 0)
|
||||
builtinIsDisabled[bfid] = true;
|
||||
}
|
||||
if (int bfid = getBuiltinFunctionId(builtin, options); bfid >= 0)
|
||||
builtinIsDisabled[bfid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,7 +337,7 @@ struct BuiltinVisitor : AstVisitor
|
|||
|
||||
int bfid = getBuiltinFunctionId(builtin, options);
|
||||
|
||||
if (FFlag::LuauCompileDisabledBuiltins && bfid >= 0 && builtinIsDisabled[bfid])
|
||||
if (bfid >= 0 && builtinIsDisabled[bfid])
|
||||
bfid = -1;
|
||||
|
||||
// getBuiltinFunctionId optimistically assumes all select() calls are builtin but actually the second argument must be a vararg
|
||||
|
|
|
@ -26,9 +26,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25)
|
|||
LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300)
|
||||
LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileOptimizeRevArith)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileLibraryConstants)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -1624,8 +1621,7 @@ struct Compiler
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauCompileOptimizeRevArith && options.optimizationLevel >= 2 &&
|
||||
(expr->op == AstExprBinary::Add || expr->op == AstExprBinary::Mul))
|
||||
else if (options.optimizationLevel >= 2 && (expr->op == AstExprBinary::Add || expr->op == AstExprBinary::Mul))
|
||||
{
|
||||
// Optimization: replace k*r with r*k when r is known to be a number (otherwise metamethods may be called)
|
||||
if (LuauBytecodeType* ty = exprTypes.find(expr); ty && *ty == LBC_TYPE_NUMBER)
|
||||
|
@ -4226,17 +4222,14 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
|
|||
{
|
||||
compiler.builtinsFoldLibraryK = true;
|
||||
}
|
||||
else if (FFlag::LuauCompileLibraryConstants)
|
||||
else if (const char* const* ptr = options.librariesWithKnownMembers)
|
||||
{
|
||||
if (const char* const* ptr = options.librariesWithKnownMembers)
|
||||
for (; *ptr; ++ptr)
|
||||
{
|
||||
for (; *ptr; ++ptr)
|
||||
if (AstName name = names.get(*ptr); name.value && getGlobalState(compiler.globals, name) == Global::Default)
|
||||
{
|
||||
if (AstName name = names.get(*ptr); name.value && getGlobalState(compiler.globals, name) == Global::Default)
|
||||
{
|
||||
compiler.builtinsFoldLibraryK = true;
|
||||
break;
|
||||
}
|
||||
compiler.builtinsFoldLibraryK = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
||||
LUAU_FASTFLAGVARIABLE(LuauVectorFolding)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Compile
|
||||
|
@ -60,7 +57,7 @@ static void foldUnary(Constant& result, AstExprUnary::Op op, const Constant& arg
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = -arg.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && arg.type == Constant::Type_Vector)
|
||||
else if (arg.type == Constant::Type_Vector)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = -arg.valueVector[0];
|
||||
|
@ -93,7 +90,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = la.valueNumber + ra.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = la.valueVector[0] + ra.valueVector[0];
|
||||
|
@ -109,7 +106,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = la.valueNumber - ra.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
result.type = Constant::Type_Vector;
|
||||
result.valueVector[0] = la.valueVector[0] - ra.valueVector[0];
|
||||
|
@ -125,7 +122,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = la.valueNumber * ra.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
|
||||
float resultW = la.valueVector[3] * ra.valueVector[3];
|
||||
|
@ -139,7 +136,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
else if (la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = ra.valueVector[3] != 0.0f;
|
||||
float resultW = float(la.valueNumber) * ra.valueVector[3];
|
||||
|
@ -153,7 +150,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f;
|
||||
float resultW = la.valueVector[3] * float(ra.valueNumber);
|
||||
|
@ -175,7 +172,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = la.valueNumber / ra.valueNumber;
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
|
||||
float resultW = la.valueVector[3] / ra.valueVector[3];
|
||||
|
@ -189,7 +186,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
else if (la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = ra.valueVector[3] != 0.0f;
|
||||
float resultW = float(la.valueNumber) / ra.valueVector[3];
|
||||
|
@ -203,7 +200,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f;
|
||||
float resultW = la.valueVector[3] / float(ra.valueNumber);
|
||||
|
@ -225,7 +222,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.type = Constant::Type_Number;
|
||||
result.valueNumber = floor(la.valueNumber / ra.valueNumber);
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f || ra.valueVector[3] != 0.0f;
|
||||
float resultW = floor(la.valueVector[3] / ra.valueVector[3]);
|
||||
|
@ -239,7 +236,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
else if (la.type == Constant::Type_Number && ra.type == Constant::Type_Vector)
|
||||
{
|
||||
bool hadW = ra.valueVector[3] != 0.0f;
|
||||
float resultW = floor(float(la.valueNumber) / ra.valueVector[3]);
|
||||
|
@ -253,7 +250,7 @@ static void foldBinary(Constant& result, AstExprBinary::Op op, const Constant& l
|
|||
result.valueVector[3] = resultW;
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauVectorFolding && la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
else if (la.type == Constant::Type_Vector && ra.type == Constant::Type_Number)
|
||||
{
|
||||
bool hadW = la.valueVector[3] != 0.0f;
|
||||
float resultW = floor(la.valueVector[3] / float(ra.valueNumber));
|
||||
|
@ -474,24 +471,14 @@ struct ConstantVisitor : AstVisitor
|
|||
|
||||
if (foldLibraryK)
|
||||
{
|
||||
if (FFlag::LuauCompileLibraryConstants)
|
||||
if (AstExprGlobal* eg = expr->expr->as<AstExprGlobal>())
|
||||
{
|
||||
if (AstExprGlobal* eg = expr->expr->as<AstExprGlobal>())
|
||||
{
|
||||
if (eg->name == "math")
|
||||
result = foldBuiltinMath(expr->index);
|
||||
|
||||
// if we have a custom handler and the constant hasn't been resolved
|
||||
if (libraryMemberConstantCb && result.type == Constant::Type_Unknown)
|
||||
libraryMemberConstantCb(eg->name.value, expr->index.value, reinterpret_cast<Luau::CompileConstant*>(&result));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AstExprGlobal* eg = expr->expr->as<AstExprGlobal>(); eg && eg->name == "math")
|
||||
{
|
||||
if (eg->name == "math")
|
||||
result = foldBuiltinMath(expr->index);
|
||||
}
|
||||
|
||||
// if we have a custom handler and the constant hasn't been resolved
|
||||
if (libraryMemberConstantCb && result.type == Constant::Type_Unknown)
|
||||
libraryMemberConstantCb(eg->name.value, expr->index.value, reinterpret_cast<Luau::CompileConstant*>(&result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include "Luau/BytecodeBuilder.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -182,8 +180,6 @@ static bool isMatchingGlobalMember(
|
|||
const char* member
|
||||
)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauCompileLibraryConstants);
|
||||
|
||||
if (AstExprGlobal* object = expr->expr->as<AstExprGlobal>())
|
||||
return getGlobalState(globals, object->name) == Compile::Global::Default && object->name == library && expr->index == member;
|
||||
|
||||
|
@ -481,50 +477,45 @@ struct TypeMapVisitor : AstVisitor
|
|||
if (node->index == "X" || node->index == "Y" || node->index == "Z")
|
||||
{
|
||||
recordResolvedType(node, &builtinTypes.numberType);
|
||||
|
||||
if (FFlag::LuauCompileLibraryConstants)
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauCompileLibraryConstants)
|
||||
if (isMatchingGlobalMember(globals, node, "vector", "zero") || isMatchingGlobalMember(globals, node, "vector", "one"))
|
||||
{
|
||||
if (isMatchingGlobalMember(globals, node, "vector", "zero") || isMatchingGlobalMember(globals, node, "vector", "one"))
|
||||
{
|
||||
recordResolvedType(node, &builtinTypes.vectorType);
|
||||
return false;
|
||||
}
|
||||
recordResolvedType(node, &builtinTypes.vectorType);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (libraryMemberTypeCb)
|
||||
if (libraryMemberTypeCb)
|
||||
{
|
||||
if (AstExprGlobal* object = node->expr->as<AstExprGlobal>())
|
||||
{
|
||||
if (AstExprGlobal* object = node->expr->as<AstExprGlobal>())
|
||||
if (LuauBytecodeType ty = LuauBytecodeType(libraryMemberTypeCb(object->name.value, node->index.value)); ty != LBC_TYPE_ANY)
|
||||
{
|
||||
if (LuauBytecodeType ty = LuauBytecodeType(libraryMemberTypeCb(object->name.value, node->index.value)); ty != LBC_TYPE_ANY)
|
||||
// TODO: 'resolvedExprs' is more limited than 'exprTypes' which limits full inference of more complex types that a user
|
||||
// callback can return
|
||||
switch (ty)
|
||||
{
|
||||
// TODO: 'resolvedExprs' is more limited than 'exprTypes' which limits full inference of more complex types that a user
|
||||
// callback can return
|
||||
switch (ty)
|
||||
{
|
||||
case LBC_TYPE_BOOLEAN:
|
||||
resolvedExprs[node] = &builtinTypes.booleanType;
|
||||
break;
|
||||
case LBC_TYPE_NUMBER:
|
||||
resolvedExprs[node] = &builtinTypes.numberType;
|
||||
break;
|
||||
case LBC_TYPE_STRING:
|
||||
resolvedExprs[node] = &builtinTypes.stringType;
|
||||
break;
|
||||
case LBC_TYPE_VECTOR:
|
||||
resolvedExprs[node] = &builtinTypes.vectorType;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
exprTypes[node] = ty;
|
||||
return false;
|
||||
case LBC_TYPE_BOOLEAN:
|
||||
resolvedExprs[node] = &builtinTypes.booleanType;
|
||||
break;
|
||||
case LBC_TYPE_NUMBER:
|
||||
resolvedExprs[node] = &builtinTypes.numberType;
|
||||
break;
|
||||
case LBC_TYPE_STRING:
|
||||
resolvedExprs[node] = &builtinTypes.stringType;
|
||||
break;
|
||||
case LBC_TYPE_VECTOR:
|
||||
resolvedExprs[node] = &builtinTypes.vectorType;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
exprTypes[node] = ty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -336,6 +336,7 @@ LUA_API const char* lua_getlightuserdataname(lua_State* L, int tag);
|
|||
LUA_API void lua_clonefunction(lua_State* L, int idx);
|
||||
|
||||
LUA_API void lua_cleartable(lua_State* L, int idx);
|
||||
LUA_API void lua_clonetable(lua_State* L, int idx);
|
||||
|
||||
LUA_API lua_Alloc lua_getallocf(lua_State* L, void** ud);
|
||||
|
||||
|
|
|
@ -1539,6 +1539,16 @@ void lua_cleartable(lua_State* L, int idx)
|
|||
luaH_clear(tt);
|
||||
}
|
||||
|
||||
void lua_clonetable(lua_State* L, int idx)
|
||||
{
|
||||
StkId t = index2addr(L, idx);
|
||||
api_check(L, ttistable(t));
|
||||
|
||||
LuaTable* tt = luaH_clone(L, hvalue(t));
|
||||
sethvalue(L, L->top, tt);
|
||||
api_incr_top(L);
|
||||
}
|
||||
|
||||
lua_Callbacks* lua_callbacks(lua_State* L)
|
||||
{
|
||||
return &L->global->cb;
|
||||
|
|
|
@ -269,7 +269,13 @@ static int buffer_readbits(lua_State* L)
|
|||
unsigned endbyte = unsigned((bitoffset + bitcount + 7) / 8);
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
#if LUAU_BIG_ENDIAN
|
||||
for (int i = int(endbyte) - 1; i >= int(startbyte); i--)
|
||||
data = (data << 8) + uint8_t(((char*)buf)[i]);
|
||||
#else
|
||||
memcpy(&data, (char*)buf + startbyte, endbyte - startbyte);
|
||||
#endif
|
||||
|
||||
uint64_t subbyteoffset = bitoffset & 0x7;
|
||||
uint64_t mask = (1ull << bitcount) - 1;
|
||||
|
@ -299,14 +305,28 @@ static int buffer_writebits(lua_State* L)
|
|||
unsigned endbyte = unsigned((bitoffset + bitcount + 7) / 8);
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
#if LUAU_BIG_ENDIAN
|
||||
for (int i = int(endbyte) - 1; i >= int(startbyte); i--)
|
||||
data = data * 256 + uint8_t(((char*)buf)[i]);
|
||||
#else
|
||||
memcpy(&data, (char*)buf + startbyte, endbyte - startbyte);
|
||||
#endif
|
||||
|
||||
uint64_t subbyteoffset = bitoffset & 0x7;
|
||||
uint64_t mask = ((1ull << bitcount) - 1) << subbyteoffset;
|
||||
|
||||
data = (data & ~mask) | ((uint64_t(value) << subbyteoffset) & mask);
|
||||
|
||||
#if LUAU_BIG_ENDIAN
|
||||
for (int i = int(startbyte); i < int(endbyte); i++)
|
||||
{
|
||||
((char*)buf)[i] = data & 0xff;
|
||||
data >>= 8;
|
||||
}
|
||||
#else
|
||||
memcpy((char*)buf + startbyte, &data, endbyte - startbyte);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauDebugInfoInvArgLeftovers, false)
|
||||
|
||||
static lua_State* getthread(lua_State* L, int* arg)
|
||||
{
|
||||
if (lua_isthread(L, 1))
|
||||
|
@ -110,7 +108,7 @@ static int db_info(lua_State* L)
|
|||
|
||||
default:
|
||||
// restore stack state of another thread as 'f' option might not have been visited yet
|
||||
if (DFFlag::LuauDebugInfoInvArgLeftovers && L != L1)
|
||||
if (L != L1)
|
||||
lua_settop(L1, l1top);
|
||||
|
||||
luaL_argerror(L, arg + 2, "invalid option");
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauVectorMetatable)
|
||||
LUAU_FASTFLAGVARIABLE(LuauVector2Constructor)
|
||||
|
||||
static int vector_create(lua_State* L)
|
||||
|
@ -261,8 +260,6 @@ static int vector_max(lua_State* L)
|
|||
|
||||
static int vector_index(lua_State* L)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauVectorMetatable);
|
||||
|
||||
const float* v = luaL_checkvector(L, 1);
|
||||
size_t namelen = 0;
|
||||
const char* name = luaL_checklstring(L, 2, &namelen);
|
||||
|
@ -307,8 +304,6 @@ static const luaL_Reg vectorlib[] = {
|
|||
|
||||
static void createmetatable(lua_State* L)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauVectorMetatable);
|
||||
|
||||
lua_createtable(L, 0, 1); // create metatable for vectors
|
||||
|
||||
// push dummy vector
|
||||
|
@ -345,8 +340,7 @@ int luaopen_vector(lua_State* L)
|
|||
lua_setfield(L, -2, "one");
|
||||
#endif
|
||||
|
||||
if (FFlag::LuauVectorMetatable)
|
||||
createmetatable(L);
|
||||
createmetatable(L);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -23,11 +23,7 @@ LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost)
|
|||
LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
|
||||
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
|
||||
LUAU_FASTINT(LuauRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauCompileOptimizeRevArith)
|
||||
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
||||
LUAU_FASTFLAG(LuauVectorFolding)
|
||||
LUAU_FASTFLAG(LuauVector2Constants)
|
||||
LUAU_FASTFLAG(LuauCompileDisabledBuiltins)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -1491,8 +1487,6 @@ RETURN R0 1
|
|||
|
||||
TEST_CASE("ConstantFoldVectorArith")
|
||||
{
|
||||
ScopedFastFlag luauVectorFolding{FFlag::LuauVectorFolding, true};
|
||||
|
||||
CHECK_EQ("\n" + compileFunction("local n = 2; local a, b = vector.create(1, 2, 3), vector.create(2, 4, 8); return a + b", 0, 2), R"(
|
||||
LOADK R0 K0 [3, 6, 11]
|
||||
RETURN R0 1
|
||||
|
@ -1558,8 +1552,6 @@ RETURN R0 1
|
|||
|
||||
TEST_CASE("ConstantFoldVectorArith4Wide")
|
||||
{
|
||||
ScopedFastFlag luauVectorFolding{FFlag::LuauVectorFolding, true};
|
||||
|
||||
CHECK_EQ("\n" + compileFunction("local n = 2; local a, b = vector.create(1, 2, 3, 4), vector.create(2, 4, 8, 1); return a + b", 0, 2), R"(
|
||||
LOADK R0 K0 [3, 6, 11, 5]
|
||||
RETURN R0 1
|
||||
|
@ -5147,8 +5139,6 @@ RETURN R0 1
|
|||
|
||||
TEST_CASE("VectorConstantFields")
|
||||
{
|
||||
ScopedFastFlag luauCompileLibraryConstants{FFlag::LuauCompileLibraryConstants, true};
|
||||
|
||||
CHECK_EQ("\n" + compileFunction("return vector.one, vector.zero", 0, 2), R"(
|
||||
LOADK R0 K0 [1, 1, 1]
|
||||
LOADK R1 K1 [0, 0, 0]
|
||||
|
@ -5169,8 +5159,6 @@ RETURN R0 1
|
|||
|
||||
TEST_CASE("CustomConstantFields")
|
||||
{
|
||||
ScopedFastFlag luauCompileLibraryConstants{FFlag::LuauCompileLibraryConstants, true};
|
||||
|
||||
CHECK_EQ("\n" + compileFunction("return test.some_nil, test.some_boolean, test.some_number, test.some_string", 0, 2), R"(
|
||||
LOADNIL R0
|
||||
LOADB R1 1
|
||||
|
@ -7904,8 +7892,6 @@ RETURN R0 1
|
|||
|
||||
TEST_CASE("BuiltinFoldingProhibitedInOptions")
|
||||
{
|
||||
ScopedFastFlag luauCompileDisabledBuiltins{FFlag::LuauCompileDisabledBuiltins, true};
|
||||
|
||||
Luau::BytecodeBuilder bcb;
|
||||
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code);
|
||||
Luau::CompileOptions options;
|
||||
|
@ -9096,8 +9082,6 @@ RETURN R0 1
|
|||
|
||||
TEST_CASE("ArithRevK")
|
||||
{
|
||||
ScopedFastFlag sff(FFlag::LuauCompileOptimizeRevArith, true);
|
||||
|
||||
// - and / have special optimized form for reverse constants; in absence of type information, we can't optimize other ops
|
||||
CHECK_EQ(
|
||||
"\n" + compileFunction0(R"(
|
||||
|
|
|
@ -35,10 +35,8 @@ LUAU_FASTFLAG(LuauMathLerp)
|
|||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauDebugInfoInvArgLeftovers)
|
||||
LUAU_FASTFLAG(LuauVectorLibNativeCodegen)
|
||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||
LUAU_FASTFLAG(LuauVectorMetatable)
|
||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
||||
LUAU_FASTFLAG(LuauBufferBitMethods2)
|
||||
LUAU_FASTFLAG(LuauCodeGenLimitLiveSlotReuse)
|
||||
|
@ -897,7 +895,6 @@ TEST_CASE("VectorLibrary")
|
|||
{
|
||||
ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true};
|
||||
ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true};
|
||||
ScopedFastFlag luauVectorMetatable{FFlag::LuauVectorMetatable, true};
|
||||
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
|
||||
|
||||
lua_CompileOptions copts = defaultOptions();
|
||||
|
@ -1024,8 +1021,6 @@ TEST_CASE("DateTime")
|
|||
|
||||
TEST_CASE("Debug")
|
||||
{
|
||||
ScopedFastFlag luauDebugInfoInvArgLeftovers{DFFlag::LuauDebugInfoInvArgLeftovers, true};
|
||||
|
||||
runConformance("debug.lua");
|
||||
}
|
||||
|
||||
|
@ -1392,6 +1387,25 @@ TEST_CASE("ApiTables")
|
|||
CHECK(strcmp(lua_tostring(L, -1), "test") == 0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// lua_clonetable
|
||||
lua_clonetable(L, -1);
|
||||
|
||||
CHECK(lua_getfield(L, -1, "key") == LUA_TNUMBER);
|
||||
CHECK(lua_tonumber(L, -1) == 123.0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// modify clone
|
||||
lua_pushnumber(L, 456.0);
|
||||
lua_rawsetfield(L, -2, "key");
|
||||
|
||||
// remove clone
|
||||
lua_pop(L, 1);
|
||||
|
||||
// check original
|
||||
CHECK(lua_getfield(L, -1, "key") == LUA_TNUMBER);
|
||||
CHECK(lua_tonumber(L, -1) == 123.0);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// lua_cleartable
|
||||
lua_cleartable(L, -1);
|
||||
lua_pushnil(L);
|
||||
|
|
|
@ -342,8 +342,11 @@ ParseResult Fixture::matchParseErrorPrefix(const std::string& source, const std:
|
|||
return result;
|
||||
}
|
||||
|
||||
ModulePtr Fixture::getMainModule()
|
||||
ModulePtr Fixture::getMainModule(bool forAutocomplete)
|
||||
{
|
||||
if (forAutocomplete && !FFlag::LuauSolverV2)
|
||||
return frontend.moduleResolverForAutocomplete.getModule(fromString(mainModuleName));
|
||||
|
||||
return frontend.moduleResolver.getModule(fromString(mainModuleName));
|
||||
}
|
||||
|
||||
|
@ -366,9 +369,9 @@ std::optional<PrimitiveType::Type> Fixture::getPrimitiveType(TypeId ty)
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TypeId> Fixture::getType(const std::string& name)
|
||||
std::optional<TypeId> Fixture::getType(const std::string& name, bool forAutocomplete)
|
||||
{
|
||||
ModulePtr module = getMainModule();
|
||||
ModulePtr module = getMainModule(forAutocomplete);
|
||||
REQUIRE(module);
|
||||
|
||||
if (!module->hasModuleScope())
|
||||
|
@ -520,6 +523,9 @@ void Fixture::registerTestTypes()
|
|||
|
||||
void Fixture::dumpErrors(const CheckResult& cr)
|
||||
{
|
||||
if (hasDumpedErrors)
|
||||
return;
|
||||
hasDumpedErrors = true;
|
||||
std::string error = getErrors(cr);
|
||||
if (!error.empty())
|
||||
MESSAGE(error);
|
||||
|
@ -527,6 +533,9 @@ void Fixture::dumpErrors(const CheckResult& cr)
|
|||
|
||||
void Fixture::dumpErrors(const ModulePtr& module)
|
||||
{
|
||||
if (hasDumpedErrors)
|
||||
return;
|
||||
hasDumpedErrors = true;
|
||||
std::stringstream ss;
|
||||
dumpErrors(ss, module->errors);
|
||||
if (!ss.str().empty())
|
||||
|
@ -535,6 +544,9 @@ void Fixture::dumpErrors(const ModulePtr& module)
|
|||
|
||||
void Fixture::dumpErrors(const Module& module)
|
||||
{
|
||||
if (hasDumpedErrors)
|
||||
return;
|
||||
hasDumpedErrors = true;
|
||||
std::stringstream ss;
|
||||
dumpErrors(ss, module.errors);
|
||||
if (!ss.str().empty())
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
||||
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)
|
||||
|
||||
#define DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(line) ScopedFastFlag sff_##line{FFlag::LuauSolverV2, FFlag::DebugLuauForceAllNewSolverTests};
|
||||
|
||||
|
@ -89,11 +88,11 @@ struct Fixture
|
|||
// Verify a parse error occurs and the parse error message has the specified prefix
|
||||
ParseResult matchParseErrorPrefix(const std::string& source, const std::string& prefix);
|
||||
|
||||
ModulePtr getMainModule();
|
||||
ModulePtr getMainModule(bool forAutocomplete = false);
|
||||
SourceModule* getMainSourceModule();
|
||||
|
||||
std::optional<PrimitiveType::Type> getPrimitiveType(TypeId ty);
|
||||
std::optional<TypeId> getType(const std::string& name);
|
||||
std::optional<TypeId> getType(const std::string& name, bool forAutocomplete = false);
|
||||
TypeId requireType(const std::string& name);
|
||||
TypeId requireType(const ModuleName& moduleName, const std::string& name);
|
||||
TypeId requireType(const ModulePtr& module, const std::string& name);
|
||||
|
@ -113,8 +112,6 @@ struct Fixture
|
|||
// In that case, flag can be forced to 'true' using the example below:
|
||||
// ScopedFastFlag sff_LuauExampleFlagDefinition{FFlag::LuauExampleFlagDefinition, true};
|
||||
|
||||
ScopedFastFlag sff_LuauVectorDefinitionsExtra{FFlag::LuauVectorDefinitionsExtra, true};
|
||||
|
||||
// Arena freezing marks the `TypeArena`'s underlying memory as read-only, raising an access violation whenever you mutate it.
|
||||
// This is useful for tracking down violations of Luau's memory model.
|
||||
ScopedFastFlag sff_DebugLuauFreezeArena{FFlag::DebugLuauFreezeArena, true};
|
||||
|
@ -142,11 +139,14 @@ struct Fixture
|
|||
void registerTestTypes();
|
||||
|
||||
LoadDefinitionFileResult loadDefinition(const std::string& source, bool forAutocomplete = false);
|
||||
|
||||
private:
|
||||
bool hasDumpedErrors = false;
|
||||
};
|
||||
|
||||
struct BuiltinsFixture : Fixture
|
||||
{
|
||||
BuiltinsFixture(bool prepareAutocomplete = false);
|
||||
explicit BuiltinsFixture(bool prepareAutocomplete = false);
|
||||
};
|
||||
|
||||
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments);
|
||||
|
@ -180,6 +180,18 @@ std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name);
|
|||
void registerHiddenTypes(Frontend* frontend);
|
||||
void createSomeClasses(Frontend* frontend);
|
||||
|
||||
template <typename E>
|
||||
const E* findError(const CheckResult& result)
|
||||
{
|
||||
for (const auto& e : result.errors)
|
||||
{
|
||||
if (auto p = get<E>(e))
|
||||
return p;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename BaseFixture>
|
||||
struct DifferFixtureGeneric : BaseFixture
|
||||
{
|
||||
|
@ -329,3 +341,51 @@ using DifferFixtureWithBuiltins = DifferFixtureGeneric<BuiltinsFixture>;
|
|||
} \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define LUAU_REQUIRE_ERROR(result, Type) \
|
||||
do \
|
||||
{ \
|
||||
using T = Type; \
|
||||
const auto& res = (result); \
|
||||
if (!findError<T>(res)) \
|
||||
{ \
|
||||
dumpErrors(res); \
|
||||
REQUIRE_MESSAGE(false, "Expected to find " #Type " error"); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define LUAU_CHECK_ERROR(result, Type) \
|
||||
do \
|
||||
{ \
|
||||
using T = Type; \
|
||||
const auto& res = (result); \
|
||||
if (!findError<T>(res)) \
|
||||
{ \
|
||||
dumpErrors(res); \
|
||||
CHECK_MESSAGE(false, "Expected to find " #Type " error"); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define LUAU_REQUIRE_NO_ERROR(result, Type) \
|
||||
do \
|
||||
{ \
|
||||
using T = Type; \
|
||||
const auto& res = (result); \
|
||||
if (findError<T>(res)) \
|
||||
{ \
|
||||
dumpErrors(res); \
|
||||
REQUIRE_MESSAGE(false, "Expected to find no " #Type " error"); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define LUAU_CHECK_NO_ERROR(result, Type) \
|
||||
do \
|
||||
{ \
|
||||
using T = Type; \
|
||||
const auto& res = (result); \
|
||||
if (findError<T>(res)) \
|
||||
{ \
|
||||
dumpErrors(res); \
|
||||
CHECK_MESSAGE(false, "Expected to find no " #Type " error"); \
|
||||
} \
|
||||
} while (false)
|
||||
|
|
|
@ -714,6 +714,77 @@ TEST_SUITE_END();
|
|||
|
||||
TEST_SUITE_BEGIN("FragmentAutocompleteTests");
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "multiple_fragment_autocomplete")
|
||||
{
|
||||
ToStringOptions opt;
|
||||
opt.exhaustive = true;
|
||||
opt.exhaustive = true;
|
||||
opt.functionTypeArguments = true;
|
||||
opt.maxTableLength = 0;
|
||||
opt.maxTypeLength = 0;
|
||||
|
||||
auto checkAndExamine = [&](const std::string& src, const std::string& idName, const std::string& idString)
|
||||
{
|
||||
check(src, getOptions());
|
||||
auto id = getType(idName, true);
|
||||
LUAU_ASSERT(id);
|
||||
CHECK_EQ(Luau::toString(*id, opt), idString);
|
||||
};
|
||||
|
||||
auto getTypeFromModule = [](ModulePtr module, const std::string& name) -> std::optional<TypeId>
|
||||
{
|
||||
if (!module->hasModuleScope())
|
||||
return std::nullopt;
|
||||
return lookupName(module->getModuleScope(), name);
|
||||
};
|
||||
|
||||
auto fragmentACAndCheck = [&](const std::string& updated,
|
||||
const Position& pos,
|
||||
const std::string& idName,
|
||||
const std::string& srcIdString,
|
||||
const std::string& fragIdString)
|
||||
{
|
||||
FragmentAutocompleteResult result = autocompleteFragment(updated, pos, std::nullopt);
|
||||
auto fragId = getTypeFromModule(result.incrementalModule, idName);
|
||||
LUAU_ASSERT(fragId);
|
||||
CHECK_EQ(Luau::toString(*fragId, opt), fragIdString);
|
||||
|
||||
auto srcId = getType(idName, true);
|
||||
LUAU_ASSERT(srcId);
|
||||
CHECK_EQ(Luau::toString(*srcId, opt), srcIdString);
|
||||
};
|
||||
|
||||
const std::string source = R"(local module = {}
|
||||
f
|
||||
return module)";
|
||||
|
||||
const std::string updated1 = R"(local module = {}
|
||||
function module.a
|
||||
return module)";
|
||||
|
||||
const std::string updated2 = R"(local module = {}
|
||||
function module.ab
|
||||
return module)";
|
||||
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
|
||||
checkAndExamine(source, "module", "{ }");
|
||||
// [TODO] CLI-140762 we shouldn't mutate stale module in autocompleteFragment
|
||||
// early return since the following checking will fail, which it shouldn't!
|
||||
// fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }");
|
||||
// fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }");
|
||||
}
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
|
||||
checkAndExamine(source, "module", "{ }");
|
||||
// [TODO] CLI-140762 we shouldn't mutate stale module in autocompleteFragment
|
||||
// early return since the following checking will fail, which it shouldn't!
|
||||
return;
|
||||
fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }");
|
||||
fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_autocomplete_simple_property_access")
|
||||
{
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ LUAU_FASTFLAG(LuauSolverV2);
|
|||
LUAU_FASTFLAG(DebugLuauFreezeArena);
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
||||
LUAU_FASTFLAG(LuauReferenceAllocatorInNewSolver);
|
||||
LUAU_FASTFLAG(LuauSelectivelyRetainDFGArena)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -1541,4 +1542,34 @@ TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_allocator")
|
|||
CHECK_EQ(module->names.get(), source->names.get());
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FrontendFixture, "dfg_data_cleared_on_retain_type_graphs_unset")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauSelectivelyRetainDFGArena, true}
|
||||
};
|
||||
fileResolver.source["game/A"] = R"(
|
||||
local a = 1
|
||||
local b = 2
|
||||
local c = 3
|
||||
return {x = a, y = b, z = c}
|
||||
)";
|
||||
|
||||
frontend.options.retainFullTypeGraphs = true;
|
||||
frontend.check("game/A");
|
||||
|
||||
auto mod = frontend.moduleResolver.getModule("game/A");
|
||||
CHECK(!mod->defArena.allocator.empty());
|
||||
CHECK(!mod->keyArena.allocator.empty());
|
||||
|
||||
// We should check that the dfg arena is empty once retainFullTypeGraphs is unset
|
||||
frontend.options.retainFullTypeGraphs = false;
|
||||
frontend.markDirty("game/A");
|
||||
frontend.check("game/A");
|
||||
|
||||
mod = frontend.moduleResolver.getModule("game/A");
|
||||
CHECK(mod->defArena.allocator.empty());
|
||||
CHECK(mod->keyArena.allocator.empty());
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include <limits.h>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||
LUAU_FASTFLAG(LuauCodeGenVectorDeadStoreElim)
|
||||
LUAU_FASTFLAG(LuauCodeGenArithOpt)
|
||||
|
||||
using namespace Luau::CodeGen;
|
||||
|
||||
|
@ -1725,8 +1723,6 @@ bb_fallback_1:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "NumericSimplifications")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenArithOpt{FFlag::LuauCodeGenArithOpt, true};
|
||||
|
||||
IrOp block = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(block);
|
||||
|
@ -4472,8 +4468,6 @@ bb_0:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "VectorOverNumber")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenVectorDeadStoreElim{FFlag::LuauCodeGenVectorDeadStoreElim, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
|
@ -4497,8 +4491,6 @@ bb_0:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "VectorOverVector")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenVectorDeadStoreElim{FFlag::LuauCodeGenVectorDeadStoreElim, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
|
@ -4522,8 +4514,6 @@ bb_0:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "NumberOverVector")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenVectorDeadStoreElim{FFlag::LuauCodeGenVectorDeadStoreElim, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
|
@ -4547,8 +4537,6 @@ bb_0:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "NumberOverNil")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenVectorDeadStoreElim{FFlag::LuauCodeGenVectorDeadStoreElim, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
|
@ -4571,8 +4559,6 @@ bb_0:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "VectorOverNil")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenVectorDeadStoreElim{FFlag::LuauCodeGenVectorDeadStoreElim, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
|
@ -4595,8 +4581,6 @@ bb_0:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "NumberOverCombinedVector")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenVectorDeadStoreElim{FFlag::LuauCodeGenVectorDeadStoreElim, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
|
@ -4622,8 +4606,6 @@ bb_0:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "VectorOverCombinedVector")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenVectorDeadStoreElim{FFlag::LuauCodeGenVectorDeadStoreElim, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
|
@ -4649,8 +4631,6 @@ bb_0:
|
|||
|
||||
TEST_CASE_FIXTURE(IrBuilderFixture, "VectorOverCombinedNumber")
|
||||
{
|
||||
ScopedFastFlag luauCodeGenVectorDeadStoreElim{FFlag::LuauCodeGenVectorDeadStoreElim, true};
|
||||
|
||||
IrOp entry = build.block(IrBlockKind::Internal);
|
||||
|
||||
build.beginBlock(entry);
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
LUAU_FASTFLAG(LuauCompileLibraryConstants)
|
||||
|
||||
static void luauLibraryConstantLookup(const char* library, const char* member, Luau::CompileConstant* constant)
|
||||
{
|
||||
// While 'vector' library constants are a Luau built-in, their constant value depends on the embedder LUA_VECTOR_SIZE value
|
||||
|
@ -2115,8 +2113,6 @@ bb_bytecode_1:
|
|||
|
||||
TEST_CASE("LibraryFieldTypesAndConstants")
|
||||
{
|
||||
ScopedFastFlag luauCompileLibraryConstants{FFlag::LuauCompileLibraryConstants, true};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + getCodegenAssembly(
|
||||
R"(
|
||||
|
@ -2153,8 +2149,6 @@ bb_bytecode_1:
|
|||
|
||||
TEST_CASE("LibraryFieldTypesAndConstants")
|
||||
{
|
||||
ScopedFastFlag luauCompileLibraryConstants{FFlag::LuauCompileLibraryConstants, true};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + getCodegenAssembly(
|
||||
R"(
|
||||
|
@ -2189,8 +2183,6 @@ bb_bytecode_1:
|
|||
|
||||
TEST_CASE("LibraryFieldTypesAndConstantsCApi")
|
||||
{
|
||||
ScopedFastFlag luauCompileLibraryConstants{FFlag::LuauCompileLibraryConstants, true};
|
||||
|
||||
CHECK_EQ(
|
||||
"\n" + getCodegenAssemblyUsingCApi(
|
||||
R"(
|
||||
|
|
|
@ -12,10 +12,14 @@
|
|||
#include "doctest.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if __APPLE__
|
||||
#include <TargetConditionals.h>
|
||||
|
@ -215,21 +219,43 @@ TEST_CASE("PathResolution")
|
|||
std::string prefix = "/";
|
||||
#endif
|
||||
|
||||
CHECK(resolvePath(prefix + "Users/modules/module.luau", "") == prefix + "Users/modules/module.luau");
|
||||
CHECK(resolvePath(prefix + "Users/modules/module.luau", "a/string/that/should/be/ignored") == prefix + "Users/modules/module.luau");
|
||||
CHECK(resolvePath(prefix + "Users/modules/module.luau", "./a/string/that/should/be/ignored") == prefix + "Users/modules/module.luau");
|
||||
CHECK(resolvePath(prefix + "Users/modules/module.luau", "/a/string/that/should/be/ignored") == prefix + "Users/modules/module.luau");
|
||||
CHECK(resolvePath(prefix + "Users/modules/module.luau", "/Users/modules") == prefix + "Users/modules/module.luau");
|
||||
// tuple format: {inputPath, inputBaseFilePath, expected}
|
||||
std::vector<std::tuple<std::string, std::string, std::string>> tests = {
|
||||
// 1. Basic path resolution
|
||||
// a. Relative to a relative path that begins with './'
|
||||
{"./dep", "./src/modules/module.luau", "./src/modules/dep"},
|
||||
{"../dep", "./src/modules/module.luau", "./src/dep"},
|
||||
{"../../dep", "./src/modules/module.luau", "./dep"},
|
||||
{"../../", "./src/modules/module.luau", "./"},
|
||||
|
||||
CHECK(resolvePath("../module", "") == "../module");
|
||||
CHECK(resolvePath("../../module", "") == "../../module");
|
||||
CHECK(resolvePath("../module/..", "") == "../");
|
||||
CHECK(resolvePath("../module/../..", "") == "../../");
|
||||
// b. Relative to a relative path that begins with '../'
|
||||
{"./dep", "../src/modules/module.luau", "../src/modules/dep"},
|
||||
{"../dep", "../src/modules/module.luau", "../src/dep"},
|
||||
{"../../dep", "../src/modules/module.luau", "../dep"},
|
||||
{"../../", "../src/modules/module.luau", "../"},
|
||||
|
||||
CHECK(resolvePath("../dependency", prefix + "Users/modules/module.luau") == prefix + "Users/dependency");
|
||||
CHECK(resolvePath("../dependency/", prefix + "Users/modules/module.luau") == prefix + "Users/dependency");
|
||||
CHECK(resolvePath("../../../../../Users/dependency", prefix + "Users/modules/module.luau") == prefix + "Users/dependency");
|
||||
CHECK(resolvePath("../..", prefix + "Users/modules/module.luau") == prefix);
|
||||
// c. Relative to an absolute path
|
||||
{"./dep", prefix + "src/modules/module.luau", prefix + "src/modules/dep"},
|
||||
{"../dep", prefix + "src/modules/module.luau", prefix + "src/dep"},
|
||||
{"../../dep", prefix + "src/modules/module.luau", prefix + "dep"},
|
||||
{"../../", prefix + "src/modules/module.luau", prefix},
|
||||
|
||||
|
||||
// 2. Check behavior for extraneous ".."
|
||||
// a. Relative paths retain '..' and append if needed
|
||||
{"../../../", "./src/modules/module.luau", "../"},
|
||||
{"../../../", "../src/modules/module.luau", "../../"},
|
||||
|
||||
// b. Absolute paths ignore '..' if already at root
|
||||
{"../../../", prefix + "src/modules/module.luau", prefix},
|
||||
};
|
||||
|
||||
for (const auto& [inputPath, inputBaseFilePath, expected] : tests)
|
||||
{
|
||||
std::optional<std::string> resolved = resolvePath(inputPath, inputBaseFilePath);
|
||||
CHECK(resolved);
|
||||
CHECK_EQ(resolved, expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("PathNormalization")
|
||||
|
@ -240,34 +266,57 @@ TEST_CASE("PathNormalization")
|
|||
std::string prefix = "/";
|
||||
#endif
|
||||
|
||||
// Relative path
|
||||
std::optional<std::string> result = normalizePath("../../modules/module");
|
||||
CHECK(result);
|
||||
std::string normalized = *result;
|
||||
std::vector<std::string> variants = {
|
||||
"./.././.././modules/./module/", "placeholder/../../../modules/module", "../placeholder/placeholder2/../../../modules/module"
|
||||
};
|
||||
for (const std::string& variant : variants)
|
||||
{
|
||||
result = normalizePath(variant);
|
||||
CHECK(result);
|
||||
CHECK(normalized == *result);
|
||||
}
|
||||
// pair format: {input, expected}
|
||||
std::vector<std::pair<std::string, std::string>> tests = {
|
||||
// 1. Basic formatting checks
|
||||
{"", "./"},
|
||||
{".", "./"},
|
||||
{"..", "../"},
|
||||
{"a/relative/path", "./a/relative/path"},
|
||||
|
||||
// Absolute path
|
||||
result = normalizePath(prefix + "Users/modules/module");
|
||||
CHECK(result);
|
||||
normalized = *result;
|
||||
variants = {
|
||||
"Users/Users/Users/.././.././modules/./module/",
|
||||
"placeholder/../Users/..//Users/modules/module",
|
||||
"Users/../placeholder/placeholder2/../../Users/modules/module"
|
||||
|
||||
// 2. Paths containing extraneous '.' and '/' symbols
|
||||
{"./remove/extraneous/symbols/", "./remove/extraneous/symbols"},
|
||||
{"./remove/extraneous//symbols", "./remove/extraneous/symbols"},
|
||||
{"./remove/extraneous/symbols/.", "./remove/extraneous/symbols"},
|
||||
{"./remove/extraneous/./symbols", "./remove/extraneous/symbols"},
|
||||
|
||||
{"../remove/extraneous/symbols/", "../remove/extraneous/symbols"},
|
||||
{"../remove/extraneous//symbols", "../remove/extraneous/symbols"},
|
||||
{"../remove/extraneous/symbols/.", "../remove/extraneous/symbols"},
|
||||
{"../remove/extraneous/./symbols", "../remove/extraneous/symbols"},
|
||||
|
||||
{prefix + "remove/extraneous/symbols/", prefix + "remove/extraneous/symbols"},
|
||||
{prefix + "remove/extraneous//symbols", prefix + "remove/extraneous/symbols"},
|
||||
{prefix + "remove/extraneous/symbols/.", prefix + "remove/extraneous/symbols"},
|
||||
{prefix + "remove/extraneous/./symbols", prefix + "remove/extraneous/symbols"},
|
||||
|
||||
|
||||
// 3. Paths containing '..'
|
||||
// a. '..' removes the erasable component before it
|
||||
{"./remove/me/..", "./remove"},
|
||||
{"./remove/me/../", "./remove"},
|
||||
|
||||
{"../remove/me/..", "../remove"},
|
||||
{"../remove/me/../", "../remove"},
|
||||
|
||||
{prefix + "remove/me/..", prefix + "remove"},
|
||||
{prefix + "remove/me/../", prefix + "remove"},
|
||||
|
||||
// b. '..' stays if path is relative and component is non-erasable
|
||||
{"./..", "../"},
|
||||
{"./../", "../"},
|
||||
|
||||
{"../..", "../../"},
|
||||
{"../../", "../../"},
|
||||
|
||||
// c. '..' disappears if path is absolute and component is non-erasable
|
||||
{prefix + "..", prefix},
|
||||
};
|
||||
for (const std::string& variant : variants)
|
||||
|
||||
for (const auto& [input, expected] : tests)
|
||||
{
|
||||
result = normalizePath(prefix + variant);
|
||||
CHECK(result);
|
||||
CHECK(normalized == *result);
|
||||
CHECK_EQ(normalizePath(input), expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -489,6 +538,25 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "AliasHasIllegalFormat")
|
|||
assertOutputContainsAll({"false", " is not a valid alias"});
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireFromLuauBinary")
|
||||
{
|
||||
char executable[] = "luau";
|
||||
std::vector<std::string> paths = {
|
||||
getLuauDirectory(PathType::Relative) + "/tests/require/without_config/dependency.luau",
|
||||
getLuauDirectory(PathType::Absolute) + "/tests/require/without_config/dependency.luau"
|
||||
};
|
||||
|
||||
for (const std::string& path : paths)
|
||||
{
|
||||
std::vector<char> pathStr(path.size() + 1);
|
||||
strncpy(pathStr.data(), path.c_str(), path.size());
|
||||
pathStr[path.size()] = '\0';
|
||||
|
||||
char* argv[2] = {executable, pathStr.data()};
|
||||
CHECK_EQ(replMain(2, argv), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ParseAliases")
|
||||
{
|
||||
std::string configJson = R"({
|
||||
|
|
|
@ -10,12 +10,9 @@ using namespace Luau;
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunFixNoReadWrite)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunFixInner)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunPrintToError)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunExportedAndLocal)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunGenerics)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunCloneTail)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
|
||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||
|
||||
|
@ -226,7 +223,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_methods_work")
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "thread_and_buffer_types")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunThreadBuffer{FFlag::LuauUserTypeFunThreadBuffer, true};
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
type function work_with_thread(x)
|
||||
|
@ -931,7 +927,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other")
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_2")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunUpdateAllEnvs{FFlag::LuauUserTypeFunUpdateAllEnvs, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function first(arg)
|
||||
|
@ -955,8 +950,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_2")
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_3")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
|
||||
ScopedFastFlag luauUserTypeFunUpdateAllEnvs{FFlag::LuauUserTypeFunUpdateAllEnvs, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
-- this function should not see 'fourth' function when invoked from 'third' that sees it
|
||||
|
@ -1281,7 +1274,6 @@ local a: foo<> = "a"
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "implicit_export")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
|
||||
|
||||
fileResolver.source["game/A"] = R"(
|
||||
type function concat(a, b)
|
||||
|
@ -1309,7 +1301,6 @@ local b: Test.Concat<'third', 'fourth'>
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "local_scope")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function foo()
|
||||
|
@ -1332,7 +1323,6 @@ local a = test()
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "explicit_export")
|
||||
{
|
||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
|
||||
|
||||
fileResolver.source["game/A"] = R"(
|
||||
export type function concat(a, b)
|
||||
|
@ -1359,7 +1349,6 @@ local b: Test.concat<'third', 'fourth'>
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "print_to_error")
|
||||
{
|
||||
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunPrintToError{FFlag::LuauUserTypeFunPrintToError, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function t0(a)
|
||||
|
@ -1378,7 +1367,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "print_to_error")
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "print_to_error_plus_error")
|
||||
{
|
||||
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunPrintToError{FFlag::LuauUserTypeFunPrintToError, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function t0(a)
|
||||
|
@ -1399,7 +1387,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "print_to_error_plus_error")
|
|||
TEST_CASE_FIXTURE(BuiltinsFixture, "print_to_error_plus_no_result")
|
||||
{
|
||||
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
|
||||
ScopedFastFlag luauUserTypeFunPrintToError{FFlag::LuauUserTypeFunPrintToError, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type function t0(a)
|
||||
|
@ -1891,4 +1878,22 @@ local function ok(idx: pass<test>): (number, ...string) -> (string, ...number) r
|
|||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_eqsat_opaque")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauUserTypeFunGenerics, true}, {FFlag::DebugLuauEqSatSimplification, true}};
|
||||
|
||||
CheckResult _ = check(R"(
|
||||
type function t0(a)
|
||||
error("test")
|
||||
end
|
||||
local v: t0<string & number>
|
||||
)");
|
||||
TypeArena arena;
|
||||
auto ty = requireType("v");
|
||||
auto simplifier = EqSatSimplification::newSimplifier(NotNull{&arena}, frontend.builtinTypes);
|
||||
auto simplified = eqSatSimplify(NotNull{simplifier.get()}, ty);
|
||||
REQUIRE(simplified);
|
||||
CHECK_EQ("t0<number & string>", toString(simplified->result)); // NOLINT(bugprone-unchecked-optional-access)
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -12,7 +12,7 @@ using namespace Luau;
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauTypestateBuiltins2)
|
||||
LUAU_FASTFLAG(LuauStringFormatArityFix)
|
||||
LUAU_FASTFLAG(LuauTableCloneClonesType)
|
||||
LUAU_FASTFLAG(LuauTableCloneClonesType2)
|
||||
LUAU_FASTFLAG(LuauStringFormatErrorSuppression)
|
||||
|
||||
TEST_SUITE_BEGIN("BuiltinTests");
|
||||
|
@ -1178,8 +1178,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_does_not_retroactively_block_mu
|
|||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
|
||||
if (FFlag::LuauTypestateBuiltins2)
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins2)
|
||||
{
|
||||
CHECK_EQ("{ a: number, q: string } | { read a: number, read q: string }", toString(requireType("t1"), {/*exhaustive */ true}));
|
||||
// before the assignment, it's `t1`
|
||||
|
@ -1600,7 +1599,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_dot_clone_type_states")
|
|||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauTableCloneClonesType)
|
||||
if (FFlag::LuauTableCloneClonesType2)
|
||||
{
|
||||
CHECK_EQ(toString(requireType("t1"), {true}), "{ x: number, z: number }");
|
||||
CHECK_EQ(toString(requireType("t2"), {true}), "{ x: number, y: number }");
|
||||
|
|
|
@ -22,6 +22,8 @@ LUAU_FASTFLAG(LuauRetrySubtypingWithoutHiddenPack)
|
|||
LUAU_FASTFLAG(LuauTableKeysAreRValues)
|
||||
LUAU_FASTFLAG(LuauAllowNilAssignmentToIndexer)
|
||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTablesOnScope)
|
||||
LUAU_FASTFLAG(LuauDontInPlaceMutateTableType)
|
||||
|
||||
TEST_SUITE_BEGIN("TableTests");
|
||||
|
||||
|
@ -3816,7 +3818,10 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compati
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauTrackInteriorFreeTypesOnScope, true};
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauTrackInteriorFreeTypesOnScope, true},
|
||||
{FFlag::LuauTrackInteriorFreeTablesOnScope, true},
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(s): string
|
||||
|
@ -3832,7 +3837,7 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_
|
|||
CHECK(toString(result.errors[0]) == "Parameter 's' has been reduced to never. This function is not callable with any possible value.");
|
||||
CHECK(
|
||||
toString(result.errors[1]) ==
|
||||
"Parameter 's' is required to be a subtype of '{- read absolutely_no_scalar_has_this_method: (never) -> (unknown, ...unknown) -}' here."
|
||||
"Parameter 's' is required to be a subtype of '{ read absolutely_no_scalar_has_this_method: (never) -> (unknown, ...unknown) }' here."
|
||||
);
|
||||
CHECK(toString(result.errors[2]) == "Parameter 's' is required to be a subtype of 'string' here.");
|
||||
CHECK_EQ("(never) -> string", toString(requireType("f")));
|
||||
|
@ -5067,4 +5072,33 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "read_only_property_reads")
|
|||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "multiple_fields_in_literal")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauDontInPlaceMutateTableType, true},
|
||||
};
|
||||
|
||||
auto result = check(R"(
|
||||
type Foo = {
|
||||
[string]: {
|
||||
Min: number,
|
||||
Max: number
|
||||
}
|
||||
}
|
||||
local Foos: Foo = {
|
||||
["Foo"] = {
|
||||
Min = -1,
|
||||
Max = 1
|
||||
},
|
||||
["Foo"] = {
|
||||
Min = -1,
|
||||
Max = 1
|
||||
}
|
||||
}
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -23,7 +23,6 @@ LUAU_FASTINT(LuauCheckRecursionLimit);
|
|||
LUAU_FASTINT(LuauNormalizeCacheLimit);
|
||||
LUAU_FASTINT(LuauRecursionLimit);
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||
LUAU_FASTFLAG(LuauNewSolverVisitErrorExprLvalues)
|
||||
LUAU_FASTFLAG(InferGlobalTypes)
|
||||
|
||||
using namespace Luau;
|
||||
|
@ -1197,13 +1196,23 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer")
|
|||
validateErrors(result.errors);
|
||||
REQUIRE_MESSAGE(!result.errors.empty(), getErrors(result));
|
||||
|
||||
CHECK(1 == result.errors.size());
|
||||
|
||||
if (FFlag::LuauSolverV2)
|
||||
CHECK(Location{{3, 22}, {3, 42}} == result.errors[0].location);
|
||||
{
|
||||
CHECK(3 == result.errors.size());
|
||||
CHECK(Location{{2, 22}, {2, 41}} == result.errors[0].location);
|
||||
CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location);
|
||||
CHECK(Location{{3, 23}, {3, 40}} == result.errors[2].location);
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[1]));
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[2]));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(1 == result.errors.size());
|
||||
|
||||
CHECK(Location{{3, 12}, {3, 46}} == result.errors[0].location);
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_infer_cache_limit_normalizer")
|
||||
|
@ -1708,7 +1717,7 @@ TEST_CASE_FIXTURE(Fixture, "react_lua_follow_free_type_ub")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "visit_error_nodes_in_lvalue")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauNewSolverVisitErrorExprLvalues, true}};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
// This should always fail to parse, but shouldn't assert. Previously this
|
||||
// would assert as we end up _roughly_ parsing this (with a lot of error
|
||||
|
|
Loading…
Add table
Reference in a new issue