Sync to upstream/release/650 (#1502)
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:macos version:macos-latest]) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:macos-arm version:macos-14]) (push) Has been cancelled
build / ${{matrix.os.name}} (map[name:ubuntu version:ubuntu-latest]) (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:macos version:macos-latest]) (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:ubuntu version:ubuntu-20.04]) (push) Has been cancelled
release / ${{matrix.os.name}} (map[name:windows version:windows-latest]) (push) Has been cancelled
release / web (push) Has been cancelled

* New `vector` library! See https://rfcs.luau.org/vector-library.html
for details
* Replace the use of non-portable `strnlen` with `memchr`. `strnlen` is
not part of any C or C++ standard.
* Introduce `lua_newuserdatataggedwithmetatable` for faster tagged
userdata creation of userdata with metatables registered with
`lua_setuserdatametatable`

Old Solver

* It used to be the case that a module's result type would
unconditionally be inferred to be `any` if it imported any module that
participates in any import cycle. This is now fixed.

New Solver

* Improve inference of `table.freeze`: We now infer read-only properties
on tables after they have been frozen.
* We now correctly flag cases where `string.format` is called with 0
arguments.
* Fix a bug in user-defined type functions where table properties could
be lost if the table had a metatable
* Reset the random number seed for each evaluation of a type function
* We now retry subtyping arguments if it failed due to hidden variadics.

---------

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
This commit is contained in:
Andy Friesen 2024-11-01 12:06:07 -07:00 committed by GitHub
parent db809395bf
commit a251bc68a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
86 changed files with 2216 additions and 406 deletions

View File

@ -265,4 +265,6 @@ void registerTypeUserData(lua_State* L);
void setTypeFunctionEnvironment(lua_State* L);
void resetTypeFunctionState(lua_State* L);
} // namespace Luau

View File

@ -38,7 +38,7 @@
#include <stdio.h>
LUAU_FASTFLAGVARIABLE(StudioReportLuauAny2, false);
LUAU_FASTFLAGVARIABLE(StudioReportLuauAny2);
LUAU_FASTINTVARIABLE(LuauAnySummaryRecursionLimit, 300);
LUAU_FASTFLAG(DebugLuauMagicTypes);

View File

@ -13,7 +13,7 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauDocumentationAtPosition, false)
LUAU_FASTFLAGVARIABLE(LuauDocumentationAtPosition)
namespace Luau
{

View File

@ -16,7 +16,7 @@
#include <utility>
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(AutocompleteRequirePathSuggestions, false)
LUAU_FASTFLAGVARIABLE(AutocompleteRequirePathSuggestions)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTINT(LuauTypeInferIterationLimit)

View File

@ -3,6 +3,7 @@
#include "Luau/Ast.h"
#include "Luau/Clone.h"
#include "Luau/DenseHash.h"
#include "Luau/Error.h"
#include "Luau/Frontend.h"
#include "Luau/Symbol.h"
@ -29,8 +30,8 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins, false)
LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix, false)
LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins2)
LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix)
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions)
@ -421,7 +422,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
attachMagicFunction(ttv->props["pack"].type(), magicFunctionPack);
attachDcrMagicFunction(ttv->props["pack"].type(), dcrMagicFunctionPack);
if (FFlag::LuauTypestateBuiltins)
if (FFlag::LuauTypestateBuiltins2)
attachDcrMagicFunction(ttv->props["freeze"].type(), dcrMagicFunctionFreeze);
}
@ -1338,54 +1339,86 @@ static bool dcrMagicFunctionPack(MagicFunctionCallContext context)
return true;
}
static std::optional<TypeId> freezeTable(TypeId inputType, MagicFunctionCallContext& context)
{
TypeArena* arena = context.solver->arena;
if (auto mt = get<MetatableType>(inputType))
{
std::optional<TypeId> frozenTable = freezeTable(mt->table, context);
if (!frozenTable)
return std::nullopt;
TypeId resultType = arena->addType(MetatableType{*frozenTable, mt->metatable, mt->syntheticName});
return resultType;
}
if (get<TableType>(inputType))
{
// Clone the input type, this will become our final result type after we mutate it.
CloneState cloneState{context.solver->builtinTypes};
TypeId resultType = shallowClone(inputType, *arena, cloneState);
auto tableTy = getMutable<TableType>(resultType);
// `clone` should not break this.
LUAU_ASSERT(tableTy);
tableTy->state = TableState::Sealed;
// We'll mutate the table to make every property type read-only.
for (auto iter = tableTy->props.begin(); iter != tableTy->props.end();)
{
if (iter->second.isWriteOnly())
iter = tableTy->props.erase(iter);
else
{
iter->second.writeTy = std::nullopt;
iter++;
}
}
return resultType;
}
context.solver->reportError(TypeMismatch{context.solver->builtinTypes->tableType, inputType}, context.callSite->argLocation);
return std::nullopt;
}
static bool dcrMagicFunctionFreeze(MagicFunctionCallContext context)
{
LUAU_ASSERT(FFlag::LuauTypestateBuiltins);
LUAU_ASSERT(FFlag::LuauTypestateBuiltins2);
TypeArena* arena = context.solver->arena;
const DataFlowGraph* dfg = context.solver->dfg.get();
Scope* scope = context.constraint->scope.get();
const auto& [paramTypes, paramTail] = extendTypePack(*arena, context.solver->builtinTypes, context.arguments, 1);
LUAU_ASSERT(paramTypes.size() >= 1);
TypeId inputType = follow(paramTypes.at(0));
// we'll check if it's a table first since this magic function also produces the error if it's not until we have bounded generics
if (!get<TableType>(inputType))
if (paramTypes.empty() || context.callSite->args.size == 0)
{
context.solver->reportError(TypeMismatch{context.solver->builtinTypes->tableType, inputType}, context.callSite->argLocation);
context.solver->reportError(CountMismatch{1, std::nullopt, 0}, context.callSite->argLocation);
return false;
}
TypeId inputType = follow(paramTypes[0]);
AstExpr* targetExpr = context.callSite->args.data[0];
std::optional<DefId> resultDef = dfg->getDefOptional(targetExpr);
std::optional<TypeId> resultTy = resultDef ? scope->lookup(*resultDef) : std::nullopt;
// Clone the input type, this will become our final result type after we mutate it.
CloneState cloneState{context.solver->builtinTypes};
TypeId clonedType = shallowClone(inputType, *arena, cloneState);
auto tableTy = getMutable<TableType>(clonedType);
// `clone` should not break this.
LUAU_ASSERT(tableTy);
tableTy->state = TableState::Sealed;
tableTy->syntheticName = std::nullopt;
std::optional<TypeId> frozenType = freezeTable(inputType, context);
// We'll mutate the table to make every property type read-only.
for (auto iter = tableTy->props.begin(); iter != tableTy->props.end();)
if (!frozenType)
{
if (iter->second.isWriteOnly())
iter = tableTy->props.erase(iter);
else
{
iter->second.writeTy = std::nullopt;
iter++;
}
if (resultTy)
asMutable(*resultTy)->ty.emplace<BoundType>(context.solver->builtinTypes->errorType);
asMutable(context.result)->ty.emplace<BoundTypePack>(context.solver->builtinTypes->errorTypePack);
return true;
}
if (resultTy)
asMutable(*resultTy)->ty.emplace<BoundType>(clonedType);
asMutable(context.result)->ty.emplace<BoundTypePack>(arena->addTypePack({clonedType}));
asMutable(*resultTy)->ty.emplace<BoundType>(*frozenType);
asMutable(context.result)->ty.emplace<BoundTypePack>(arena->addTypePack({*frozenType}));
return true;
}

View File

@ -31,9 +31,9 @@ LUAU_FASTINT(LuauCheckRecursionLimit)
LUAU_FASTFLAG(DebugLuauLogSolverToJson)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAG(LuauTypestateBuiltins)
LUAU_FASTFLAG(LuauTypestateBuiltins2)
LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues, false)
LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues)
namespace Luau
{
@ -1078,7 +1078,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocal* stat
addConstraint(scope, value->location, NameConstraint{*firstValueType, var->name.value, /*synthetic*/ true});
else if (const AstExprCall* call = value->as<AstExprCall>())
{
if (FFlag::LuauTypestateBuiltins)
if (FFlag::LuauTypestateBuiltins2)
{
if (matchSetMetatable(*call))
addConstraint(scope, value->location, NameConstraint{*firstValueType, var->name.value, /*synthetic*/ true});
@ -2062,7 +2062,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
return InferencePack{arena->addTypePack({resultTy}), {refinementArena.variadic(returnRefinements)}};
}
if (FFlag::LuauTypestateBuiltins && shouldTypestateForFirstArgument(*call) && call->args.size > 0 && isLValue(call->args.data[0]))
if (FFlag::LuauTypestateBuiltins2 && shouldTypestateForFirstArgument(*call) && call->args.size > 0 && isLValue(call->args.data[0]))
{
AstExpr* targetExpr = call->args.data[0];
auto resultTy = arena->addType(BlockedType{});

View File

@ -27,12 +27,12 @@
#include <algorithm>
#include <utility>
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAGVARIABLE(LuauRemoveNotAnyHack, false)
LUAU_FASTFLAGVARIABLE(LuauRemoveNotAnyHack)
namespace Luau
{

View File

@ -13,7 +13,7 @@
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTypestateBuiltins)
LUAU_FASTFLAG(LuauTypestateBuiltins2)
namespace Luau
{
@ -939,7 +939,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
{
visitExpr(c->func);
if (FFlag::LuauTypestateBuiltins && shouldTypestateForFirstArgument(*c) && c->args.size > 1 && isLValue(*c->args.begin()))
if (FFlag::LuauTypestateBuiltins2 && shouldTypestateForFirstArgument(*c) && c->args.size > 1 && isLValue(*c->args.begin()))
{
AstExpr* firstArg = *c->args.begin();

View File

@ -3,6 +3,8 @@
LUAU_FASTFLAG(LuauMathMap)
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitions)
namespace Luau
{
@ -450,9 +452,39 @@ declare buffer: {
)BUILTIN_SRC";
static const std::string kBuiltinDefinitionVectorSrc = 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";
std::string getBuiltinDefinitionSource()
{
std::string result = FFlag::LuauMathMap ? kBuiltinDefinitionLuaSrcChecked : kBuiltinDefinitionLuaSrcChecked_DEPRECATED;
if (FFlag::LuauVectorDefinitions)
result += kBuiltinDefinitionVectorSrc;
return result;
}

View File

@ -36,20 +36,20 @@ LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(LuauInferInNoCheckMode)
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
LUAU_FASTFLAGVARIABLE(LuauStoreCommentsForDefinitionFiles, false)
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
LUAU_FASTFLAGVARIABLE(LuauStoreCommentsForDefinitionFiles)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode, false)
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode, false)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionNoEvaluation, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionNoEvaluation)
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
LUAU_FASTFLAGVARIABLE(LuauMoreThoroughCycleDetection, false)
LUAU_FASTFLAGVARIABLE(LuauMoreThoroughCycleDetection)
LUAU_FASTFLAG(StudioReportLuauAny2)
LUAU_FASTFLAGVARIABLE(LuauStoreDFGOnModule2, false)
LUAU_FASTFLAGVARIABLE(LuauStoreDFGOnModule2)
namespace Luau
{

View File

@ -18,7 +18,7 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAttribute)
LUAU_FASTFLAG(LuauNativeAttribute)
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute, false)
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
namespace Luau
{

View File

@ -19,6 +19,8 @@
#include <iostream>
#include <iterator>
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunNonstrict)
namespace Luau
{
@ -421,7 +423,9 @@ struct NonStrictTypeChecker
NonStrictContext visit(AstStatTypeFunction* typeFunc)
{
reportError(GenericError{"This syntax is not supported"}, typeFunc->location);
if (!FFlag::LuauUserTypeFunNonstrict)
reportError(GenericError{"This syntax is not supported"}, typeFunc->location);
return {};
}

View File

@ -15,14 +15,14 @@
#include "Luau/TypeFwd.h"
#include "Luau/Unifier.h"
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant, false)
LUAU_FASTFLAGVARIABLE(DebugLuauCheckNormalizeInvariant)
LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000);
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200)
LUAU_FASTFLAGVARIABLE(LuauNormalizationTracksCyclicPairsThroughInhabitance, false);
LUAU_FASTFLAGVARIABLE(LuauIntersectNormalsNeedsToTrackResourceLimits, false);
LUAU_FASTFLAGVARIABLE(LuauNormalizationTracksCyclicPairsThroughInhabitance);
LUAU_FASTFLAGVARIABLE(LuauIntersectNormalsNeedsToTrackResourceLimits);
namespace Luau
{

View File

@ -15,7 +15,7 @@
LUAU_FASTINT(LuauTypeReductionRecursionLimit)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8);
LUAU_FASTFLAGVARIABLE(LuauFlagBasicIntersectFollows, false);
LUAU_FASTFLAGVARIABLE(LuauFlagBasicIntersectFollows);
namespace Luau
{

View File

@ -21,8 +21,9 @@
#include <algorithm>
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity, false);
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAGVARIABLE(LuauRetrySubtypingWithoutHiddenPack)
namespace Luau
{
@ -1477,6 +1478,20 @@ SubtypingResult Subtyping::isCovariantWith(
result.orElse(
isContravariantWith(env, subFunction->argTypes, superFunction->argTypes, scope).withBothComponent(TypePath::PackField::Arguments)
);
// If subtyping failed in the argument packs, we should check if there's a hidden variadic tail and try ignoring it.
// This might cause subtyping correctly because the sub type here may not have a hidden variadic tail or equivalent.
if (FFlag::LuauRetrySubtypingWithoutHiddenPack && !result.isSubtype)
{
auto [arguments, tail] = flatten(superFunction->argTypes);
if (auto variadic = get<VariadicTypePack>(tail); variadic && variadic->hidden)
{
result.orElse(
isContravariantWith(env, subFunction->argTypes, arena->addTypePack(TypePack{arguments}), scope).withBothComponent(TypePath::PackField::Arguments)
);
}
}
}
result.andAlso(isCovariantWith(env, subFunction->retTypes, superFunction->retTypes, scope).withBothComponent(TypePath::PackField::Returns));

View File

@ -38,7 +38,7 @@ LUAU_FASTFLAG(LuauSolverV2)
* 3: Suffix free/generic types with their scope pointer, if present.
*/
LUAU_FASTINTVARIABLE(DebugLuauVerboseTypeNames, 0)
LUAU_FASTFLAGVARIABLE(DebugLuauToStringNoLexicalSort, false)
LUAU_FASTFLAGVARIABLE(DebugLuauToStringNoLexicalSort)
namespace Luau
{

View File

@ -2,7 +2,7 @@
#include "Luau/TypeArena.h"
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false);
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena);
namespace Luau
{

View File

@ -45,11 +45,12 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
// when this value is set to a negative value, guessing will be totally disabled.
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies, false)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions2, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
LUAU_FASTFLAG(LuauUserTypeFunFixRegister)
LUAU_FASTFLAG(LuauRemoveNotAnyHack)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionResetState)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
@ -645,6 +646,9 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
lua_getglobal(global, name.value);
lua_xmove(global, L, 1);
if (FFlag::LuauUserDefinedTypeFunctionResetState)
resetTypeFunctionState(L);
// Push serialized arguments onto the stack
// Since there aren't any new class types being created in type functions, there isn't a deserialization function

View File

@ -15,8 +15,8 @@
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixRegister, false)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixNoReadWrite, false)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixRegister)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixNoReadWrite)
namespace Luau
{
@ -1647,6 +1647,15 @@ void setTypeFunctionEnvironment(lua_State* L)
}
}
void resetTypeFunctionState(lua_State* L)
{
lua_getglobal(L, "math");
lua_getfield(L, -1, "randomseed");
lua_pushnumber(L, 0);
lua_call(L, 1, 0);
lua_pop(L, 1);
}
/*
* Below are helper methods for __eq
* Same as one from Type.cpp

View File

@ -20,6 +20,8 @@
// currently, controls serialization, deserialization, and `type.copy`
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixMetatable)
namespace Luau
{
@ -241,31 +243,31 @@ private:
return target;
}
void serializeChildren(TypeId ty, TypeFunctionTypeId tfti)
void serializeChildren(const TypeId ty, TypeFunctionTypeId tfti)
{
if (auto [p1, p2] = std::tuple{getMutable<PrimitiveType>(ty), getMutable<TypeFunctionPrimitiveType>(tfti)}; p1 && p2)
if (auto [p1, p2] = std::tuple{get<PrimitiveType>(ty), getMutable<TypeFunctionPrimitiveType>(tfti)}; p1 && p2)
serializeChildren(p1, p2);
else if (auto [u1, u2] = std::tuple{getMutable<UnknownType>(ty), getMutable<TypeFunctionUnknownType>(tfti)}; u1 && u2)
else if (auto [u1, u2] = std::tuple{get<UnknownType>(ty), getMutable<TypeFunctionUnknownType>(tfti)}; u1 && u2)
serializeChildren(u1, u2);
else if (auto [n1, n2] = std::tuple{getMutable<NeverType>(ty), getMutable<TypeFunctionNeverType>(tfti)}; n1 && n2)
else if (auto [n1, n2] = std::tuple{get<NeverType>(ty), getMutable<TypeFunctionNeverType>(tfti)}; n1 && n2)
serializeChildren(n1, n2);
else if (auto [a1, a2] = std::tuple{getMutable<AnyType>(ty), getMutable<TypeFunctionAnyType>(tfti)}; a1 && a2)
else if (auto [a1, a2] = std::tuple{get<AnyType>(ty), getMutable<TypeFunctionAnyType>(tfti)}; a1 && a2)
serializeChildren(a1, a2);
else if (auto [s1, s2] = std::tuple{getMutable<SingletonType>(ty), getMutable<TypeFunctionSingletonType>(tfti)}; s1 && s2)
else if (auto [s1, s2] = std::tuple{get<SingletonType>(ty), getMutable<TypeFunctionSingletonType>(tfti)}; s1 && s2)
serializeChildren(s1, s2);
else if (auto [u1, u2] = std::tuple{getMutable<UnionType>(ty), getMutable<TypeFunctionUnionType>(tfti)}; u1 && u2)
else if (auto [u1, u2] = std::tuple{get<UnionType>(ty), getMutable<TypeFunctionUnionType>(tfti)}; u1 && u2)
serializeChildren(u1, u2);
else if (auto [i1, i2] = std::tuple{getMutable<IntersectionType>(ty), getMutable<TypeFunctionIntersectionType>(tfti)}; i1 && i2)
else if (auto [i1, i2] = std::tuple{get<IntersectionType>(ty), getMutable<TypeFunctionIntersectionType>(tfti)}; i1 && i2)
serializeChildren(i1, i2);
else if (auto [n1, n2] = std::tuple{getMutable<NegationType>(ty), getMutable<TypeFunctionNegationType>(tfti)}; n1 && n2)
else if (auto [n1, n2] = std::tuple{get<NegationType>(ty), getMutable<TypeFunctionNegationType>(tfti)}; n1 && n2)
serializeChildren(n1, n2);
else if (auto [t1, t2] = std::tuple{getMutable<TableType>(ty), getMutable<TypeFunctionTableType>(tfti)}; t1 && t2)
else if (auto [t1, t2] = std::tuple{get<TableType>(ty), getMutable<TypeFunctionTableType>(tfti)}; t1 && t2)
serializeChildren(t1, t2);
else if (auto [m1, m2] = std::tuple{getMutable<MetatableType>(ty), getMutable<TypeFunctionTableType>(tfti)}; m1 && m2)
else if (auto [m1, m2] = std::tuple{get<MetatableType>(ty), getMutable<TypeFunctionTableType>(tfti)}; m1 && m2)
serializeChildren(m1, m2);
else if (auto [f1, f2] = std::tuple{getMutable<FunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
else if (auto [f1, f2] = std::tuple{get<FunctionType>(ty), getMutable<TypeFunctionFunctionType>(tfti)}; f1 && f2)
serializeChildren(f1, f2);
else if (auto [c1, c2] = std::tuple{getMutable<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
else if (auto [c1, c2] = std::tuple{get<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
serializeChildren(c1, c2);
else
{ // Either this or ty and tfti do not represent the same type
@ -274,12 +276,11 @@ private:
}
}
void serializeChildren(TypePackId tp, TypeFunctionTypePackId tftp)
void serializeChildren(const TypePackId tp, TypeFunctionTypePackId tftp)
{
if (auto [tPack1, tPack2] = std::tuple{getMutable<TypePack>(tp), getMutable<TypeFunctionTypePack>(tftp)}; tPack1 && tPack2)
if (auto [tPack1, tPack2] = std::tuple{get<TypePack>(tp), getMutable<TypeFunctionTypePack>(tftp)}; tPack1 && tPack2)
serializeChildren(tPack1, tPack2);
else if (auto [vPack1, vPack2] = std::tuple{getMutable<VariadicTypePack>(tp), getMutable<TypeFunctionVariadicTypePack>(tftp)};
vPack1 && vPack2)
else if (auto [vPack1, vPack2] = std::tuple{get<VariadicTypePack>(tp), getMutable<TypeFunctionVariadicTypePack>(tftp)}; vPack1 && vPack2)
serializeChildren(vPack1, vPack2);
else
{ // Either this or ty and tfti do not represent the same type
@ -298,49 +299,49 @@ private:
state->ctx->ice->ice("Serializing user defined type function arguments: kind and tfkind do not represent the same type");
}
void serializeChildren(PrimitiveType* p1, TypeFunctionPrimitiveType* p2)
void serializeChildren(const PrimitiveType* p1, TypeFunctionPrimitiveType* p2)
{
// noop.
}
void serializeChildren(UnknownType* u1, TypeFunctionUnknownType* u2)
void serializeChildren(const UnknownType* u1, TypeFunctionUnknownType* u2)
{
// noop.
}
void serializeChildren(NeverType* n1, TypeFunctionNeverType* n2)
void serializeChildren(const NeverType* n1, TypeFunctionNeverType* n2)
{
// noop.
}
void serializeChildren(AnyType* a1, TypeFunctionAnyType* a2)
void serializeChildren(const AnyType* a1, TypeFunctionAnyType* a2)
{
// noop.
}
void serializeChildren(SingletonType* s1, TypeFunctionSingletonType* s2)
void serializeChildren(const SingletonType* s1, TypeFunctionSingletonType* s2)
{
// noop.
}
void serializeChildren(UnionType* u1, TypeFunctionUnionType* u2)
void serializeChildren(const UnionType* u1, TypeFunctionUnionType* u2)
{
for (TypeId& ty : u1->options)
for (const TypeId& ty : u1->options)
u2->components.push_back(shallowSerialize(ty));
}
void serializeChildren(IntersectionType* i1, TypeFunctionIntersectionType* i2)
void serializeChildren(const IntersectionType* i1, TypeFunctionIntersectionType* i2)
{
for (TypeId& ty : i1->parts)
for (const TypeId& ty : i1->parts)
i2->components.push_back(shallowSerialize(ty));
}
void serializeChildren(NegationType* n1, TypeFunctionNegationType* n2)
void serializeChildren(const NegationType* n1, TypeFunctionNegationType* n2)
{
n2->type = shallowSerialize(n1->ty);
}
void serializeChildren(TableType* t1, TypeFunctionTableType* t2)
void serializeChildren(const TableType* t1, TypeFunctionTableType* t2)
{
for (const auto& [k, p] : t1->props)
{
@ -359,25 +360,34 @@ private:
t2->indexer = TypeFunctionTableIndexer(shallowSerialize(t1->indexer->indexType), shallowSerialize(t1->indexer->indexResultType));
}
void serializeChildren(MetatableType* m1, TypeFunctionTableType* m2)
void serializeChildren(const MetatableType* m1, TypeFunctionTableType* m2)
{
auto tmpTable = get<TypeFunctionTableType>(shallowSerialize(m1->table));
if (!tmpTable)
state->ctx->ice->ice("Serializing user defined type function arguments: metatable's table is not a TableType");
if (FFlag::LuauUserTypeFunFixMetatable)
{
// Serialize main part of the metatable immediately
if (auto tableTy = get<TableType>(m1->table))
serializeChildren(tableTy, m2);
}
else
{
auto tmpTable = get<TypeFunctionTableType>(shallowSerialize(m1->table));
if (!tmpTable)
state->ctx->ice->ice("Serializing user defined type function arguments: metatable's table is not a TableType");
m2->props = tmpTable->props;
m2->indexer = tmpTable->indexer;
m2->props = tmpTable->props;
m2->indexer = tmpTable->indexer;
}
m2->metatable = shallowSerialize(m1->metatable);
}
void serializeChildren(FunctionType* f1, TypeFunctionFunctionType* f2)
void serializeChildren(const FunctionType* f1, TypeFunctionFunctionType* f2)
{
f2->argTypes = shallowSerialize(f1->argTypes);
f2->retTypes = shallowSerialize(f1->retTypes);
}
void serializeChildren(ClassType* c1, TypeFunctionClassType* c2)
void serializeChildren(const ClassType* c1, TypeFunctionClassType* c2)
{
for (const auto& [k, p] : c1->props)
{
@ -402,16 +412,16 @@ private:
c2->parent = shallowSerialize(*c1->parent);
}
void serializeChildren(TypePack* t1, TypeFunctionTypePack* t2)
void serializeChildren(const TypePack* t1, TypeFunctionTypePack* t2)
{
for (TypeId& ty : t1->head)
for (const TypeId& ty : t1->head)
t2->head.push_back(shallowSerialize(ty));
if (t1->tail.has_value())
t2->tail = shallowSerialize(*t1->tail);
}
void serializeChildren(VariadicTypePack* v1, TypeFunctionVariadicTypePack* v2)
void serializeChildren(const VariadicTypePack* v1, TypeFunctionVariadicTypePack* v2)
{
v2->type = shallowSerialize(v1->ty);
}

View File

@ -23,16 +23,18 @@
#include <algorithm>
#include <iterator>
LUAU_FASTFLAGVARIABLE(DebugLuauMagicTypes, false)
LUAU_FASTFLAGVARIABLE(DebugLuauMagicTypes)
LUAU_FASTINTVARIABLE(LuauTypeInferRecursionLimit, 165)
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 20000)
LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000)
LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 300)
LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAGVARIABLE(LuauAcceptIndexingTableUnionsIntersections, false)
LUAU_FASTFLAGVARIABLE(LuauAcceptIndexingTableUnionsIntersections)
LUAU_FASTFLAGVARIABLE(LuauMetatableFollow)
LUAU_FASTFLAGVARIABLE(LuauRequireCyclesDontAlwaysReturnAny)
namespace Luau
{
@ -263,10 +265,17 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo
ScopePtr parentScope = environmentScope.value_or(globalScope);
ScopePtr moduleScope = std::make_shared<Scope>(parentScope);
if (module.cyclic)
moduleScope->returnType = addTypePack(TypePack{{anyType}, std::nullopt});
else
if (FFlag::LuauRequireCyclesDontAlwaysReturnAny)
{
moduleScope->returnType = freshTypePack(moduleScope);
}
else
{
if (module.cyclic)
moduleScope->returnType = addTypePack(TypePack{{anyType}, std::nullopt});
else
moduleScope->returnType = freshTypePack(moduleScope);
}
moduleScope->varargPack = anyTypePack;
@ -2870,7 +2879,7 @@ TypeId TypeChecker::checkRelationalOperation(
std::optional<TypeId> metamethod = findMetatableEntry(lhsType, metamethodName, expr.location, /* addErrors= */ true);
if (metamethod)
{
if (const FunctionType* ftv = get<FunctionType>(*metamethod))
if (const FunctionType* ftv = get<FunctionType>(FFlag::LuauMetatableFollow ? follow(*metamethod) : *metamethod))
{
if (isEquality)
{

View File

@ -17,11 +17,11 @@
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit)
LUAU_FASTFLAG(LuauErrorRecoveryType)
LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false)
LUAU_FASTFLAGVARIABLE(LuauTransitiveSubtyping, false)
LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping)
LUAU_FASTFLAGVARIABLE(LuauTransitiveSubtyping)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering, false)
LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart, false)
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering)
LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart)
namespace Luau
{

View File

@ -217,7 +217,7 @@ private:
AstType* parseTableType(bool inDeclarationContext = false);
AstTypeOrPack parseSimpleType(bool allowPack, bool inDeclarationContext = false);
AstTypeOrPack parseTypeOrPack();
AstTypeOrPack parseSimpleTypeOrPack();
AstType* parseType(bool inDeclarationContext = false);
AstTypePack* parseTypePack();

View File

@ -8,6 +8,7 @@
#include <errno.h>
#include <limits.h>
#include <string.h>
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
LUAU_FASTINTVARIABLE(LuauTypeLengthLimit, 1000)
@ -16,11 +17,12 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
// Warning: If you are introducing new syntax, ensure that it is behind a separate
// flag so that we don't break production games by reverting syntax changes.
// See docs/SyntaxChanges.md for an explanation.
LUAU_FASTFLAGVARIABLE(LuauSolverV2, false)
LUAU_FASTFLAGVARIABLE(LuauNativeAttribute, false)
LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr, false)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionsSyntax2, false)
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing, false)
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauNativeAttribute)
LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionsSyntax2)
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing)
LUAU_FASTFLAGVARIABLE(LuauPortableStringZeroCheck)
namespace Luau
{
@ -1131,7 +1133,8 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
AstType* type = parseType();
// since AstName contains a char*, it can't contain null
bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size);
bool containsNull = chars && (FFlag::LuauPortableStringZeroCheck ? memchr(chars->data, 0, chars->size) != nullptr
: strnlen(chars->data, chars->size) < chars->size);
if (chars && !containsNull)
{
@ -1609,7 +1612,8 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
AstType* type = parseType();
// since AstName contains a char*, it can't contain null
bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size);
bool containsNull = chars && (FFlag::LuauPortableStringZeroCheck ? memchr(chars->data, 0, chars->size) != nullptr
: strnlen(chars->data, chars->size) < chars->size);
if (chars && !containsNull)
props.push_back(AstTableProp{AstName(chars->data), begin.location, type, access, accessLocation});
@ -1858,7 +1862,7 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin)
ParseError::raise(begin, "Composite type was not an intersection or union.");
}
AstTypeOrPack Parser::parseTypeOrPack()
AstTypeOrPack Parser::parseSimpleTypeOrPack()
{
unsigned int oldRecursionCount = recursionCounter;
// recursion counter is incremented in parseSimpleType
@ -2873,7 +2877,7 @@ std::pair<AstArray<AstGenericType>, AstArray<AstGenericTypePack>> Parser::parseG
}
else
{
auto [type, typePack] = parseTypeOrPack();
auto [type, typePack] = parseSimpleTypeOrPack();
if (type)
report(type->location, "Expected type pack after '=', got type");
@ -2950,7 +2954,7 @@ AstArray<AstTypeOrPack> Parser::parseTypeParams()
}
else if (lexer.current().type == '(')
{
auto [type, typePack] = parseTypeOrPack();
auto [type, typePack] = parseSimpleTypeOrPack();
if (typePack)
parameters.push_back({{}, typePack});

View File

@ -26,7 +26,7 @@
#include <time.h>
LUAU_FASTFLAGVARIABLE(DebugLuauTimeTracing, false)
LUAU_FASTFLAGVARIABLE(DebugLuauTimeTracing)
namespace Luau
{
namespace TimeTrace

View File

@ -515,6 +515,40 @@ static void applyBuiltinCall(int bfid, BytecodeTypes& types)
types.a = LBC_TYPE_TABLE;
types.b = LBC_TYPE_TABLE;
break;
case LBF_VECTOR_MAGNITUDE:
types.result = LBC_TYPE_NUMBER;
types.a = LBC_TYPE_VECTOR;
break;
case LBF_VECTOR_NORMALIZE:
types.result = LBC_TYPE_VECTOR;
types.a = LBC_TYPE_VECTOR;
break;
case LBF_VECTOR_CROSS:
types.result = LBC_TYPE_VECTOR;
types.a = LBC_TYPE_VECTOR;
types.b = LBC_TYPE_VECTOR;
break;
case LBF_VECTOR_DOT:
types.result = LBC_TYPE_NUMBER;
types.a = LBC_TYPE_VECTOR;
types.b = LBC_TYPE_VECTOR;
break;
case LBF_VECTOR_FLOOR:
case LBF_VECTOR_CEIL:
case LBF_VECTOR_ABS:
case LBF_VECTOR_SIGN:
case LBF_VECTOR_CLAMP:
types.result = LBC_TYPE_VECTOR;
types.a = LBC_TYPE_VECTOR;
types.b = LBC_TYPE_VECTOR;
break;
case LBF_VECTOR_MIN:
case LBF_VECTOR_MAX:
types.result = LBC_TYPE_VECTOR;
types.a = LBC_TYPE_VECTOR;
types.b = LBC_TYPE_VECTOR;
types.c = LBC_TYPE_VECTOR; // We can mark optional arguments
break;
}
}

View File

@ -41,9 +41,9 @@
#endif
#endif
LUAU_FASTFLAGVARIABLE(DebugCodegenNoOpt, false)
LUAU_FASTFLAGVARIABLE(DebugCodegenOptSize, false)
LUAU_FASTFLAGVARIABLE(DebugCodegenSkipNumbering, false)
LUAU_FASTFLAGVARIABLE(DebugCodegenNoOpt)
LUAU_FASTFLAGVARIABLE(DebugCodegenOptSize)
LUAU_FASTFLAGVARIABLE(DebugCodegenSkipNumbering)
// Per-module IR instruction count limit
LUAU_FASTINTVARIABLE(CodegenHeuristicsInstructionLimit, 1'048'576) // 1 M

View File

@ -226,9 +226,10 @@ Udata* newUserdata(lua_State* L, size_t s, int tag)
if (Table* h = L->global->udatamt[tag])
{
u->metatable = h;
// currently, we always allocate unmarked objects, so forward barrier can be skipped
LUAU_ASSERT(!isblack(obj2gco(u)));
luaC_objbarrier(L, u, h);
u->metatable = h;
}
return u;

View File

@ -10,7 +10,7 @@
#include <string.h>
LUAU_FASTFLAGVARIABLE(DebugCodegenChaosA64, false)
LUAU_FASTFLAGVARIABLE(DebugCodegenChaosA64)
namespace Luau
{

View File

@ -13,6 +13,8 @@
static const int kMinMaxUnrolledParams = 5;
static const int kBit32BinaryOpUnrolledParams = 5;
LUAU_FASTFLAGVARIABLE(LuauVectorLibNativeCodegen);
namespace Luau
{
namespace CodeGen
@ -885,6 +887,300 @@ static BuiltinImplResult translateBuiltinBufferWrite(
return {BuiltinImplType::Full, 0};
}
static BuiltinImplResult translateBuiltinVectorMagnitude(
IrBuilder& build,
int nparams,
int ra,
int arg,
IrOp args,
IrOp arg3,
int nresults,
int pcpos
)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
IrOp arg1 = build.vmReg(arg);
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
return {BuiltinImplType::None, -1};
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
IrOp x = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
IrOp y = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
IrOp z = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
IrOp x2 = build.inst(IrCmd::MUL_NUM, x, x);
IrOp y2 = build.inst(IrCmd::MUL_NUM, y, y);
IrOp z2 = build.inst(IrCmd::MUL_NUM, z, z);
IrOp sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, x2, y2), z2);
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), mag);
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
return {BuiltinImplType::Full, 1};
}
static BuiltinImplResult translateBuiltinVectorNormalize(
IrBuilder& build,
int nparams,
int ra,
int arg,
IrOp args,
IrOp arg3,
int nresults,
int pcpos
)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
IrOp arg1 = build.vmReg(arg);
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
return {BuiltinImplType::None, -1};
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
IrOp x = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
IrOp y = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
IrOp z = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
IrOp x2 = build.inst(IrCmd::MUL_NUM, x, x);
IrOp y2 = build.inst(IrCmd::MUL_NUM, y, y);
IrOp z2 = build.inst(IrCmd::MUL_NUM, z, z);
IrOp sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, x2, y2), z2);
IrOp mag = build.inst(IrCmd::SQRT_NUM, sum);
IrOp inv = build.inst(IrCmd::DIV_NUM, build.constDouble(1.0), mag);
IrOp xr = build.inst(IrCmd::MUL_NUM, x, inv);
IrOp yr = build.inst(IrCmd::MUL_NUM, y, inv);
IrOp zr = build.inst(IrCmd::MUL_NUM, z, inv);
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xr, yr, zr);
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
return {BuiltinImplType::Full, 1};
}
static BuiltinImplResult translateBuiltinVectorCross(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
IrOp arg1 = build.vmReg(arg);
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
return {BuiltinImplType::None, -1};
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
IrOp x2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(0));
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
IrOp y2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(4));
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
IrOp z2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(8));
IrOp y1z2 = build.inst(IrCmd::MUL_NUM, y1, z2);
IrOp z1y2 = build.inst(IrCmd::MUL_NUM, z1, y2);
IrOp xr = build.inst(IrCmd::SUB_NUM, y1z2, z1y2);
IrOp z1x2 = build.inst(IrCmd::MUL_NUM, z1, x2);
IrOp x1z2 = build.inst(IrCmd::MUL_NUM, x1, z2);
IrOp yr = build.inst(IrCmd::SUB_NUM, z1x2, x1z2);
IrOp x1y2 = build.inst(IrCmd::MUL_NUM, x1, y2);
IrOp y1x2 = build.inst(IrCmd::MUL_NUM, y1, x2);
IrOp zr = build.inst(IrCmd::SUB_NUM, x1y2, y1x2);
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xr, yr, zr);
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
return {BuiltinImplType::Full, 1};
}
static BuiltinImplResult translateBuiltinVectorDot(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
IrOp arg1 = build.vmReg(arg);
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
return {BuiltinImplType::None, -1};
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
IrOp x2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(0));
IrOp xx = build.inst(IrCmd::MUL_NUM, x1, x2);
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
IrOp y2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(4));
IrOp yy = build.inst(IrCmd::MUL_NUM, y1, y2);
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
IrOp z2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(8));
IrOp zz = build.inst(IrCmd::MUL_NUM, z1, z2);
IrOp sum = build.inst(IrCmd::ADD_NUM, build.inst(IrCmd::ADD_NUM, xx, yy), zz);
build.inst(IrCmd::STORE_DOUBLE, build.vmReg(ra), sum);
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TNUMBER));
return {BuiltinImplType::Full, 1};
}
static BuiltinImplResult translateBuiltinVectorMap1(
IrBuilder& build,
IrCmd cmd,
int nparams,
int ra,
int arg,
IrOp args,
IrOp arg3,
int nresults,
int pcpos
)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
IrOp arg1 = build.vmReg(arg);
if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant)
return {BuiltinImplType::None, -1};
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
IrOp xr = build.inst(cmd, x1);
IrOp yr = build.inst(cmd, y1);
IrOp zr = build.inst(cmd, z1);
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xr, yr, zr);
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
return {BuiltinImplType::Full, 1};
}
static BuiltinImplResult translateBuiltinVectorClamp(
IrBuilder& build,
int nparams,
int ra,
int arg,
IrOp args,
IrOp arg3,
int nresults,
IrOp fallback,
int pcpos
)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
IrOp arg1 = build.vmReg(arg);
if (nparams != 3 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant || arg3.kind == IrOpKind::Constant)
return {BuiltinImplType::None, -1};
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
build.loadAndCheckTag(arg3, LUA_TVECTOR, build.vmExit(pcpos));
IrOp block1 = build.block(IrBlockKind::Internal);
IrOp block2 = build.block(IrBlockKind::Internal);
IrOp block3 = build.block(IrBlockKind::Internal);
IrOp x = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
IrOp xmin = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(0));
IrOp xmax = build.inst(IrCmd::LOAD_FLOAT, arg3, build.constInt(0));
build.inst(IrCmd::JUMP_CMP_NUM, xmin, xmax, build.cond(IrCondition::NotLessEqual), fallback, block1);
build.beginBlock(block1);
IrOp y = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
IrOp ymin = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(4));
IrOp ymax = build.inst(IrCmd::LOAD_FLOAT, arg3, build.constInt(4));
build.inst(IrCmd::JUMP_CMP_NUM, ymin, ymax, build.cond(IrCondition::NotLessEqual), fallback, block2);
build.beginBlock(block2);
IrOp z = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
IrOp zmin = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(8));
IrOp zmax = build.inst(IrCmd::LOAD_FLOAT, arg3, build.constInt(8));
build.inst(IrCmd::JUMP_CMP_NUM, zmin, zmax, build.cond(IrCondition::NotLessEqual), fallback, block3);
build.beginBlock(block3);
IrOp xtemp = build.inst(IrCmd::MAX_NUM, xmin, x);
IrOp xclamped = build.inst(IrCmd::MIN_NUM, xmax, xtemp);
IrOp ytemp = build.inst(IrCmd::MAX_NUM, ymin, y);
IrOp yclamped = build.inst(IrCmd::MIN_NUM, ymax, ytemp);
IrOp ztemp = build.inst(IrCmd::MAX_NUM, zmin, z);
IrOp zclamped = build.inst(IrCmd::MIN_NUM, zmax, ztemp);
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xclamped, yclamped, zclamped);
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
return {BuiltinImplType::UsesFallback, 1};
}
static BuiltinImplResult translateBuiltinVectorMap2(
IrBuilder& build,
IrCmd cmd,
int nparams,
int ra,
int arg,
IrOp args,
IrOp arg3,
int nresults,
int pcpos
)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen);
IrOp arg1 = build.vmReg(arg);
if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant)
return {BuiltinImplType::None, -1};
build.loadAndCheckTag(arg1, LUA_TVECTOR, build.vmExit(pcpos));
build.loadAndCheckTag(args, LUA_TVECTOR, build.vmExit(pcpos));
IrOp x1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(0));
IrOp y1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(4));
IrOp z1 = build.inst(IrCmd::LOAD_FLOAT, arg1, build.constInt(8));
IrOp x2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(0));
IrOp y2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(4));
IrOp z2 = build.inst(IrCmd::LOAD_FLOAT, args, build.constInt(8));
IrOp xr = build.inst(cmd, x1, x2);
IrOp yr = build.inst(cmd, y1, y2);
IrOp zr = build.inst(cmd, z1, z2);
build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), xr, yr, zr);
build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR));
return {BuiltinImplType::Full, 1};
}
BuiltinImplResult translateBuiltin(
IrBuilder& build,
int bfid,
@ -898,6 +1194,8 @@ BuiltinImplResult translateBuiltin(
int pcpos
)
{
BuiltinImplResult noneResult = {BuiltinImplType::None, -1};
// Builtins are not allowed to handle variadic arguments
if (nparams == LUA_MULTRET)
return {BuiltinImplType::None, -1};
@ -1018,6 +1316,35 @@ BuiltinImplResult translateBuiltin(
return translateBuiltinBufferRead(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_READF64, 8, IrCmd::NOP);
case LBF_BUFFER_WRITEF64:
return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEF64, 8, IrCmd::NOP);
case LBF_VECTOR_MAGNITUDE:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMagnitude(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult;
case LBF_VECTOR_NORMALIZE:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorNormalize(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult;
case LBF_VECTOR_CROSS:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorCross(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult;
case LBF_VECTOR_DOT:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorDot(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult;
case LBF_VECTOR_FLOOR:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::FLOOR_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
: noneResult;
case LBF_VECTOR_CEIL:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::CEIL_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
: noneResult;
case LBF_VECTOR_ABS:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::ABS_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
: noneResult;
case LBF_VECTOR_SIGN:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::SIGN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
: noneResult;
case LBF_VECTOR_CLAMP:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorClamp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos)
: noneResult;
case LBF_VECTOR_MIN:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap2(build, IrCmd::MIN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
: noneResult;
case LBF_VECTOR_MAX:
return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap2(build, IrCmd::MAX_NUM, nparams, ra, arg, args, arg3, nresults, pcpos)
: noneResult;
default:
return {BuiltinImplType::None, -1};
}

View File

@ -17,7 +17,7 @@
LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3)
LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64)
LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks, false)
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
namespace Luau
{
@ -536,6 +536,17 @@ static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid
case LBF_BUFFER_WRITEF32:
case LBF_BUFFER_READF64:
case LBF_BUFFER_WRITEF64:
case LBF_VECTOR_MAGNITUDE:
case LBF_VECTOR_NORMALIZE:
case LBF_VECTOR_CROSS:
case LBF_VECTOR_DOT:
case LBF_VECTOR_FLOOR:
case LBF_VECTOR_CEIL:
case LBF_VECTOR_ABS:
case LBF_VECTOR_SIGN:
case LBF_VECTOR_CLAMP:
case LBF_VECTOR_MIN:
case LBF_VECTOR_MAX:
break;
case LBF_TABLE_INSERT:
state.invalidateHeap();

View File

@ -600,6 +600,19 @@ enum LuauBuiltinFunction
LBF_BUFFER_WRITEF32,
LBF_BUFFER_READF64,
LBF_BUFFER_WRITEF64,
// vector.
LBF_VECTOR_MAGNITUDE,
LBF_VECTOR_NORMALIZE,
LBF_VECTOR_CROSS,
LBF_VECTOR_DOT,
LBF_VECTOR_FLOOR,
LBF_VECTOR_CEIL,
LBF_VECTOR_ABS,
LBF_VECTOR_SIGN,
LBF_VECTOR_CLAMP,
LBF_VECTOR_MIN,
LBF_VECTOR_MAX,
};
// Capture type, used in LOP_CAPTURE

View File

@ -106,10 +106,10 @@ FValue<T>* FValue<T>::list = nullptr;
{ \
extern Luau::FValue<bool> flag; \
}
#define LUAU_FASTFLAGVARIABLE(flag, def) \
#define LUAU_FASTFLAGVARIABLE(flag) \
namespace FFlag \
{ \
Luau::FValue<bool> flag(#flag, def, false); \
Luau::FValue<bool> flag(#flag, false, false); \
}
#define LUAU_FASTINT(flag) \
namespace FInt \

View File

@ -37,11 +37,11 @@ struct CompileOptions
// 2 - statement and expression coverage (verbose)
int coverageLevel = 0;
// global builtin to construct vectors; disabled by default
// alternative global builtin to construct vectors, in addition to default builtin 'vector.create'
const char* vectorLib = nullptr;
const char* vectorCtor = nullptr;
// vector type name for type tables; disabled by default
// alternative vector type name for type tables, in addition to default type 'vector'
const char* vectorType = nullptr;
// null-terminated array of globals that are mutable; disables the import optimization for fields accessed through these

View File

@ -33,11 +33,11 @@ struct lua_CompileOptions
// 2 - statement and expression coverage (verbose)
int coverageLevel; // default=0
// global builtin to construct vectors; disabled by default
// alternative global builtin to construct vectors, in addition to default builtin 'vector.create'
const char* vectorLib;
const char* vectorCtor;
// vector type name for type tables; disabled by default
// alternative vector type name for type tables, in addition to default type 'vector'
const char* vectorType;
// null-terminated array of globals that are mutable; disables the import optimization for fields accessed through these

View File

@ -4,6 +4,8 @@
#include "Luau/Bytecode.h"
#include "Luau/Compiler.h"
LUAU_FASTFLAGVARIABLE(LuauVectorBuiltins)
namespace Luau
{
namespace Compile
@ -220,6 +222,34 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
return LBF_BUFFER_WRITEF64;
}
if (FFlag::LuauVectorBuiltins && builtin.object == "vector")
{
if (builtin.method == "create")
return LBF_VECTOR;
if (builtin.method == "magnitude")
return LBF_VECTOR_MAGNITUDE;
if (builtin.method == "normalize")
return LBF_VECTOR_NORMALIZE;
if (builtin.method == "cross")
return LBF_VECTOR_CROSS;
if (builtin.method == "dot")
return LBF_VECTOR_DOT;
if (builtin.method == "floor")
return LBF_VECTOR_FLOOR;
if (builtin.method == "ceil")
return LBF_VECTOR_CEIL;
if (builtin.method == "abs")
return LBF_VECTOR_ABS;
if (builtin.method == "sign")
return LBF_VECTOR_SIGN;
if (builtin.method == "clamp")
return LBF_VECTOR_CLAMP;
if (builtin.method == "min")
return LBF_VECTOR_MIN;
if (builtin.method == "max")
return LBF_VECTOR_MAX;
}
if (options.vectorCtor)
{
if (options.vectorLib)
@ -463,6 +493,23 @@ BuiltinInfo getBuiltinInfo(int bfid)
case LBF_BUFFER_WRITEF32:
case LBF_BUFFER_WRITEF64:
return {3, 0, BuiltinInfo::Flag_NoneSafe};
case LBF_VECTOR_MAGNITUDE:
case LBF_VECTOR_NORMALIZE:
return {1, 1, BuiltinInfo::Flag_NoneSafe};
case LBF_VECTOR_CROSS:
case LBF_VECTOR_DOT:
return {2, 1, BuiltinInfo::Flag_NoneSafe};
case LBF_VECTOR_FLOOR:
case LBF_VECTOR_CEIL:
case LBF_VECTOR_ABS:
case LBF_VECTOR_SIGN:
return {1, 1, BuiltinInfo::Flag_NoneSafe};
case LBF_VECTOR_CLAMP:
return {3, 1, BuiltinInfo::Flag_NoneSafe};
case LBF_VECTOR_MIN:
case LBF_VECTOR_MAX:
return {-1, 1}; // variadic
}
LUAU_UNREACHABLE();

View File

@ -3,6 +3,8 @@
#include "Luau/BytecodeBuilder.h"
LUAU_FASTFLAGVARIABLE(LuauCompileVectorTypeInfo)
namespace Luau
{
@ -29,6 +31,8 @@ static LuauBytecodeType getPrimitiveType(AstName name)
return LBC_TYPE_THREAD;
else if (name == "buffer")
return LBC_TYPE_BUFFER;
else if (FFlag::LuauCompileVectorTypeInfo && name == "vector")
return LBC_TYPE_VECTOR;
else if (name == "any" || name == "unknown")
return LBC_TYPE_ANY;
else
@ -40,7 +44,7 @@ static LuauBytecodeType getType(
const AstArray<AstGenericType>& generics,
const DenseHashMap<AstName, AstStatTypeAlias*>& typeAliases,
bool resolveAliases,
const char* vectorType,
const char* hostVectorType,
const DenseHashMap<AstName, uint8_t>& userdataTypes,
BytecodeBuilder& bytecode
)
@ -54,7 +58,7 @@ static LuauBytecodeType getType(
{
// note: we only resolve aliases to the depth of 1 to avoid dealing with recursive aliases
if (resolveAliases)
return getType((*alias)->type, (*alias)->generics, typeAliases, /* resolveAliases= */ false, vectorType, userdataTypes, bytecode);
return getType((*alias)->type, (*alias)->generics, typeAliases, /* resolveAliases= */ false, hostVectorType, userdataTypes, bytecode);
else
return LBC_TYPE_ANY;
}
@ -62,7 +66,7 @@ static LuauBytecodeType getType(
if (isGeneric(ref->name, generics))
return LBC_TYPE_ANY;
if (vectorType && ref->name == vectorType)
if (hostVectorType && ref->name == hostVectorType)
return LBC_TYPE_VECTOR;
if (LuauBytecodeType prim = getPrimitiveType(ref->name); prim != LBC_TYPE_INVALID)
@ -92,7 +96,7 @@ static LuauBytecodeType getType(
for (AstType* ty : un->types)
{
LuauBytecodeType et = getType(ty, generics, typeAliases, resolveAliases, vectorType, userdataTypes, bytecode);
LuauBytecodeType et = getType(ty, generics, typeAliases, resolveAliases, hostVectorType, userdataTypes, bytecode);
if (et == LBC_TYPE_NIL)
{
@ -126,7 +130,7 @@ static LuauBytecodeType getType(
static std::string getFunctionType(
const AstExprFunction* func,
const DenseHashMap<AstName, AstStatTypeAlias*>& typeAliases,
const char* vectorType,
const char* hostVectorType,
const DenseHashMap<AstName, uint8_t>& userdataTypes,
BytecodeBuilder& bytecode
)
@ -146,8 +150,9 @@ static std::string getFunctionType(
for (AstLocal* arg : func->args)
{
LuauBytecodeType ty =
arg->annotation ? getType(arg->annotation, func->generics, typeAliases, /* resolveAliases= */ true, vectorType, userdataTypes, bytecode)
: LBC_TYPE_ANY;
arg->annotation
? getType(arg->annotation, func->generics, typeAliases, /* resolveAliases= */ true, hostVectorType, userdataTypes, bytecode)
: LBC_TYPE_ANY;
if (ty != LBC_TYPE_ANY)
haveNonAnyParam = true;
@ -175,7 +180,7 @@ struct TypeMapVisitor : AstVisitor
DenseHashMap<AstExprFunction*, std::string>& functionTypes;
DenseHashMap<AstLocal*, LuauBytecodeType>& localTypes;
DenseHashMap<AstExpr*, LuauBytecodeType>& exprTypes;
const char* vectorType;
const char* hostVectorType;
const DenseHashMap<AstName, uint8_t>& userdataTypes;
const BuiltinAstTypes& builtinTypes;
const DenseHashMap<AstExprCall*, int>& builtinCalls;
@ -191,7 +196,7 @@ struct TypeMapVisitor : AstVisitor
DenseHashMap<AstExprFunction*, std::string>& functionTypes,
DenseHashMap<AstLocal*, LuauBytecodeType>& localTypes,
DenseHashMap<AstExpr*, LuauBytecodeType>& exprTypes,
const char* vectorType,
const char* hostVectorType,
const DenseHashMap<AstName, uint8_t>& userdataTypes,
const BuiltinAstTypes& builtinTypes,
const DenseHashMap<AstExprCall*, int>& builtinCalls,
@ -201,7 +206,7 @@ struct TypeMapVisitor : AstVisitor
: functionTypes(functionTypes)
, localTypes(localTypes)
, exprTypes(exprTypes)
, vectorType(vectorType)
, hostVectorType(hostVectorType)
, userdataTypes(userdataTypes)
, builtinTypes(builtinTypes)
, builtinCalls(builtinCalls)
@ -271,7 +276,7 @@ struct TypeMapVisitor : AstVisitor
resolvedExprs[expr] = ty;
LuauBytecodeType bty = getType(ty, {}, typeAliases, /* resolveAliases= */ true, vectorType, userdataTypes, bytecode);
LuauBytecodeType bty = getType(ty, {}, typeAliases, /* resolveAliases= */ true, hostVectorType, userdataTypes, bytecode);
exprTypes[expr] = bty;
return bty;
}
@ -282,7 +287,7 @@ struct TypeMapVisitor : AstVisitor
resolvedLocals[local] = ty;
LuauBytecodeType bty = getType(ty, {}, typeAliases, /* resolveAliases= */ true, vectorType, userdataTypes, bytecode);
LuauBytecodeType bty = getType(ty, {}, typeAliases, /* resolveAliases= */ true, hostVectorType, userdataTypes, bytecode);
if (bty != LBC_TYPE_ANY)
localTypes[local] = bty;
@ -370,7 +375,7 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprFunction* node) override
{
std::string type = getFunctionType(node, typeAliases, vectorType, userdataTypes, bytecode);
std::string type = getFunctionType(node, typeAliases, hostVectorType, userdataTypes, bytecode);
if (!type.empty())
functionTypes[node] = std::move(type);
@ -675,6 +680,8 @@ struct TypeMapVisitor : AstVisitor
case LBF_BUFFER_READU32:
case LBF_BUFFER_READF32:
case LBF_BUFFER_READF64:
case LBF_VECTOR_MAGNITUDE:
case LBF_VECTOR_DOT:
recordResolvedType(node, &builtinTypes.numberType);
break;
@ -691,6 +698,15 @@ struct TypeMapVisitor : AstVisitor
break;
case LBF_VECTOR:
case LBF_VECTOR_NORMALIZE:
case LBF_VECTOR_CROSS:
case LBF_VECTOR_FLOOR:
case LBF_VECTOR_CEIL:
case LBF_VECTOR_ABS:
case LBF_VECTOR_SIGN:
case LBF_VECTOR_CLAMP:
case LBF_VECTOR_MIN:
case LBF_VECTOR_MAX:
recordResolvedType(node, &builtinTypes.vectorType);
break;
}
@ -712,7 +728,7 @@ void buildTypeMap(
DenseHashMap<AstLocal*, LuauBytecodeType>& localTypes,
DenseHashMap<AstExpr*, LuauBytecodeType>& exprTypes,
AstNode* root,
const char* vectorType,
const char* hostVectorType,
const DenseHashMap<AstName, uint8_t>& userdataTypes,
const BuiltinAstTypes& builtinTypes,
const DenseHashMap<AstExprCall*, int>& builtinCalls,
@ -720,7 +736,7 @@ void buildTypeMap(
BytecodeBuilder& bytecode
)
{
TypeMapVisitor visitor(functionTypes, localTypes, exprTypes, vectorType, userdataTypes, builtinTypes, builtinCalls, globals, bytecode);
TypeMapVisitor visitor(functionTypes, localTypes, exprTypes, hostVectorType, userdataTypes, builtinTypes, builtinCalls, globals, bytecode);
root->visit(&visitor);
}

View File

@ -14,8 +14,8 @@ class BytecodeBuilder;
struct BuiltinAstTypes
{
BuiltinAstTypes(const char* vectorType)
: vectorType{{}, std::nullopt, AstName{vectorType}, std::nullopt, {}}
BuiltinAstTypes(const char* hostVectorType)
: hostVectorType{{}, std::nullopt, AstName{hostVectorType}, std::nullopt, {}}
{
}
@ -23,7 +23,9 @@ struct BuiltinAstTypes
AstTypeReference booleanType{{}, std::nullopt, AstName{"boolean"}, std::nullopt, {}};
AstTypeReference numberType{{}, std::nullopt, AstName{"number"}, std::nullopt, {}};
AstTypeReference stringType{{}, std::nullopt, AstName{"string"}, std::nullopt, {}};
AstTypeReference vectorType;
AstTypeReference vectorType{{}, std::nullopt, AstName{"vector"}, std::nullopt, {}};
AstTypeReference hostVectorType;
};
void buildTypeMap(
@ -31,7 +33,7 @@ void buildTypeMap(
DenseHashMap<AstLocal*, LuauBytecodeType>& localTypes,
DenseHashMap<AstExpr*, LuauBytecodeType>& exprTypes,
AstNode* root,
const char* vectorType,
const char* hostVectorType,
const DenseHashMap<AstName, uint8_t>& userdataTypes,
const BuiltinAstTypes& builtinTypes,
const DenseHashMap<AstExprCall*, int>& builtinCalls,

View File

@ -351,6 +351,7 @@ target_sources(Luau.VM PRIVATE
VM/src/ltm.cpp
VM/src/ludata.cpp
VM/src/lutf8lib.cpp
VM/src/lveclib.cpp
VM/src/lvmexecute.cpp
VM/src/lvmload.cpp
VM/src/lvmutils.cpp

View File

@ -189,6 +189,7 @@ LUA_API int lua_pushthread(lua_State* L);
LUA_API void lua_pushlightuserdatatagged(lua_State* L, void* p, int tag);
LUA_API void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag);
LUA_API void* lua_newuserdatataggedwithmetatable(lua_State* L, size_t sz, int tag); // metatable fetched with lua_getuserdatametatable
LUA_API void* lua_newuserdatadtor(lua_State* L, size_t sz, void (*dtor)(void*));
LUA_API void* lua_newbuffer(lua_State* L, size_t sz);

View File

@ -136,6 +136,9 @@ LUALIB_API int luaopen_math(lua_State* L);
#define LUA_DBLIBNAME "debug"
LUALIB_API int luaopen_debug(lua_State* L);
#define LUA_VECLIBNAME "vector"
LUALIB_API int luaopen_vector(lua_State* L);
// open all builtin libraries
LUALIB_API void luaL_openlibs(lua_State* L);

View File

@ -1283,6 +1283,26 @@ void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag)
return u->data;
}
void* lua_newuserdatataggedwithmetatable(lua_State* L, size_t sz, int tag)
{
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
luaC_checkGC(L);
luaC_threadbarrier(L);
Udata* u = luaU_newudata(L, sz, tag);
// currently, we always allocate unmarked objects, so forward barrier can be skipped
LUAU_ASSERT(!isblack(obj2gco(u)));
Table* h = L->global->udatamt[tag];
api_check(L, h != nullptr);
u->metatable = h;
setuvalue(L, L->top, u);
api_incr_top(L);
return u->data;
}
void* lua_newuserdatadtor(lua_State* L, size_t sz, void (*dtor)(void*))
{
luaC_checkGC(L);

View File

@ -1437,6 +1437,263 @@ static int luauF_writefp(lua_State* L, StkId res, TValue* arg0, int nresults, St
return -1;
}
static int luauF_vectormagnitude(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisvector(arg0))
{
const float* v = vvalue(arg0);
#if LUA_VECTOR_SIZE == 4
setnvalue(res, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]));
#else
setnvalue(res, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
#endif
return 1;
}
return -1;
}
static int luauF_vectornormalize(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisvector(arg0))
{
const float* v = vvalue(arg0);
#if LUA_VECTOR_SIZE == 4
float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
setvvalue(res, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt, v[3] * invSqrt);
#else
float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
setvvalue(res, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt, 0.0f);
#endif
return 1;
}
return -1;
}
static int luauF_vectorcross(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 2 && nresults <= 1 && ttisvector(arg0) && ttisvector(args))
{
const float* a = vvalue(arg0);
const float* b = vvalue(args);
// same for 3- and 4- wide vectors
setvvalue(res, a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0f);
return 1;
}
return -1;
}
static int luauF_vectordot(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 2 && nresults <= 1 && ttisvector(arg0) && ttisvector(args))
{
const float* a = vvalue(arg0);
const float* b = vvalue(args);
#if LUA_VECTOR_SIZE == 4
setnvalue(res, a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]);
#else
setnvalue(res, a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
#endif
return 1;
}
return -1;
}
static int luauF_vectorfloor(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisvector(arg0))
{
const float* v = vvalue(arg0);
#if LUA_VECTOR_SIZE == 4
setvvalue(res, floorf(v[0]), floorf(v[1]), floorf(v[2]), floorf(v[3]));
#else
setvvalue(res, floorf(v[0]), floorf(v[1]), floorf(v[2]), 0.0f);
#endif
return 1;
}
return -1;
}
static int luauF_vectorceil(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisvector(arg0))
{
const float* v = vvalue(arg0);
#if LUA_VECTOR_SIZE == 4
setvvalue(res, ceilf(v[0]), ceilf(v[1]), ceilf(v[2]), ceilf(v[3]));
#else
setvvalue(res, ceilf(v[0]), ceilf(v[1]), ceilf(v[2]), 0.0f);
#endif
return 1;
}
return -1;
}
static int luauF_vectorabs(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisvector(arg0))
{
const float* v = vvalue(arg0);
#if LUA_VECTOR_SIZE == 4
setvvalue(res, fabsf(v[0]), fabsf(v[1]), fabsf(v[2]), fabsf(v[3]));
#else
setvvalue(res, fabsf(v[0]), fabsf(v[1]), fabsf(v[2]), 0.0f);
#endif
return 1;
}
return -1;
}
static int luauF_vectorsign(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 1 && nresults <= 1 && ttisvector(arg0))
{
const float* v = vvalue(arg0);
#if LUA_VECTOR_SIZE == 4
setvvalue(res, luaui_signf(v[0]), luaui_signf(v[1]), luaui_signf(v[2]), luaui_signf(v[3]));
#else
setvvalue(res, luaui_signf(v[0]), luaui_signf(v[1]), luaui_signf(v[2]), 0.0f);
#endif
return 1;
}
return -1;
}
static int luauF_vectorclamp(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 3 && nresults <= 1 && ttisvector(arg0) && ttisvector(args) && ttisvector(args + 1))
{
const float* v = vvalue(arg0);
const float* min = vvalue(args);
const float* max = vvalue(args + 1);
if (min[0] <= max[0] && min[1] <= max[1] && min[2] <= max[2])
{
#if LUA_VECTOR_SIZE == 4
setvvalue(
res,
luaui_clampf(v[0], min[0], max[0]),
luaui_clampf(v[1], min[1], max[1]),
luaui_clampf(v[2], min[2], max[2]),
luaui_clampf(v[3], min[3], max[3])
);
#else
setvvalue(res, luaui_clampf(v[0], min[0], max[0]), luaui_clampf(v[1], min[1], max[1]), luaui_clampf(v[2], min[2], max[2]), 0.0f);
#endif
return 1;
}
}
return -1;
}
static int luauF_vectormin(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 2 && nresults <= 1 && ttisvector(arg0) && ttisvector(args))
{
const float* a = vvalue(arg0);
const float* b = vvalue(args);
float result[4];
result[0] = (b[0] < a[0]) ? b[0] : a[0];
result[1] = (b[1] < a[1]) ? b[1] : a[1];
result[2] = (b[2] < a[2]) ? b[2] : a[2];
#if LUA_VECTOR_SIZE == 4
result[3] = (b[3] < a[3]) ? b[3] : a[3];
#else
result[3] = 0.0f;
#endif
for (int i = 3; i <= nparams; ++i)
{
if (!ttisvector(args + (i - 2)))
return -1;
const float* c = vvalue(args + (i - 2));
result[0] = (c[0] < result[0]) ? c[0] : result[0];
result[1] = (c[1] < result[1]) ? c[1] : result[1];
result[2] = (c[2] < result[2]) ? c[2] : result[2];
#if LUA_VECTOR_SIZE == 4
result[3] = (c[3] < result[3]) ? c[3] : result[3];
#endif
}
setvvalue(res, result[0], result[1], result[2], result[3]);
return 1;
}
return -1;
}
static int luauF_vectormax(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
if (nparams >= 2 && nresults <= 1 && ttisvector(arg0) && ttisvector(args))
{
const float* a = vvalue(arg0);
const float* b = vvalue(args);
float result[4];
result[0] = (b[0] > a[0]) ? b[0] : a[0];
result[1] = (b[1] > a[1]) ? b[1] : a[1];
result[2] = (b[2] > a[2]) ? b[2] : a[2];
#if LUA_VECTOR_SIZE == 4
result[3] = (b[3] > a[3]) ? b[3] : a[3];
#else
result[3] = 0.0f;
#endif
for (int i = 3; i <= nparams; ++i)
{
if (!ttisvector(args + (i - 2)))
return -1;
const float* c = vvalue(args + (i - 2));
result[0] = (c[0] > result[0]) ? c[0] : result[0];
result[1] = (c[1] > result[1]) ? c[1] : result[1];
result[2] = (c[2] > result[2]) ? c[2] : result[2];
#if LUA_VECTOR_SIZE == 4
result[3] = (c[3] > result[3]) ? c[3] : result[3];
#endif
}
setvvalue(res, result[0], result[1], result[2], result[3]);
return 1;
}
return -1;
}
static int luauF_missing(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
{
return -1;
@ -1620,6 +1877,18 @@ const luau_FastFunction luauF_table[256] = {
luauF_readfp<double>,
luauF_writefp<double>,
luauF_vectormagnitude,
luauF_vectornormalize,
luauF_vectorcross,
luauF_vectordot,
luauF_vectorfloor,
luauF_vectorceil,
luauF_vectorabs,
luauF_vectorsign,
luauF_vectorclamp,
luauF_vectormin,
luauF_vectormax,
// When adding builtins, add them above this line; what follows is 64 "dummy" entries with luauF_missing fallback.
// This is important so that older versions of the runtime that don't support newer builtins automatically fall back via luauF_missing.
// Given the builtin addition velocity this should always provide a larger compatibility window than bytecode versions suggest.

View File

@ -8,6 +8,8 @@
#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))
@ -107,6 +109,10 @@ static int db_info(lua_State* L)
break;
default:
// restore stack state of another thread as 'f' option might not have been visited yet
if (DFFlag::LuauDebugInfoInvArgLeftovers && L != L1)
lua_settop(L1, l1top);
luaL_argerror(L, arg + 2, "invalid option");
}
}

View File

@ -15,6 +15,7 @@ static const luaL_Reg lualibs[] = {
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_BITLIBNAME, luaopen_bit32},
{LUA_BUFFERLIBNAME, luaopen_buffer},
{LUA_VECLIBNAME, luaopen_vector},
{NULL, NULL},
};

View File

@ -7,7 +7,7 @@
#include <math.h>
#include <time.h>
LUAU_FASTFLAGVARIABLE(LuauMathMap, false)
LUAU_FASTFLAGVARIABLE(LuauMathMap)
#undef PI
#define PI (3.14159265358979323846)

View File

@ -33,6 +33,17 @@ inline bool luai_vecisnan(const float* a)
#endif
}
inline float luaui_signf(float v)
{
return v > 0.0f ? 1.0f : v < 0.0f ? -1.0f : 0.0f;
}
inline float luaui_clampf(float v, float min, float max)
{
float r = v < min ? min : v;
return r > max ? max : r;
}
LUAU_FASTMATH_BEGIN
inline double luai_nummod(double a, double b)
{

291
VM/src/lveclib.cpp Normal file
View File

@ -0,0 +1,291 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lualib.h"
#include "lcommon.h"
#include "lnumutils.h"
#include <math.h>
static int vector_create(lua_State* L)
{
double x = luaL_checknumber(L, 1);
double y = luaL_checknumber(L, 2);
double z = luaL_checknumber(L, 3);
#if LUA_VECTOR_SIZE == 4
// checking argument count to avoid accepting 'nil' as a valid value
double w = lua_gettop(L) >= 4 ? luaL_checknumber(L, 4) : 0.0;
lua_pushvector(L, float(x), float(y), float(z), float(w));
#else
lua_pushvector(L, float(x), float(y), float(z));
#endif
return 1;
}
static int vector_magnitude(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]));
#else
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));
#endif
return 1;
}
static int vector_normalize(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
lua_pushvector(L, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt, v[3] * invSqrt);
#else
float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
lua_pushvector(L, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt);
#endif
return 1;
}
static int vector_cross(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0f);
#else
lua_pushvector(L, a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
#endif
return 1;
}
static int vector_dot(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
#if LUA_VECTOR_SIZE == 4
lua_pushnumber(L, a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]);
#else
lua_pushnumber(L, a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
#endif
return 1;
}
static int vector_angle(lua_State* L)
{
const float* a = luaL_checkvector(L, 1);
const float* b = luaL_checkvector(L, 2);
const float* axis = luaL_optvector(L, 3, nullptr);
// cross(a, b)
float cross[] = {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]};
double sinA = sqrt(cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]);
double cosA = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
double angle = atan2(sinA, cosA);
if (axis)
{
if (cross[0] * axis[0] + cross[1] * axis[1] + cross[2] * axis[2] < 0.0f)
angle = -angle;
}
lua_pushnumber(L, angle);
return 1;
}
static int vector_floor(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, floorf(v[0]), floorf(v[1]), floorf(v[2]), floorf(v[3]));
#else
lua_pushvector(L, floorf(v[0]), floorf(v[1]), floorf(v[2]));
#endif
return 1;
}
static int vector_ceil(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, ceilf(v[0]), ceilf(v[1]), ceilf(v[2]), ceilf(v[3]));
#else
lua_pushvector(L, ceilf(v[0]), ceilf(v[1]), ceilf(v[2]));
#endif
return 1;
}
static int vector_abs(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, fabsf(v[0]), fabsf(v[1]), fabsf(v[2]), fabsf(v[3]));
#else
lua_pushvector(L, fabsf(v[0]), fabsf(v[1]), fabsf(v[2]));
#endif
return 1;
}
static int vector_sign(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, luaui_signf(v[0]), luaui_signf(v[1]), luaui_signf(v[2]), luaui_signf(v[3]));
#else
lua_pushvector(L, luaui_signf(v[0]), luaui_signf(v[1]), luaui_signf(v[2]));
#endif
return 1;
}
static int vector_clamp(lua_State* L)
{
const float* v = luaL_checkvector(L, 1);
const float* min = luaL_checkvector(L, 2);
const float* max = luaL_checkvector(L, 3);
luaL_argcheck(L, min[0] <= max[0], 3, "max.x must be greater than or equal to min.x");
luaL_argcheck(L, min[1] <= max[1], 3, "max.y must be greater than or equal to min.y");
luaL_argcheck(L, min[2] <= max[2], 3, "max.z must be greater than or equal to min.z");
#if LUA_VECTOR_SIZE == 4
lua_pushvector(
L,
luaui_clampf(v[0], min[0], max[0]),
luaui_clampf(v[1], min[1], max[1]),
luaui_clampf(v[2], min[2], max[2]),
luaui_clampf(v[3], min[3], max[3])
);
#else
lua_pushvector(L, luaui_clampf(v[0], min[0], max[0]), luaui_clampf(v[1], min[1], max[1]), luaui_clampf(v[2], min[2], max[2]));
#endif
return 1;
}
static int vector_min(lua_State* L)
{
int n = lua_gettop(L);
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
float result[] = {v[0], v[1], v[2], v[3]};
#else
float result[] = {v[0], v[1], v[2]};
#endif
for (int i = 2; i <= n; i++)
{
const float* b = luaL_checkvector(L, i);
if (b[0] < result[0])
result[0] = b[0];
if (b[1] < result[1])
result[1] = b[1];
if (b[2] < result[2])
result[2] = b[2];
#if LUA_VECTOR_SIZE == 4
if (b[3] < result[3])
result[3] = b[3];
#endif
}
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, result[0], result[1], result[2], result[3]);
#else
lua_pushvector(L, result[0], result[1], result[2]);
#endif
return 1;
}
static int vector_max(lua_State* L)
{
int n = lua_gettop(L);
const float* v = luaL_checkvector(L, 1);
#if LUA_VECTOR_SIZE == 4
float result[] = {v[0], v[1], v[2], v[3]};
#else
float result[] = {v[0], v[1], v[2]};
#endif
for (int i = 2; i <= n; i++)
{
const float* b = luaL_checkvector(L, i);
if (b[0] > result[0])
result[0] = b[0];
if (b[1] > result[1])
result[1] = b[1];
if (b[2] > result[2])
result[2] = b[2];
#if LUA_VECTOR_SIZE == 4
if (b[3] > result[3])
result[3] = b[3];
#endif
}
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, result[0], result[1], result[2], result[3]);
#else
lua_pushvector(L, result[0], result[1], result[2]);
#endif
return 1;
}
static const luaL_Reg vectorlib[] = {
{"create", vector_create},
{"magnitude", vector_magnitude},
{"normalize", vector_normalize},
{"cross", vector_cross},
{"dot", vector_dot},
{"angle", vector_angle},
{"floor", vector_floor},
{"ceil", vector_ceil},
{"abs", vector_abs},
{"sign", vector_sign},
{"clamp", vector_clamp},
{"max", vector_max},
{"min", vector_min},
{NULL, NULL},
};
int luaopen_vector(lua_State* L)
{
luaL_register(L, LUA_VECLIBNAME, vectorlib);
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
lua_setfield(L, -2, "zero");
lua_pushvector(L, 1.0f, 1.0f, 1.0f, 1.0f);
lua_setfield(L, -2, "one");
#else
lua_pushvector(L, 0.0f, 0.0f, 0.0f);
lua_setfield(L, -2, "zero");
lua_pushvector(L, 1.0f, 1.0f, 1.0f);
lua_setfield(L, -2, "one");
#endif
return 1;
}

View File

@ -0,0 +1,165 @@
--!strict
local function prequire(name) local success, result = pcall(require, name); return if success then result else nil end
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
function test()
type Vertex = { p: vector, uv: vector, n: vector, t: vector, b: vector, h: number }
local grid_size = 100
local mesh: {
vertices: {Vertex},
indices: {number},
triangle_cone_p: {vector},
triangle_cone_n: {vector}
} = {
vertices = table.create(grid_size * grid_size),
indices = table.create((grid_size - 1) * (grid_size - 1) * 6),
triangle_cone_p = table.create((grid_size - 1) * (grid_size - 1) * 2),
triangle_cone_n = table.create((grid_size - 1) * (grid_size - 1) * 2)
}
function init_vertices()
local i = 1
for y = 1,grid_size do
for x = 1,grid_size do
local v: Vertex = {}
v.p = vector.create(x, y, math.cos(x) + math.sin(y))
v.uv = vector.create((x-1)/(grid_size-1), (y-1)/(grid_size-1), 0)
v.n = vector.create(0, 0, 0)
v.b = vector.create(0, 0, 0)
v.t = vector.create(0, 0, 0)
v.h = 0
mesh.vertices[i] = v
i += 1
end
end
end
function init_indices()
local i = 1
for y = 1,grid_size-1 do
for x = 1,grid_size-1 do
mesh.indices[i] = x + (y-1)*grid_size
i += 1
mesh.indices[i] = x + y*grid_size
i += 1
mesh.indices[i] = (x+1) + (y-1)*grid_size
i += 1
mesh.indices[i] = (x+1) + (y-1)*grid_size
i += 1
mesh.indices[i] = x + y*grid_size
i += 1
mesh.indices[i] = (x+1) + y*grid_size
i += 1
end
end
end
function calculate_normals()
local norm_sum = 0
for i = 1,#mesh.indices,3 do
local a = mesh.vertices[mesh.indices[i]]
local b = mesh.vertices[mesh.indices[i + 1]]
local c = mesh.vertices[mesh.indices[i + 2]]
local n = vector.cross(a.p - b.p, a.p - c.p)
a.n += n
b.n += n
c.n += n
end
for _,v in ipairs(mesh.vertices) do
v.n = vector.normalize(v.n)
norm_sum += vector.dot(v.n, v.n)
end
return norm_sum
end
function compute_triangle_cones()
local mesh_area = 0
local i = 1
for i = 1,#mesh.indices,3 do
local p0 = mesh.vertices[mesh.indices[i]]
local p1 = mesh.vertices[mesh.indices[i + 1]]
local p2 = mesh.vertices[mesh.indices[i + 2]]
local p10 = p1.p - p0.p
local p20 = p2.p - p0.p
local normal = vector.cross(p10, p20)
local area = vector.magnitude(normal)
local invarea = (area == 0) and 0 or 1 / area;
mesh.triangle_cone_p[i] = (p0.p + p1.p + p2.p) / 3
mesh.triangle_cone_n[i] = normal * invarea
i += 1
mesh_area += area
end
return mesh_area
end
function compute_tangent_space()
local checksum = 0
for i = 1,#mesh.indices,3 do
local a = mesh.vertices[mesh.indices[i]]
local b = mesh.vertices[mesh.indices[i + 1]]
local c = mesh.vertices[mesh.indices[i + 2]]
local vba = b.p - a.p
local vca = c.p - a.p
local uvba = b.uv - a.uv
local uvca = c.uv - a.uv
local r = 1.0 / (uvba.X * uvca.Y - uvca.X * uvba.Y);
local sdir = (uvca.Y * vba - uvba.Y * vca) * r
local tdir = (uvba.X * vca - uvca.X * vba) * r
a.t += sdir
b.t += sdir
c.t += sdir
a.b += tdir
b.b += tdir
c.b += tdir
end
for _,v in ipairs(mesh.vertices) do
local t = v.t
-- Gram-Schmidt orthogonalize
v.t = vector.normalize(t - v.n * vector.dot(v.n, t))
local ht = vector.dot(vector.cross(v.n, t), v.b)
v.h = ht < 0 and -1 or 1
checksum += v.t.X + v.h
end
return checksum
end
init_vertices()
init_indices()
calculate_normals()
compute_triangle_cones()
compute_tangent_space()
end
bench.runCode(test, "mesh-normal-vector")

View File

@ -0,0 +1,39 @@
local function prequire(name) local success, result = pcall(require, name); if success then return result end return nil end
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
function fma(a: vector, b: vector, c: vector)
return a * b + c
end
function approx(a: vector): vector
local r = vector.create(1, 1, 1)
local aa = a
r += aa * 0.123
aa *= a
r += aa * 0.123
aa *= a
r += aa * 0.123
aa *= a
r += aa * 0.123
aa *= a
r += aa * 0.123
aa *= a
r += aa * 0.123
return r
end
function test()
local A = vector.create(1, 2, 3)
local B = vector.create(4, 5, 6)
local C = vector.create(7, 8, 9)
local fma = fma
local approx = approx
for i=1,100000 do
fma(A, B, C)
approx(A)
end
end
bench.runCode(test, "vector math")

View File

@ -209,7 +209,7 @@ TEST_SUITE_BEGIN("AstQuery");
TEST_CASE_FIXTURE(Fixture, "last_argument_function_call_type")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
check(R"(
local function foo() return 2 end

View File

@ -22,6 +22,7 @@ LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
LUAU_FASTINT(LuauRecursionLimit)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
LUAU_FASTFLAG(LuauCompileVectorTypeInfo)
using namespace Luau;
@ -94,6 +95,8 @@ TEST_CASE("BytecodeIsStable")
// Note: these aren't strictly bound to specific bytecode versions, but must monotonically increase to keep backwards compat
CHECK(LBF_VECTOR == 54);
CHECK(LBF_TOSTRING == 63);
CHECK(LBF_BUFFER_WRITEF64 == 77);
CHECK(LBF_VECTOR_MAX == 88);
// Bytecode capture type (serialized & in-memory)
CHECK(LCT_UPVAL == 2); // bytecode v1
@ -8417,6 +8420,21 @@ end
);
}
TEST_CASE("BuiltinTypeVector")
{
ScopedFastFlag luauCompileVectorTypeInfo{FFlag::LuauCompileVectorTypeInfo, true};
CHECK_EQ(
"\n" + compileTypeTable(R"(
function myfunc(test: Instance, pos: vector)
end
)"),
R"(
0: function(userdata, vector)
)"
);
}
TEST_CASE("TypeAliasScoping")
{
CHECK_EQ(

View File

@ -36,6 +36,9 @@ LUAU_FASTFLAG(DebugLuauAbortingChecks)
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
LUAU_FASTFLAG(LuauNativeAttribute)
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
LUAU_FASTFLAG(LuauVectorDefinitions)
LUAU_DYNAMIC_FASTFLAG(LuauDebugInfoInvArgLeftovers)
LUAU_FASTFLAG(LuauVectorLibNativeCodegen)
static lua_CompileOptions defaultOptions()
{
@ -884,6 +887,30 @@ TEST_CASE("Vector")
);
}
TEST_CASE("VectorLibrary")
{
ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true};
lua_CompileOptions copts = defaultOptions();
SUBCASE("O0")
{
copts.optimizationLevel = 0;
}
SUBCASE("O1")
{
copts.optimizationLevel = 1;
}
SUBCASE("O2")
{
copts.optimizationLevel = 2;
}
runConformance(
"vector_library.lua", [](lua_State* L) {}, nullptr, nullptr, &copts
);
}
static void populateRTTI(lua_State* L, Luau::TypeId type)
{
if (auto p = Luau::get<Luau::PrimitiveType>(type))
@ -943,6 +970,10 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
lua_pushstring(L, "function");
}
else if (auto c = Luau::get<Luau::ClassType>(type))
{
lua_pushstring(L, c->name.c_str());
}
else
{
LUAU_ASSERT(!"Unknown type");
@ -951,6 +982,8 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
TEST_CASE("Types")
{
ScopedFastFlag luauVectorDefinitions{FFlag::LuauVectorDefinitions, true};
runConformance(
"types.lua",
[](lua_State* L)
@ -982,6 +1015,8 @@ TEST_CASE("DateTime")
TEST_CASE("Debug")
{
ScopedFastFlag luauDebugInfoInvArgLeftovers{DFFlag::LuauDebugInfoInvArgLeftovers, true};
runConformance("debug.lua");
}
@ -2196,9 +2231,7 @@ TEST_CASE("UserdataApi")
lua_getuserdatametatable(L, 50);
lua_setmetatable(L, -2);
void* ud8 = lua_newuserdatatagged(L, 16, 51);
lua_getuserdatametatable(L, 51);
lua_setmetatable(L, -2);
void* ud8 = lua_newuserdatataggedwithmetatable(L, 16, 51);
CHECK(luaL_checkudata(L, -2, "udata3") == ud7);
CHECK(luaL_checkudata(L, -1, "udata4") == ud8);

View File

@ -234,7 +234,7 @@ TEST_CASE_FIXTURE(DifferFixture, "right_cyclic_table_left_table_property_wrong")
TEST_CASE_FIXTURE(DifferFixture, "equal_table_two_cyclic_tables_are_not_different")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function id<a>(x: a): a
@ -1473,7 +1473,7 @@ TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "equal_metatable")
TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "metatable_normal")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local metaFoo = {

View File

@ -28,6 +28,8 @@ LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAGVARIABLE(DebugLuauForceAllNewSolverTests);
extern std::optional<unsigned> randomSeed; // tests/main.cpp
namespace Luau

View File

@ -26,6 +26,12 @@
#include <vector>
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
#define DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(line) \
ScopedFastFlag sff_##line{FFlag::LuauSolverV2, FFlag::DebugLuauForceAllNewSolverTests};
#define DOES_NOT_PASS_NEW_SOLVER_GUARD() DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(__LINE__)
namespace Luau
{

View File

@ -12,7 +12,8 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauRequireCyclesDontAlwaysReturnAny);
LUAU_FASTFLAG(DebugLuauFreezeArena);
LUAU_FASTFLAG(DebugLuauMagicTypes);
@ -313,6 +314,8 @@ TEST_CASE_FIXTURE(FrontendFixture, "nocheck_cycle_used_by_checked")
if (FFlag::LuauSolverV2)
CHECK_EQ("{ a: { hello: any }, b: { hello: any } }", toString(*cExports));
else if (FFlag::LuauRequireCyclesDontAlwaysReturnAny)
CHECK("{| a: any, b: any |}, {| a: {| hello: any |}, b: {| hello: any |} |}" == toString(*cExports));
else
CHECK_EQ("{| a: any, b: any |}", toString(*cExports));
}
@ -1375,7 +1378,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "checked_modules_have_the_correct_mode")
TEST_CASE_FIXTURE(FrontendFixture, "separate_caches_for_autocomplete")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
fileResolver.source["game/A"] = R"(
--!nonstrict

View File

@ -110,9 +110,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table")
// breaks this test. I'm not sure if that behaviour change is important or
// not, but it's tangental to the core purpose of this test.
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local Cyclic = {}
@ -283,7 +281,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
TEST_CASE_FIXTURE(Fixture, "clone_free_types")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
TypeArena arena;
TypeId freeTy = freshType(NotNull{&arena}, builtinTypes, nullptr);

View File

@ -16,7 +16,7 @@ TEST_SUITE_BEGIN("NonstrictModeTests");
TEST_CASE_FIXTURE(Fixture, "infer_nullary_function")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
function foo(x, y) end
@ -39,7 +39,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_nullary_function")
TEST_CASE_FIXTURE(Fixture, "infer_the_maximum_number_of_values_the_function_could_return")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
function getMinCardCountForWidth(width)
@ -103,7 +103,7 @@ TEST_CASE_FIXTURE(Fixture, "inconsistent_return_types_are_ok")
TEST_CASE_FIXTURE(Fixture, "locals_are_any_by_default")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
local m = 55
@ -130,7 +130,7 @@ TEST_CASE_FIXTURE(Fixture, "parameters_having_type_any_are_optional")
TEST_CASE_FIXTURE(Fixture, "local_tables_are_not_any")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
local T = {}
@ -148,7 +148,7 @@ TEST_CASE_FIXTURE(Fixture, "local_tables_are_not_any")
TEST_CASE_FIXTURE(Fixture, "offer_a_hint_if_you_use_a_dot_instead_of_a_colon")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
local T = {}
@ -163,7 +163,7 @@ TEST_CASE_FIXTURE(Fixture, "offer_a_hint_if_you_use_a_dot_instead_of_a_colon")
TEST_CASE_FIXTURE(Fixture, "table_props_are_any")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
local T = {}
@ -185,7 +185,7 @@ TEST_CASE_FIXTURE(Fixture, "table_props_are_any")
TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
local T = {
@ -261,7 +261,7 @@ TEST_CASE_FIXTURE(Fixture, "delay_function_does_not_require_its_argument_to_retu
TEST_CASE_FIXTURE(Fixture, "inconsistent_module_return_types_are_ok")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict

View File

@ -1191,9 +1191,7 @@ until false
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_local_function")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
try
{
@ -1228,9 +1226,7 @@ end
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_failsafe_earlier")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
try
{
@ -2628,9 +2624,7 @@ TEST_CASE_FIXTURE(Fixture, "recovery_of_parenthesized_expressions")
}
};
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
checkRecovery("function foo(a, b. c) return a + b end", "function foo(a, b) return a + b end", 1);
checkRecovery(
@ -2872,9 +2866,7 @@ TEST_CASE_FIXTURE(Fixture, "AstName_comparison")
TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
try
{

View File

@ -45,9 +45,7 @@ TEST_SUITE_BEGIN("RuntimeLimits");
TEST_CASE_FIXTURE(LimitFixture, "typescript_port_of_Result_type")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
constexpr const char* src = R"LUA(
--!strict

View File

@ -322,9 +322,7 @@ n3 [label="TableType 3"];
TEST_CASE_FIXTURE(Fixture, "free")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
Type type{TypeVariant{FreeType{TypeLevel{0, 0}}}};

View File

@ -45,7 +45,7 @@ TEST_CASE_FIXTURE(Fixture, "bound_types")
TEST_CASE_FIXTURE(Fixture, "free_types")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check("local a");
LUAU_REQUIRE_NO_ERRORS(result);
@ -166,7 +166,7 @@ TEST_CASE_FIXTURE(Fixture, "named_metatable")
TEST_CASE_FIXTURE(BuiltinsFixture, "named_metatable_toStringNamedFunction")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function createTbl(): NamedMetatable
@ -594,7 +594,7 @@ TEST_CASE_FIXTURE(Fixture, "toStringDetailed")
TEST_CASE_FIXTURE(Fixture, "toStringErrorPack")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function target(callback: nil) return callback(4, "hello") end

View File

@ -13,6 +13,9 @@ LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
LUAU_FASTFLAG(LuauUserTypeFunFixRegister)
LUAU_FASTFLAG(LuauUserTypeFunFixNoReadWrite)
LUAU_FASTFLAG(LuauUserTypeFunFixMetatable)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionResetState)
LUAU_FASTFLAG(LuauUserTypeFunNonstrict)
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
@ -987,6 +990,23 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_no_shared_state")
CHECK(toString(result.errors[1]) == R"(Type function instance bar<"x"> is uninhabited)");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_math_reset")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserDefinedTypeFunctionResetState{FFlag::LuauUserDefinedTypeFunctionResetState, true};
CheckResult result = check(R"(
type function foo(x)
return types.singleton(tostring(math.random(1, 100)))
end
local x: foo<'a'> = ('' :: any) :: foo<'b'>
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
@ -1230,4 +1250,52 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field")
CHECK(toString(result.errors[2]) == R"(Type pack '"table"' could not be converted into 'never'; at [0], "table" is not a subtype of never)");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_serialization")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
ScopedFastFlag luauUserTypeFunFixMetatable{FFlag::LuauUserTypeFunFixMetatable, true};
CheckResult result = check(R"(
type function makemttbl()
local metaprops = {
[types.singleton("ma")] = types.boolean
}
local mt = types.newtable(metaprops)
local props = {
[types.singleton("a")] = types.number
}
return types.newtable(props, nil, mt)
end
type function id(x)
return x
end
local a: number = {} :: id<makemttbl<>>
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) == R"(Type '{ @metatable { ma: boolean }, { a: number } }' could not be converted into 'number')");
}
TEST_CASE_FIXTURE(BuiltinsFixture, "nonstrict_mode")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
ScopedFastFlag luauUserTypeFunNonstrict{FFlag::LuauUserTypeFunNonstrict, true};
CheckResult result = check(R"(
--!nonstrict
type function foo() return types.string end
local a: foo<> = "a"
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_SUITE_END();

View File

@ -106,7 +106,7 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias")
TEST_CASE_FIXTURE(Fixture, "mismatched_generic_type_param")
{
// We erroneously report an extra error in this case when the new solver is enabled.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type T<A> = (A...) -> ()
@ -245,7 +245,7 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases")
{
// CLI-116108
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -355,9 +355,7 @@ TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_typ
// Check that recursive intersection type doesn't generate an OOM
TEST_CASE_FIXTURE(Fixture, "cli_38393_recursive_intersection_oom")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
}; // FIXME
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function _(l0:(t0)&((t0)&(((t0)&((t0)->()))->(typeof(_),typeof(# _)))),l39,...):any
@ -418,7 +416,7 @@ TEST_CASE_FIXTURE(Fixture, "corecursive_function_types")
TEST_CASE_FIXTURE(Fixture, "generic_param_remap")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
const std::string code = R"(
-- An example of a forwarded use of a type that has different type arguments than parameters
@ -544,7 +542,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_import_mutation")
TEST_CASE_FIXTURE(Fixture, "type_alias_local_mutation")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Cool = { a: number, b: string }
@ -565,7 +563,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_local_mutation")
TEST_CASE_FIXTURE(Fixture, "type_alias_local_rename")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Cool = { a: number, b: string }
@ -713,7 +711,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_ok")
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1")
{
// CLI-116108
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
-- OK because forwarded types are used with their parameters.
@ -727,7 +725,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1")
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_2")
{
// CLI-116108
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
-- Not OK because forwarded types are used with different types than their parameters.
@ -751,7 +749,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_ok")
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_not_ok")
{
// CLI-116108
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Tree1<T,U> = { data: T, children: {Tree2<U,T>} }
@ -876,7 +874,7 @@ TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_ok")
TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok")
{
// CLI-116108
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
-- this would be an infinite type if we allowed it
@ -889,7 +887,7 @@ TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok")
TEST_CASE_FIXTURE(Fixture, "report_shadowed_aliases")
{
// CLI-116110
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
// We allow a previous type alias to depend on a future type alias. That exact feature enables a confusing example, like the following snippet,
// which has the type alias FakeString point to the type alias `string` that which points to `number`.
@ -973,7 +971,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_locations")
TEST_CASE_FIXTURE(BuiltinsFixture, "dont_lose_track_of_PendingExpansionTypes_after_substitution")
{
// CLI-114134 - We need egraphs to properly simplify these types.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
fileResolver.source["game/ReactCurrentDispatcher"] = R"(
export type BasicStateAction<S> = ((S) -> S) | S

View File

@ -11,7 +11,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
LUAU_FASTFLAG(LuauTypestateBuiltins)
LUAU_FASTFLAG(LuauTypestateBuiltins2)
LUAU_FASTFLAG(LuauStringFormatArityFix)
TEST_SUITE_BEGIN("BuiltinTests");
@ -136,7 +136,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_predicate")
TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_bad_predicate")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -516,7 +516,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "buffer_is_a_type")
TEST_CASE_FIXTURE(BuiltinsFixture, "coroutine_resume_anything_goes")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function nifty(x, y)
@ -1133,7 +1133,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic")
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins)
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins2)
CHECK("Key 'b' not found in table '{ read a: number }'" == toString(result.errors[0]));
else if (FFlag::LuauSolverV2)
CHECK("Key 'b' not found in table '{ a: number }'" == toString(result.errors[0]));
@ -1141,7 +1141,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic")
CHECK_EQ("Key 'b' not found in table '{| a: number |}'", toString(result.errors[0]));
CHECK(Location({13, 18}, {13, 23}) == result.errors[0].location);
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins)
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins2)
{
CHECK_EQ("{ read a: number }", toString(requireTypeAtPosition({15, 19})));
CHECK_EQ("{ read b: string }", toString(requireTypeAtPosition({16, 19})));
@ -1178,13 +1178,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_does_not_retroactively_block_mu
LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauTypestateBuiltins)
if (FFlag::LuauTypestateBuiltins2)
{
CHECK_EQ("t1 | { read a: number, read q: string }", toString(requireType("t1")));
CHECK_EQ("{ a: number, q: string } | { read a: number, read q: string }", toString(requireType("t1"), {/*exhaustive */ true}));
// before the assignment, it's `t1`
CHECK_EQ("t1", toString(requireTypeAtPosition({3, 8})));
CHECK_EQ("{ a: number, q: string }", toString(requireTypeAtPosition({3, 8}), {/*exhaustive */ true}));
// after the assignment, it's read-only.
CHECK_EQ("{ read a: number, read q: string }", toString(requireTypeAtPosition({8, 18})));
CHECK_EQ("{ read a: number, read q: string }", toString(requireTypeAtPosition({8, 18}), {/*exhaustive */ true}));
}
CHECK_EQ("number", toString(requireType("a")));
@ -1208,10 +1208,46 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_no_generic_table")
end
)");
if (FFlag::LuauTypestateBuiltins)
if (FFlag::LuauTypestateBuiltins2)
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_on_metatable")
{
CheckResult result = check(R"(
--!strict
local meta = {
__index = function()
return "foo"
end
}
local myTable = setmetatable({}, meta)
table.freeze(myTable)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_errors_on_no_args")
{
CheckResult result = check(R"(
--!strict
table.freeze()
)");
// this does not error in the new solver without the typestate builtins functionality.
if (FFlag::LuauSolverV2 && !FFlag::LuauTypestateBuiltins2)
{
LUAU_REQUIRE_NO_ERRORS(result);
return;
}
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<CountMismatch>(result.errors[0]));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_errors_on_non_tables")
{
CheckResult result = check(R"(
@ -1220,7 +1256,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_errors_on_non_tables")
)");
// this does not error in the new solver without the typestate builtins functionality.
if (FFlag::LuauSolverV2 && !FFlag::LuauTypestateBuiltins)
if (FFlag::LuauSolverV2 && !FFlag::LuauTypestateBuiltins2)
{
LUAU_REQUIRE_NO_ERRORS(result);
return;
@ -1231,7 +1267,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_errors_on_non_tables")
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm);
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins)
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins2)
CHECK_EQ(toString(tm->wantedType), "table");
else
CHECK_EQ(toString(tm->wantedType), "{- -}");
@ -1241,7 +1277,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_errors_on_non_tables")
TEST_CASE_FIXTURE(BuiltinsFixture, "set_metatable_needs_arguments")
{
// In the new solver, nil can certainly be used where a generic is required, so all generic parameters are optional.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local a = {b=setmetatable}

View File

@ -128,7 +128,7 @@ TEST_CASE_FIXTURE(ClassFixture, "we_can_infer_that_a_parameter_must_be_a_particu
TEST_CASE_FIXTURE(ClassFixture, "we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function makeClone(o)
@ -235,7 +235,7 @@ TEST_CASE_FIXTURE(ClassFixture, "can_assign_to_prop_of_base_class_using_string")
TEST_CASE_FIXTURE(ClassFixture, "cannot_unify_class_instance_with_primitive")
{
// This is allowed in the new solver
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local v = Vector2.New(0, 5)
@ -472,7 +472,7 @@ Type 'number' could not be converted into 'string')";
TEST_CASE_FIXTURE(ClassFixture, "class_type_mismatch_with_name_conflict")
{
// CLI-116433
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local i = ChildClass.New()

View File

@ -443,6 +443,26 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_string_props")
CHECK_EQ(toString(requireType("y")), "string");
}
TEST_CASE_FIXTURE(Fixture, "class_definition_malformed_string")
{
unfreeze(frontend.globals.globalTypes);
LoadDefinitionFileResult result = frontend.loadDefinitionFile(
frontend.globals,
frontend.globals.globalScope,
R"(
declare class Foo
["a\0property"]: string
end
)",
"@test",
/* captureComments */ false
);
freeze(frontend.globals.globalTypes);
REQUIRE(!result.success);
REQUIRE_EQ(result.parseResult.errors.size(), 1);
CHECK_EQ(result.parseResult.errors[0].getMessage(), "String literal contains malformed escape sequence or \\0");
}
TEST_CASE_FIXTURE(Fixture, "class_definition_indexer")
{

View File

@ -16,9 +16,10 @@
using namespace Luau;
LUAU_FASTFLAG(LuauInstantiateInSubtyping);
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTINT(LuauTarjanChildLimit);
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(LuauRetrySubtypingWithoutHiddenPack)
TEST_SUITE_BEGIN("TypeInferFunctions");
@ -310,7 +311,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_return_type_from_selected_overload")
TEST_CASE_FIXTURE(Fixture, "too_many_arguments")
{
// This is not part of the new non-strict specification currently.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
@ -602,7 +603,7 @@ TEST_CASE_FIXTURE(Fixture, "duplicate_functions_allowed_in_nonstrict")
TEST_CASE_FIXTURE(Fixture, "duplicate_functions_with_different_signatures_not_allowed_in_nonstrict")
{
// This is not part of the spec for the new non-strict mode currently.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
@ -881,7 +882,7 @@ TEST_CASE_FIXTURE(Fixture, "another_indirect_function_case_where_it_is_ok_to_pro
TEST_CASE_FIXTURE(Fixture, "report_exiting_without_return_nonstrict")
{
// new non-strict mode spec does not include this error yet.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
@ -1016,7 +1017,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_lea
TEST_CASE_FIXTURE(Fixture, "too_many_return_values")
{
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -1040,7 +1041,7 @@ TEST_CASE_FIXTURE(Fixture, "too_many_return_values")
TEST_CASE_FIXTURE(Fixture, "too_many_return_values_in_parentheses")
{
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -1064,7 +1065,7 @@ TEST_CASE_FIXTURE(Fixture, "too_many_return_values_in_parentheses")
TEST_CASE_FIXTURE(Fixture, "too_many_return_values_no_function")
{
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -1233,7 +1234,7 @@ TEST_CASE_FIXTURE(Fixture, "return_type_by_overload")
TEST_CASE_FIXTURE(BuiltinsFixture, "infer_anonymous_function_arguments")
{
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
// Simple direct arg to arg propagation
CheckResult result = check(R"(
@ -1352,7 +1353,7 @@ f(function(x) return x * 2 end)
TEST_CASE_FIXTURE(BuiltinsFixture, "infer_generic_function_function_argument")
{
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function sum<a>(x: a, y: a, f: (a, a) -> a) return f(x, y) end
@ -1383,7 +1384,7 @@ local r = foldl(a, {s=0,c=0}, function(a, b) return {s = a.s + b, c = a.c + 1} e
TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded")
{
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function g1<T>(a: T, f: (T) -> T) return f(a) end
@ -1450,7 +1451,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "variadic_any_is_compatible_with_a_generic_Ty
TEST_CASE_FIXTURE(Fixture, "infer_anonymous_function_arguments_outside_call")
{
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Table = { x: number, y: number }
@ -1493,7 +1494,7 @@ end
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_arg_count")
{
// FIXME: CLI-116111 test disabled until type path stringification is improved
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type A = (number, number) -> string
@ -1516,7 +1517,7 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_arg")
{
// FIXME: CLI-116111 test disabled until type path stringification is improved
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type A = (number, number) -> string
@ -1540,7 +1541,7 @@ Type 'string' could not be converted into 'number')";
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret_count")
{
// FIXME: CLI-116111 test disabled until type path stringification is improved
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type A = (number, number) -> (number)
@ -1563,7 +1564,7 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret")
{
// FIXME: CLI-116111 test disabled until type path stringification is improved
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type A = (number, number) -> string
@ -1587,7 +1588,7 @@ Type 'string' could not be converted into 'number')";
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret_mult")
{
// FIXME: CLI-116111 test disabled until type path stringification is improved
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type A = (number, number) -> (number, string)
@ -1718,7 +1719,7 @@ TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_th
{
// This test regresses in the new solver, but is sort of nonsensical insofar as `foo` is known to be `nil`, so it's "right" to not be able to call
// it.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local foo
@ -1787,7 +1788,7 @@ TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments")
TEST_CASE_FIXTURE(Fixture, "function_statement_sealed_table_assignment_through_indexer")
{
// FIXME: CLI-116122 bug where `t:b` does not check against the type from the indexer annotation on `t`.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local t: {[string]: () -> number} = {}
@ -1832,7 +1833,7 @@ TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic_generic")
{
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function test(a: number, b: string, ...)
@ -1860,7 +1861,7 @@ wrapper(test)
TEST_CASE_FIXTURE(BuiltinsFixture, "too_few_arguments_variadic_generic2")
{
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function test(a: number, b: string, ...)
@ -2014,7 +2015,7 @@ u.b().foo()
TEST_CASE_FIXTURE(BuiltinsFixture, "improved_function_arg_mismatch_error_nonstrict")
{
// This behavior is not part of the current specification of the new type solver.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
@ -2029,7 +2030,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "improved_function_arg_mismatch_error_nonstri
TEST_CASE_FIXTURE(Fixture, "luau_subtyping_is_np_hard")
{
// The case that _should_ succeed here (`z = x`) does not currently in the new solver.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -2994,4 +2995,25 @@ local u,v = id(3), id(id(44))
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "hidden_variadics_should_not_break_subtyping")
{
// Only applies to new solver.
ScopedFastFlag sff{FFlag::LuauRetrySubtypingWithoutHiddenPack, true};
CheckResult result = check(R"(
--!strict
type FooType = {
SetValue: (Value: number) -> ()
}
local Foo: FooType = {
SetValue = function(Value: number)
end
}
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_SUITE_END();

View File

@ -143,7 +143,7 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes")
TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local t: { m: (number)->number } = { m = function(x:number) return x+1 end }
@ -261,7 +261,7 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_errors")
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_old_solver")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type T = { id: <a>(a) -> a }
@ -287,7 +287,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_new_solver")
TEST_CASE_FIXTURE(Fixture, "generic_factories")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type T<a> = { id: (a) -> a }
@ -310,7 +310,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_factories")
TEST_CASE_FIXTURE(Fixture, "factories_of_generics")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type T = { id: <a>(a) -> a }
@ -775,7 +775,7 @@ return exports
TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names_old_solver")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function f<T, U...>(a: T, ...: U...) end
@ -794,7 +794,7 @@ TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names_old_solver")
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_types")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type C = () -> ()
@ -811,7 +811,7 @@ local d: D = c
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_pack")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type C = () -> ()
@ -887,7 +887,7 @@ Type 'number' could not be converted into 'string' in an invariant context)";
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification1")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -907,7 +907,7 @@ local TheDispatcher: Dispatcher = {
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification2")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -927,7 +927,7 @@ local TheDispatcher: Dispatcher = {
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification3")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -947,7 +947,7 @@ local TheDispatcher: Dispatcher = {
TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_few")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function test(a: number)
@ -966,7 +966,7 @@ wrapper(test)
TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_many")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function test2(a: number, b: string)
@ -1463,7 +1463,7 @@ TEST_CASE_FIXTURE(Fixture, "no_extra_quantification_for_generic_functions")
TEST_CASE_FIXTURE(Fixture, "do_not_always_instantiate_generic_intersection_types")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -1498,8 +1498,9 @@ end
TEST_CASE_FIXTURE(BuiltinsFixture, "higher_rank_polymorphism_should_not_accept_instantiated_arguments")
{
DOES_NOT_PASS_NEW_SOLVER_GUARD();
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, false},
{FFlag::LuauInstantiateInSubtyping, true},
};
@ -1579,7 +1580,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_implicit_explicit_name_clash")
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_type_functions_work_in_subtyping")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
if (!FFlag::LuauSolverV2)
return;

View File

@ -332,9 +332,9 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed")
TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
{
ScopedFastFlag dcr{
FFlag::LuauSolverV2, false
}; // CLI-116476 Subtyping between type alias and an equivalent but not named type isn't working.
// CLI-116476 Subtyping between type alias and an equivalent but not named type isn't working.
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type X = { x: (number) -> number }
type Y = { y: (string) -> string }
@ -372,7 +372,7 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "table_write_sealed_indirect")
{
ScopedFastFlag dcr{FFlag::LuauSolverV2, false}; // CLI-
DOES_NOT_PASS_NEW_SOLVER_GUARD();
// After normalization, previous 'table_intersection_write_sealed_indirect' is identical to this one
CheckResult result = check(R"(
type XY = { x: (number) -> number, y: (string) -> string }
@ -581,9 +581,9 @@ could not be converted into
TEST_CASE_FIXTURE(Fixture, "union_saturate_overloaded_functions")
{
ScopedFastFlag dcr{
FFlag::LuauSolverV2, false
}; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
// CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(x: ((number) -> number) & ((string) -> string))
local y : ((number | string) -> (number | string)) = x -- OK
@ -811,9 +811,9 @@ could not be converted into
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_result")
{
ScopedFastFlag dcr{
FFlag::LuauSolverV2, false
}; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
// CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f<a...,b...>()
function g(x : ((number) -> number) & ((nil) -> unknown))
@ -833,9 +833,9 @@ could not be converted into
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_arguments")
{
ScopedFastFlag dcr{
FFlag::LuauSolverV2, false
}; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
// CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f<a...,b...>()
function g(x : ((number) -> number?) & ((unknown) -> string?))
@ -939,9 +939,9 @@ could not be converted into
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_overlapping_results_and_variadics")
{
ScopedFastFlag dcr{
FFlag::LuauSolverV2, false
}; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
// CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(x : ((string?) -> (string | number)) & ((number?) -> ...number))
local y : ((nil) -> (number, number?)) = x -- OK

View File

@ -152,7 +152,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop")
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next")
{
// CLI-116494 The generics K and V are leaking out of the next() function somehow.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local n
@ -270,7 +270,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error")
TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function")
{
// We report a spuriouus duplicate error here.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local bad_iter = 5
@ -287,7 +287,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function")
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_factory_not_returning_the_right_amount_of_values")
{
// Spurious duplicate errors
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function hasDivisors(value: number, table)
@ -339,7 +339,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_factory_not_returning_t
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_iterator_requiring_args_but_none_given")
{
// CLI-116496
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function prime_iter(state, index)
@ -757,7 +757,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_basic")
TEST_CASE_FIXTURE(Fixture, "loop_iter_trailing_nil")
{
// CLI-116498 Sometimes you can iterate over tables with no indexers.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local t: {string} = {}
@ -774,9 +774,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_trailing_nil")
TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer_strict")
{
// CLI-116498 Sometimes you can iterate over tables with no indexers.
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local t = {}
@ -1082,7 +1080,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_on_never_gives_never")
TEST_CASE_FIXTURE(BuiltinsFixture, "iterate_over_properties")
{
// CLI-116498 - Sometimes you can iterate over tables with no indexer.
ScopedFastFlag sff0{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function f()

View File

@ -11,8 +11,9 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauRequireCyclesDontAlwaysReturnAny)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTypestateBuiltins)
LUAU_FASTFLAG(LuauTypestateBuiltins2)
using namespace Luau;
@ -184,7 +185,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cross_module_table_freeze")
ModulePtr b = frontend.moduleResolver.getModule("game/B");
REQUIRE(b != nullptr);
// confirm that no cross-module mutation happened here!
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins)
if (FFlag::LuauSolverV2 && FFlag::LuauTypestateBuiltins2)
CHECK(toString(b->returnType) == "{ read a: number }");
else if (FFlag::LuauSolverV2)
CHECK(toString(b->returnType) == "{ a: number }");
@ -736,4 +737,45 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "spooky_blocked_type_laundered_by_bound_type"
)"));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "cycles_dont_make_everything_any")
{
ScopedFastFlag sff{FFlag::LuauRequireCyclesDontAlwaysReturnAny, true};
fileResolver.source["game/A"] = R"(
--!strict
local module = {}
function module.foo()
return 2
end
function module.bar()
local m = require(game.B)
return m.foo() + 1
end
return module
)";
fileResolver.source["game/B"] = R"(
--!strict
local module = {}
function module.foo()
return 2
end
function module.bar()
local m = require(game.A)
return m.foo() + 1
end
return module
)";
frontend.check("game/A");
CHECK("module" == toString(frontend.moduleResolver.getModule("game/B")->returnType));
}
TEST_SUITE_END();

View File

@ -19,7 +19,7 @@ TEST_SUITE_BEGIN("TypeInferOOP");
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon")
{
// CLI-116571 method calls are missing arity checking?
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local someTable = {}
@ -37,7 +37,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defi
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2")
{
// CLI-116571 method calls are missing arity checking?
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local someTable = {}

View File

@ -17,6 +17,7 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauMetatableFollow)
TEST_SUITE_BEGIN("TypeInferOperators");
@ -630,7 +631,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error")
TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_len_error")
{
// CLI-116463
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -884,7 +885,7 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato
TEST_CASE_FIXTURE(Fixture, "cli_38355_recursive_union")
{
// There's an extra spurious warning here when the new solver is enabled.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -1425,7 +1426,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_is_array_simplified")
TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_is_array")
{
// CLI-116480 Subtyping bug: table should probably be a subtype of {[unknown]: unknown}
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -1611,4 +1612,28 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "compound_operator_on_upvalue")
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_operator_follow")
{
ScopedFastFlag luauMetatableFollow{FFlag::LuauMetatableFollow, true};
CheckResult result = check(R"(
local t1 = {}
local t2 = {}
local mt = {}
mt.__eq = function(a, b)
return false
end
setmetatable(t1, mt)
setmetatable(t2, mt)
if t1 == t2 then
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_SUITE_END();

View File

@ -73,7 +73,7 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
TEST_CASE_FIXTURE(BuiltinsFixture, "luau-polyfill.Array.filter")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
// This test exercises the fact that we should reduce sealed/unsealed/free tables
// res is a unsealed table with type {((T & ~nil)?) & any}
@ -172,7 +172,7 @@ TEST_CASE_FIXTURE(Fixture, "it_should_be_agnostic_of_actual_size")
// For now, infer it as just a free table.
TEST_CASE_FIXTURE(BuiltinsFixture, "setmetatable_constrains_free_type_into_free_table")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local a = {}
@ -192,7 +192,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "setmetatable_constrains_free_type_into_free_
// Luau currently doesn't yet know how to allow assignments when the binding was refined.
TEST_CASE_FIXTURE(Fixture, "while_body_are_also_refined")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Node<T> = { value: T, child: Node<T>? }
@ -217,7 +217,7 @@ TEST_CASE_FIXTURE(Fixture, "while_body_are_also_refined")
// We should be type checking the metamethod at the call site of setmetatable.
TEST_CASE_FIXTURE(BuiltinsFixture, "error_on_eq_metamethod_returning_a_type_other_than_boolean")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local tab = {a = 1}
@ -390,11 +390,9 @@ TEST_CASE_FIXTURE(Fixture, "specialization_binds_with_prototypes_too_early")
TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_type_pack")
{
ScopedFastFlag sff[] = {
// I'm not sure why this is broken without DCR, but it seems to be fixed
// when DCR is enabled.
{FFlag::LuauSolverV2, false},
};
// I'm not sure why this is broken without DCR, but it seems to be fixed
// when DCR is enabled.
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function f() return end
@ -517,7 +515,7 @@ TEST_CASE_FIXTURE(Fixture, "dcr_can_partially_dispatch_a_constraint")
TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
TypeArena arena;
TypeId nilType = builtinTypes->nilType;
@ -553,7 +551,7 @@ TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together")
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_zero_iterators")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function no_iter() end
@ -847,7 +845,7 @@ Type 'number?' could not be converted into 'number' in an invariant context)";
TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_with_a_singleton_argument")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function foo(t, x)
@ -921,7 +919,7 @@ TEST_CASE_FIXTURE(Fixture, "expected_type_should_be_a_helpful_deduction_guide_fo
TEST_CASE_FIXTURE(Fixture, "floating_generics_should_not_be_allowed")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local assign : <T, U, V, W>(target: T, source0: U?, source1: V?, source2: W?, ...any) -> T & U & V & W = (nil :: any)
@ -945,7 +943,7 @@ TEST_CASE_FIXTURE(Fixture, "floating_generics_should_not_be_allowed")
TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
TypeArena arena;
TypeId nilType = builtinTypes->nilType;
@ -992,7 +990,7 @@ TEST_CASE_FIXTURE(Fixture, "unify_more_complex_unions_that_include_nil")
TEST_CASE_FIXTURE(Fixture, "optional_class_instances_are_invariant_old_solver")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
createSomeClasses(&frontend);
@ -1076,7 +1074,7 @@ end
TEST_CASE_FIXTURE(BuiltinsFixture, "table_unification_infinite_recursion")
{
// The new solver doesn't recurse as heavily in this situation.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
#if defined(_NOOPT) || defined(_DEBUG)
ScopedFastInt LuauTypeInferRecursionLimit{FInt::LuauTypeInferRecursionLimit, 100};

View File

@ -1375,7 +1375,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "discriminate_from_isa_of_x")
TEST_CASE_FIXTURE(RefinementClassFixture, "typeguard_cast_free_table_to_vector")
{
// CLI-115286 - Refining via type(x) == 'vector' does not work in the new solver
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function f(vec)
@ -1569,7 +1569,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "isa_type_refinement_must_be_known_ahe
{
// CLI-115087 - The new solver does not consistently combine tables with
// class types when they appear in the upper bounds of a free type.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function f(x): Instance

View File

@ -153,7 +153,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons")
TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons_mismatch")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(g: ((true, string) -> ()) & ((false, number) -> ()))
@ -463,7 +463,7 @@ local a: Animal = if true then { tag = 'cat', catfood = 'something' } else { tag
TEST_CASE_FIXTURE(Fixture, "widen_the_supertype_if_it_is_free_and_subtype_has_singleton")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function foo(f, x)
@ -483,7 +483,7 @@ TEST_CASE_FIXTURE(Fixture, "widen_the_supertype_if_it_is_free_and_subtype_has_si
TEST_CASE_FIXTURE(Fixture, "return_type_of_f_is_not_widened")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function foo(f, x): "hello"? -- anyone there?

View File

@ -19,6 +19,7 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections)
LUAU_FASTFLAG(LuauRetrySubtypingWithoutHiddenPack)
LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease)
@ -318,7 +319,7 @@ TEST_CASE_FIXTURE(Fixture, "call_method_with_explicit_self_argument")
TEST_CASE_FIXTURE(Fixture, "used_dot_instead_of_colon")
{
// CLI-114792 Dot vs colon warnings aren't in the new solver yet.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local T = {}
@ -371,7 +372,7 @@ TEST_CASE_FIXTURE(Fixture, "used_dot_instead_of_colon_but_correctly")
TEST_CASE_FIXTURE(Fixture, "used_colon_instead_of_dot")
{
// CLI-114792 Dot vs colon warnings aren't in the new solver yet.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local T = {}
@ -396,7 +397,7 @@ TEST_CASE_FIXTURE(Fixture, "used_colon_instead_of_dot")
TEST_CASE_FIXTURE(Fixture, "open_table_unification_2")
{
// CLI-114792 We don't report MissingProperties in many places where the old solver does.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local a = {}
@ -536,7 +537,7 @@ TEST_CASE_FIXTURE(Fixture, "table_param_width_subtyping_3")
TEST_CASE_FIXTURE(Fixture, "table_unification_4")
{
// CLI-114134 - Use egraphs to simplify types better.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function foo(o)
@ -567,7 +568,7 @@ TEST_CASE_FIXTURE(Fixture, "ok_to_add_property_to_free_table")
TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_assignment")
{
// CLI-114872
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -586,7 +587,7 @@ TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_assignmen
TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_function_call")
{
// CLI-114873
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -811,7 +812,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_value_property_in_literal")
TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_its_variable_type_and_unifiable")
{
// This code is totally different in the new solver. We instead create a new type state for t2.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local t1: { [string]: string } = {}
@ -893,7 +894,7 @@ TEST_CASE_FIXTURE(Fixture, "sealed_table_value_can_infer_an_indexer")
TEST_CASE_FIXTURE(Fixture, "array_factory_function")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function empty() return {} end
@ -930,7 +931,7 @@ TEST_CASE_FIXTURE(Fixture, "indexer_on_sealed_table_must_unify_with_free_table")
// CLI-114134 What should be happening here is that the type of `t` should
// be reduced from `{number} & {string}` to `never`, but that's not
// happening.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function F(t): {number}
@ -993,7 +994,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_from_a_table_should_prefer_properti
TEST_CASE_FIXTURE(Fixture, "any_when_indexing_into_an_unsealed_table_with_no_indexer_in_nonstrict_mode")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
@ -1145,7 +1146,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_inferred")
TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_both_ways")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type VectorMt = { __add: (Vector, number) -> Vector }
@ -1523,7 +1524,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "found_multiple_like_keys")
TEST_CASE_FIXTURE(BuiltinsFixture, "dont_suggest_exact_match_keys")
{
// CLI-114977 Unsealed table writes don't account for order properly
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local t = {}
@ -1566,7 +1567,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_returns_pointer_to_metatable")
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_mismatch_should_fail")
{
// This test is invalid because we now create a new type state for t1 at the assignment.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local t1 = {x = 1}
@ -1610,7 +1611,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "property_lookup_through_tabletypevar_metatab
TEST_CASE_FIXTURE(BuiltinsFixture, "missing_metatable_for_sealed_tables_do_not_get_inferred")
{
// This test is invalid because we now create a new type state for t at the assignment.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local t = {x = 1}
@ -1665,7 +1666,7 @@ TEST_CASE_FIXTURE(Fixture, "right_table_missing_key")
TEST_CASE_FIXTURE(Fixture, "right_table_missing_key2")
{
// CLI-114792 We don't report MissingProperties
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(t: {}): { [string]: string, a: string }
@ -1927,7 +1928,7 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_on_massive_table_is_cut_short")
TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr")
{
// CLI-100076 Assigning nil to an indexer should always succeed
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function f(): { [string]: number }
@ -2096,7 +2097,7 @@ local Test: {Table} = {
TEST_CASE_FIXTURE(Fixture, "common_table_element_general")
{
// CLI-115275 - Bidirectional inference does not always propagate indexer types into the expression
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Table = {
@ -2218,7 +2219,7 @@ foo({
TEST_CASE_FIXTURE(Fixture, "common_table_element_union_in_call_tail")
{
// CLI-115239 - Bidirectional checking does not work for __call metamethods
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Foo = {x: number | string}
@ -2265,7 +2266,16 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table
local c : string = t.m("hi")
)");
if (FFlag::LuauSolverV2)
if (FFlag::LuauSolverV2 && FFlag::LuauRetrySubtypingWithoutHiddenPack)
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<ExplicitFunctionAnnotationRecommended>(result.errors[0]));
// This is not actually the expected behavior, but the typemismatch we were seeing before was for the wrong reason.
// The behavior of this test is just regressed generally in the new solver, and will need to be consciously addressed.
}
else if (FFlag::LuauSolverV2)
{
LUAU_REQUIRE_ERROR_COUNT(2, result);
@ -2504,7 +2514,7 @@ Type 'number' could not be converted into 'string' in an invariant context)";
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table")
{
// Table properties like HasSuper.p must be invariant. The new solver rightly rejects this program.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -2554,7 +2564,7 @@ Table type '{ x: number, y: number }' not compatible with type 'Super' because t
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
{
// CLI-114791 Bidirectional inference should be able to cause the inference engine to forget that a table literal has some property
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -2572,7 +2582,7 @@ TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_metatable_type_call")
{
// CLI-114782
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local b
@ -2827,7 +2837,7 @@ TEST_CASE_FIXTURE(Fixture, "table_length")
TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_indexer")
{
// CLI-100076 - Assigning a table key to `nil` in the presence of an indexer should always be permitted
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check("local a = {} a[0] = 7 a[0] = nil");
LUAU_REQUIRE_ERROR_COUNT(0, result);
@ -3296,7 +3306,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_generic")
TEST_CASE_FIXTURE(BuiltinsFixture, "table_simple_call")
{
// The new solver can see that this function is safe to oversaturate.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local a = setmetatable({ x = 2 }, {
@ -3589,7 +3599,7 @@ local b = a.x
TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shape_type")
{
// CLI-115087 The new solver cannot infer that a table-like type is actually string
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function f(s)
@ -3678,7 +3688,7 @@ Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutel
TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compatible")
{
// CLI-115087 The new solver cannot infer that a table-like type is actually string
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function f(s): string
@ -3733,7 +3743,7 @@ Table type 'typeof(string)' not compatible with type 't1 where t1 = {+ absolutel
TEST_CASE_FIXTURE(BuiltinsFixture, "a_free_shape_can_turn_into_a_scalar_directly")
{
// We need egraphs to simplify the type of `out` here. CLI-114134
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function stringByteList(str)
@ -4231,9 +4241,7 @@ TEST_CASE_FIXTURE(Fixture, "identify_all_problematic_table_fields")
TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type W = {read x: number}
@ -4370,7 +4378,7 @@ TEST_CASE_FIXTURE(Fixture, "write_annotations_are_unsupported_even_with_the_new_
TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported")
{
ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, false}};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type W = {read x: number}
@ -4394,7 +4402,7 @@ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported
TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported")
{
ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, false}};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type T = {read [string]: number}

View File

@ -146,9 +146,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_locals_via_assignment_from_its_call_site")
TEST_CASE_FIXTURE(Fixture, "infer_in_nocheck_mode")
{
ScopedFastFlag sff[]{
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nocheck
@ -225,7 +223,7 @@ TEST_CASE_FIXTURE(Fixture, "statements_are_topologically_sorted")
TEST_CASE_FIXTURE(Fixture, "unify_nearly_identical_recursive_types")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local o
@ -266,7 +264,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "weird_case")
TEST_CASE_FIXTURE(Fixture, "dont_ice_when_failing_the_occurs_check")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -385,7 +383,7 @@ TEST_CASE_FIXTURE(Fixture, "exponential_blowup_from_copying_types")
// checker. We also want it to somewhat match up with production values, so we push up the parser recursion limit a little bit instead.
TEST_CASE_FIXTURE(Fixture, "check_type_infer_recursion_count")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
#if defined(LUAU_ENABLE_ASAN)
int limit = 250;
@ -443,7 +441,7 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit")
TEST_CASE_FIXTURE(Fixture, "globals")
{
// The new solver does not permit assignments to globals like this.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
@ -457,7 +455,7 @@ TEST_CASE_FIXTURE(Fixture, "globals")
TEST_CASE_FIXTURE(Fixture, "globals2")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!nonstrict
@ -507,7 +505,7 @@ TEST_CASE_FIXTURE(Fixture, "correctly_scope_locals_do")
TEST_CASE_FIXTURE(Fixture, "checking_should_not_ice")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CHECK_NOTHROW(check(R"(
--!nonstrict
@ -601,7 +599,7 @@ TEST_CASE_FIXTURE(Fixture, "tc_after_error_recovery_no_assert")
TEST_CASE_FIXTURE(BuiltinsFixture, "tc_after_error_recovery_no_replacement_name_in_error")
{
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -623,7 +621,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tc_after_error_recovery_no_replacement_name_
}
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -698,7 +696,7 @@ TEST_CASE_FIXTURE(Fixture, "cli_39932_use_unifier_in_ensure_methods")
TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstStatError")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
foo
@ -709,7 +707,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstStatError")
TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstExprError")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local a = foo:
@ -1100,7 +1098,7 @@ end
TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
--!strict
@ -1223,7 +1221,7 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_cache_limit_normalizer")
TEST_CASE_FIXTURE(Fixture, "follow_on_new_types_in_substitution")
{
// CLI-114134
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local obj = {}
@ -1564,7 +1562,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "lti_must_record_contributing_locations")
*/
TEST_CASE_FIXTURE(BuiltinsFixture, "be_sure_to_use_active_txnlog_when_evaluating_a_variadic_overload")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local function concat<T>(target: {T}, ...: {T} | T): {T}

View File

@ -17,7 +17,7 @@ LUAU_FASTFLAG(LuauUnifierRecursionOnRestart);
struct TryUnifyFixture : Fixture
{
// Cannot use `TryUnifyFixture` under DCR.
ScopedFastFlag noDcr{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
TypeArena arena;
ScopePtr globalScope{new Scope{arena.addTypePack({TypeId{}})}};
@ -154,7 +154,7 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_intersection_sub_anything")
TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_never")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(arg : { prop : string & number }) : never
@ -166,7 +166,7 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_never")
TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_anything")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(arg : { prop : string & number }) : boolean
@ -178,7 +178,7 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_anything")
TEST_CASE_FIXTURE(Fixture, "members_of_failed_typepack_unification_are_unified_with_errorType")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(arg: number) end
@ -195,7 +195,7 @@ TEST_CASE_FIXTURE(Fixture, "members_of_failed_typepack_unification_are_unified_w
TEST_CASE_FIXTURE(Fixture, "result_of_failed_typepack_unification_is_constrained")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(arg: number) return arg end

View File

@ -787,7 +787,7 @@ local d: Y<number, string, ...boolean, ...() -> ()>
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Y<T = T> = { a: T }
@ -811,7 +811,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors2")
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors3")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Y<T = string, U... = ...string> = { a: (T) -> U... }
@ -824,7 +824,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors3")
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors4")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type Packed<T> = (T) -> T
@ -1065,7 +1065,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "detect_cyclic_typepacks2")
TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function foo(...: string): number

View File

@ -36,7 +36,7 @@ TEST_CASE_FIXTURE(Fixture, "return_types_can_be_disjoint")
{
// CLI-114134 We need egraphs to consistently reduce the cyclic union
// introduced by the increment here.
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local count = 0
@ -122,7 +122,7 @@ TEST_CASE_FIXTURE(Fixture, "optional_arguments")
TEST_CASE_FIXTURE(Fixture, "optional_arguments_table")
{
// CLI-115588 - Bidirectional inference does not happen for assignments
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local a:{a:string, b:string?}
@ -478,7 +478,7 @@ end
TEST_CASE_FIXTURE(Fixture, "unify_unsealed_table_union_check")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
local x = { x = 3 }
@ -653,7 +653,7 @@ TEST_CASE_FIXTURE(Fixture, "indexing_into_a_cyclic_union_doesnt_crash")
TEST_CASE_FIXTURE(BuiltinsFixture, "table_union_write_indirect")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
type A = { x: number, y: (number) -> string } | { z: number, y: (number) -> string }
@ -729,7 +729,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_generic_typepack_functions")
TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generics")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f<a,b>()
@ -749,7 +749,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generics")
TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generic_typepacks")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f<a...>()
@ -770,7 +770,7 @@ could not be converted into
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_arities")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(x : (number) -> number?)
@ -789,7 +789,7 @@ could not be converted into
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_arities")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(x : () -> (number | string))
@ -808,7 +808,7 @@ could not be converted into
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_variadics")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(x : (...nil) -> (...number?))
@ -854,7 +854,7 @@ could not be converted into
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_variadics")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(x : () -> (number?, ...number))
@ -922,7 +922,7 @@ TEST_CASE_FIXTURE(Fixture, "union_table_any_property")
TEST_CASE_FIXTURE(Fixture, "union_function_any_args")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f(sup : ((...any) -> (...any))?, sub : ((number) -> (...any)))
@ -946,7 +946,7 @@ TEST_CASE_FIXTURE(Fixture, "optional_any")
TEST_CASE_FIXTURE(Fixture, "generic_function_with_optional_arg")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"(
function f<T>(x : T?) : {T}

View File

@ -538,9 +538,7 @@ TEST_SUITE_BEGIN("TypePathToString");
TEST_CASE("field")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CHECK(toString(PathBuilder().prop("foo").build()) == R"(["foo"])");
}
@ -567,9 +565,7 @@ TEST_CASE("empty_path")
TEST_CASE("prop")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, false},
};
DOES_NOT_PASS_NEW_SOLVER_GUARD();
Path p = PathBuilder().prop("foo").build();
CHECK(p == Path(TypePath::Property{"foo"}));

View File

@ -138,4 +138,17 @@ end)
coroutine.resume(wrapped2)
local wrapped3 = coroutine.create(function()
local thread = coroutine.create(function(target)
for i = 1, 100 do pcall(debug.info, target, 0, "?f") end
return 123
end)
local success, res = coroutine.resume(thread, coroutine.running())
assert(success)
assert(res == 123)
end)
coroutine.resume(wrapped3)
return 'OK'

View File

@ -0,0 +1,159 @@
-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
print('testing vector library')
function ecall(fn, ...)
local ok, err = pcall(fn, ...)
assert(not ok)
return err:sub((err:find(": ") or -1) + 2, #err)
end
-- make sure we cover both builtin and C impl
assert(vector.create(1, 2, 4) == vector.create("1", "2", "4"))
-- testing 'dot' with error handling and different call kinds to mostly check details in the codegen
assert(vector.dot(vector.create(1, 2, 4), vector.create(5, 6, 7)) == 45)
assert(ecall(function() vector.dot(vector.create(1, 2, 4)) end) == "missing argument #2 to 'dot' (vector expected)")
assert(ecall(function() vector.dot(vector.create(1, 2, 4), 2) end) == "invalid argument #2 to 'dot' (vector expected, got number)")
local function doDot1(a: vector, b)
return vector.dot(a, b)
end
local function doDot2(a: vector, b)
return (vector.dot(a, b))
end
local v124 = vector.create(1, 2, 4)
assert(doDot1(v124, vector.create(5, 6, 7)) == 45)
assert(doDot2(v124, vector.create(5, 6, 7)) == 45)
assert(ecall(function() doDot1(v124, "a") end) == "invalid argument #2 to 'dot' (vector expected, got string)")
assert(ecall(function() doDot2(v124, "a") end) == "invalid argument #2 to 'dot' (vector expected, got string)")
assert(select("#", doDot1(v124, vector.create(5, 6, 7))) == 1)
assert(select("#", doDot2(v124, vector.create(5, 6, 7))) == 1)
-- 'cross' tests and next ones will only test basic results
assert(vector.cross(vector.create(1, 0, 0), vector.create(0, 1, 0)) == vector.create(0, 0, 1))
assert(vector.cross(vector.create(0, 1, 0), vector.create(1, 0, 0)) == vector.create(0, 0, -1))
assert(select("#", vector.cross(vector.zero, vector.one)) == 1)
-- 'normalize'
assert(vector.normalize(vector.create(0.5, 0, 0)) == vector.create(1, 0, 0))
assert(select("#", vector.normalize(vector.one)) == 1)
-- 'magnitude'
assert(vector.magnitude(vector.create(1, 2, 2)) == 3)
assert(select("#", vector.magnitude(vector.one)) == 1)
-- 'abs'
assert(vector.abs(-vector.one) == vector.one)
assert(vector.abs(vector.create(math.huge, 0, 0)).x == math.abs(math.huge))
assert(vector.abs(vector.create(0/0, 0, 0)).x ~= 0/0)
assert(select("#", vector.abs(vector.one)) == 1)
-- 'floor'
assert(vector.floor(vector.create(1, 2, 3)) == vector.create(1, 2, 3))
assert(vector.floor(vector.create(1.5, 2.4, 3)) == vector.create(1, 2, 3))
assert(vector.floor(vector.create(-1.5, -2.4, -3)) == vector.create(-2, -3, -3))
assert(select("#", vector.floor(vector.one)) == 1)
-- 'ceil'
assert(vector.ceil(vector.create(1, 2, 3)) == vector.create(1, 2, 3))
assert(vector.ceil(vector.create(1.5, 2.4, 3)) == vector.create(2, 3, 3))
assert(vector.ceil(vector.create(-1.5, -2.4, -3)) == vector.create(-1, -2, -3))
assert(select("#", vector.ceil(vector.one)) == 1)
-- 'sign'
assert(vector.sign(vector.zero) == vector.zero)
assert(vector.sign(vector.one) == vector.one)
assert(vector.sign(vector.create(-10, 0, 10)) == vector.create(-1, 0, 1))
assert(vector.sign(vector.create(math.huge, 0, -math.huge)) == vector.create(1, 0, -1))
-- negative zero and nan are consistent with math library, even if implementation defined
assert(vector.sign(vector.create(-0, 0, 0)).x == math.sign(-0))
assert(vector.sign(vector.create(0/0, 0, 0)).x == math.sign(0/0))
assert(select("#", vector.sign(vector.one)) == 1)
-- 'angle'
assert(math.abs(vector.angle(vector.create(1, 2, 3), vector.create(4, 5, 6)) - 0.2257259) < 0.00001)
assert(select("#", vector.angle(vector.zero, vector.one)) == 1)
assert(select("#", vector.angle(vector.one, -vector.one, vector.zero)) == 1)
do
-- random (non-unit) vectors
local rand = {
vector.create(-1.05, -0.04, 1.06),
vector.create(-0.75, 1.71, 1.29),
vector.create(1.94, 0.76, -0.93),
vector.create(0.02, -1.58, 0.20),
vector.create(1.64, -0.76, -0.73),
vector.create(-2.44, 0.66, 1.06),
vector.create(-2.61, 1.01, 0.50),
vector.create(1.21, -2.28, -0.45),
vector.create(-0.31, -0.12, 1.96),
vector.create(1.16, -0.07, -1.93)
}
-- numeric answers to the tests below (in degrees)
local ans = {
-105.1702,
-69.49491,
0.0,
-102.9083,
0.0,
0.0,
180.0,
-0.02797646,
-90.0,
165.8858
}
for i,v in ans do
ans[i] = math.rad(ans[i])
end
local function fuzzyeq(x, y, eps) return x == y or math.abs(x - y) < (eps or 1e-6) end
assert(fuzzyeq(vector.angle(rand[10], rand[1]), math.abs(ans[10])))
assert(fuzzyeq(vector.angle(rand[2], rand[3]), math.abs(ans[1])))
assert(fuzzyeq(vector.angle(rand[4], rand[5]), math.abs(ans[2])))
assert(fuzzyeq(vector.angle(vector.zero, rand[6]), math.abs(ans[3])))
assert(fuzzyeq(vector.angle(vector.one, rand[7]), math.abs(ans[4])))
assert(fuzzyeq(vector.angle(vector.zero, vector.zero), math.abs(ans[5])))
assert(fuzzyeq(vector.angle(rand[8], rand[8]), math.abs(ans[6])))
assert(fuzzyeq(vector.angle(-rand[8], rand[8]), math.abs(ans[7])))
assert(fuzzyeq(vector.angle(rand[9], rand[9] + vector.create(0, 1, 0) * 0.001), math.abs(ans[8]), 1e-3)) -- slightly more generous eps
assert(fuzzyeq(vector.angle(vector.create(1, 0, 0), vector.create(0, 1, 0)), math.abs(ans[9])))
assert(fuzzyeq(vector.angle(rand[10], rand[1], rand[2]), ans[10]))
assert(fuzzyeq(vector.angle(rand[2], rand[3], rand[4]), ans[1]))
assert(fuzzyeq(vector.angle(rand[4], rand[5], rand[5]), ans[2]))
assert(fuzzyeq(vector.angle(vector.zero, rand[6], rand[10]), ans[3]))
assert(fuzzyeq(vector.angle(vector.one, rand[7], rand[10]), ans[4]))
assert(fuzzyeq(vector.angle(vector.zero, vector.zero, vector.zero), ans[5]))
assert(fuzzyeq(vector.angle(rand[8], rand[8], rand[10]), ans[6]))
assert(fuzzyeq(vector.angle(rand[9], rand[9] + vector.create(0, 1, 0) * 0.001, rand[10]), ans[8], 1e-3)) -- slightly more generous eps
assert(fuzzyeq(vector.angle(vector.create(1, 0, 0), vector.create(0, 1, 0), rand[10]), ans[9]))
end
-- 'min'/'max'
assert(vector.max(vector.create(-1, 2, 0.5)) == vector.create(-1, 2, 0.5))
assert(vector.min(vector.create(-1, 2, 0.5)) == vector.create(-1, 2, 0.5))
assert(ecall(function() vector.min() end) == "missing argument #1 to 'min' (vector expected)")
assert(ecall(function() vector.max() end) == "missing argument #1 to 'max' (vector expected)")
assert(select("#", vector.max(vector.zero, vector.one)) == 1)
assert(select("#", vector.min(vector.zero, vector.one)) == 1)
assert(vector.max(vector.create(-1, 2, 3), vector.create(3, 2, 1)) == vector.create(3, 2, 3))
assert(vector.min(vector.create(-1, 2, 3), vector.create(3, 2, 1)) == vector.create(-1, 2, 1))
assert(vector.max(vector.create(1, 2, 3),vector.create(2, 3, 4),vector.create(3, 4, 5),vector.create(4, 5, 6)) == vector.create(4, 5, 6))
assert(vector.min(vector.create(1, 2, 3),vector.create(2, 3, 4),vector.create(3, 4, 5),vector.create(4, 5, 6)) == vector.create(1, 2, 3))
-- clamp
assert(vector.clamp(vector.create(1, 1, 1), vector.create(0, 1, 2), vector.create(3, 3, 3)) == vector.create(1, 1, 2))
assert(vector.clamp(vector.create(1, 1, 1), vector.create(-1, -1, -1), vector.create(0, 1, 2)) == vector.create(0, 1, 1))
assert(select("#", vector.clamp(vector.zero, vector.zero, vector.one)) == 1)
return 'OK'