From a251bc68a2b70212e53941fd541d16ce523a1e01 Mon Sep 17 00:00:00 2001 From: Andy Friesen Date: Fri, 1 Nov 2024 12:06:07 -0700 Subject: [PATCH] Sync to upstream/release/650 (#1502) * 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 Co-authored-by: Alexander McCord Co-authored-by: Vighnesh Co-authored-by: Aviral Goel Co-authored-by: David Cope Co-authored-by: Lily Brown Co-authored-by: Vyacheslav Egorov Co-authored-by: Junseo Yoo --- Analysis/include/Luau/TypeFunctionRuntime.h | 2 + Analysis/src/AnyTypeSummary.cpp | 4 +- Analysis/src/AstQuery.cpp | 2 +- Analysis/src/Autocomplete.cpp | 2 +- Analysis/src/BuiltinDefinitions.cpp | 95 ++++-- Analysis/src/ConstraintGenerator.cpp | 12 +- Analysis/src/ConstraintSolver.cpp | 10 +- Analysis/src/DataFlowGraph.cpp | 4 +- Analysis/src/EmbeddedBuiltinDefinitions.cpp | 32 ++ Analysis/src/Frontend.cpp | 20 +- Analysis/src/Linter.cpp | 2 +- Analysis/src/NonStrictTypeChecker.cpp | 6 +- Analysis/src/Normalize.cpp | 6 +- Analysis/src/Simplify.cpp | 2 +- Analysis/src/Subtyping.cpp | 17 +- Analysis/src/ToString.cpp | 2 +- Analysis/src/TypeArena.cpp | 2 +- Analysis/src/TypeFunction.cpp | 8 +- Analysis/src/TypeFunctionRuntime.cpp | 13 +- Analysis/src/TypeFunctionRuntimeBuilder.cpp | 88 +++--- Analysis/src/TypeInfer.cpp | 23 +- Analysis/src/Unifier.cpp | 8 +- Ast/include/Luau/Parser.h | 2 +- Ast/src/Parser.cpp | 24 +- Ast/src/TimeTrace.cpp | 2 +- CodeGen/src/BytecodeAnalysis.cpp | 34 ++ CodeGen/src/CodeGen.cpp | 6 +- CodeGen/src/CodeGenUtils.cpp | 5 +- CodeGen/src/IrRegAllocA64.cpp | 2 +- CodeGen/src/IrTranslateBuiltins.cpp | 327 ++++++++++++++++++++ CodeGen/src/OptimizeConstProp.cpp | 13 +- Common/include/Luau/Bytecode.h | 13 + Common/include/Luau/Common.h | 4 +- Compiler/include/Luau/Compiler.h | 4 +- Compiler/include/luacode.h | 4 +- Compiler/src/Builtins.cpp | 47 +++ Compiler/src/Types.cpp | 46 ++- Compiler/src/Types.h | 10 +- Sources.cmake | 1 + VM/include/lua.h | 1 + VM/include/lualib.h | 3 + VM/src/lapi.cpp | 20 ++ VM/src/lbuiltins.cpp | 269 ++++++++++++++++ VM/src/ldblib.cpp | 6 + VM/src/linit.cpp | 1 + VM/src/lmathlib.cpp | 2 +- VM/src/lnumutils.h | 11 + VM/src/lveclib.cpp | 291 +++++++++++++++++ bench/tests/mesh-normal-vector.lua | 165 ++++++++++ bench/tests/vector-math.lua | 39 +++ tests/AstQuery.test.cpp | 2 +- tests/Compiler.test.cpp | 18 ++ tests/Conformance.test.cpp | 39 ++- tests/Differ.test.cpp | 4 +- tests/Fixture.cpp | 2 + tests/Fixture.h | 6 + tests/Frontend.test.cpp | 7 +- tests/Module.test.cpp | 6 +- tests/NonstrictMode.test.cpp | 16 +- tests/Parser.test.cpp | 16 +- tests/RuntimeLimits.test.cpp | 4 +- tests/ToDot.test.cpp | 4 +- tests/ToString.test.cpp | 6 +- tests/TypeFunction.user.test.cpp | 68 ++++ tests/TypeInfer.aliases.test.cpp | 26 +- tests/TypeInfer.builtins.test.cpp | 62 +++- tests/TypeInfer.classes.test.cpp | 6 +- tests/TypeInfer.definitions.test.cpp | 20 ++ tests/TypeInfer.functions.test.cpp | 70 +++-- tests/TypeInfer.generics.test.cpp | 31 +- tests/TypeInfer.intersectionTypes.test.cpp | 32 +- tests/TypeInfer.loops.test.cpp | 16 +- tests/TypeInfer.modules.test.cpp | 52 +++- tests/TypeInfer.oop.test.cpp | 4 +- tests/TypeInfer.operators.test.cpp | 31 +- tests/TypeInfer.provisional.test.cpp | 30 +- tests/TypeInfer.refinements.test.cpp | 4 +- tests/TypeInfer.singletons.test.cpp | 6 +- tests/TypeInfer.tables.test.cpp | 72 +++-- tests/TypeInfer.test.cpp | 30 +- tests/TypeInfer.tryUnify.test.cpp | 10 +- tests/TypeInfer.typePacks.test.cpp | 8 +- tests/TypeInfer.unionTypes.test.cpp | 24 +- tests/TypePath.test.cpp | 8 +- tests/conformance/debug.lua | 13 + tests/conformance/vector_library.lua | 159 ++++++++++ 86 files changed, 2217 insertions(+), 407 deletions(-) create mode 100644 VM/src/lveclib.cpp create mode 100644 bench/tests/mesh-normal-vector.lua create mode 100644 bench/tests/vector-math.lua create mode 100644 tests/conformance/vector_library.lua diff --git a/Analysis/include/Luau/TypeFunctionRuntime.h b/Analysis/include/Luau/TypeFunctionRuntime.h index 44eef1360..be091351c 100644 --- a/Analysis/include/Luau/TypeFunctionRuntime.h +++ b/Analysis/include/Luau/TypeFunctionRuntime.h @@ -265,4 +265,6 @@ void registerTypeUserData(lua_State* L); void setTypeFunctionEnvironment(lua_State* L); +void resetTypeFunctionState(lua_State* L); + } // namespace Luau diff --git a/Analysis/src/AnyTypeSummary.cpp b/Analysis/src/AnyTypeSummary.cpp index 85f567aff..e82592df7 100644 --- a/Analysis/src/AnyTypeSummary.cpp +++ b/Analysis/src/AnyTypeSummary.cpp @@ -38,7 +38,7 @@ #include -LUAU_FASTFLAGVARIABLE(StudioReportLuauAny2, false); +LUAU_FASTFLAGVARIABLE(StudioReportLuauAny2); LUAU_FASTINTVARIABLE(LuauAnySummaryRecursionLimit, 300); LUAU_FASTFLAG(DebugLuauMagicTypes); @@ -161,7 +161,7 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatReturn* ret, const Module* typeInfo.push_back(ti); } } - + if (ret->list.size > 1 && !seenTP) { if (containsAny(retScope->returnType)) diff --git a/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index 6b48b16ea..93dabeae8 100644 --- a/Analysis/src/AstQuery.cpp +++ b/Analysis/src/AstQuery.cpp @@ -13,7 +13,7 @@ LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAGVARIABLE(LuauDocumentationAtPosition, false) +LUAU_FASTFLAGVARIABLE(LuauDocumentationAtPosition) namespace Luau { diff --git a/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index c89d77931..829f6bb74 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/Analysis/src/Autocomplete.cpp @@ -16,7 +16,7 @@ #include LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAGVARIABLE(AutocompleteRequirePathSuggestions, false) +LUAU_FASTFLAGVARIABLE(AutocompleteRequirePathSuggestions) LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) LUAU_FASTINT(LuauTypeInferIterationLimit) diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index 84d2d6e9d..30fc2696f 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -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 freezeTable(TypeId inputType, MagicFunctionCallContext& context) +{ + TypeArena* arena = context.solver->arena; + + if (auto mt = get(inputType)) + { + std::optional frozenTable = freezeTable(mt->table, context); + + if (!frozenTable) + return std::nullopt; + + TypeId resultType = arena->addType(MetatableType{*frozenTable, mt->metatable, mt->syntheticName}); + + return resultType; + } + + if (get(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(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(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 resultDef = dfg->getDefOptional(targetExpr); std::optional 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(clonedType); - // `clone` should not break this. - LUAU_ASSERT(tableTy); - tableTy->state = TableState::Sealed; - tableTy->syntheticName = std::nullopt; - - // We'll mutate the table to make every property type read-only. - for (auto iter = tableTy->props.begin(); iter != tableTy->props.end();) + std::optional frozenType = freezeTable(inputType, context); + + if (!frozenType) { - if (iter->second.isWriteOnly()) - iter = tableTy->props.erase(iter); - else - { - iter->second.writeTy = std::nullopt; - iter++; - } + if (resultTy) + asMutable(*resultTy)->ty.emplace(context.solver->builtinTypes->errorType); + asMutable(context.result)->ty.emplace(context.solver->builtinTypes->errorTypePack); + + return true; } if (resultTy) - asMutable(*resultTy)->ty.emplace(clonedType); - asMutable(context.result)->ty.emplace(arena->addTypePack({clonedType})); + asMutable(*resultTy)->ty.emplace(*frozenType); + asMutable(context.result)->ty.emplace(arena->addTypePack({*frozenType})); return true; } diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index e242df8ec..8153c3d56 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -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()) { - 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{}); @@ -2913,7 +2913,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, std::vector toBlock; if (DFInt::LuauTypeSolverRelease >= 648) { - // This logic is incomplete as we want to re-run this + // This logic is incomplete as we want to re-run this // _after_ blocked types have resolved, but this // allows us to do some bidirectional inference. toBlock = findBlockedTypesIn(expr, NotNull{&module->astTypes}); @@ -2931,7 +2931,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, toBlock ); // The visitor we ran prior should ensure that there are no - // blocked types that we would encounter while matching on + // blocked types that we would encounter while matching on // this expression. LUAU_ASSERT(toBlock.empty()); } diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 31afabb23..34e08fe34 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -27,12 +27,12 @@ #include #include -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 { @@ -1337,7 +1337,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNullfunc); - 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(); diff --git a/Analysis/src/EmbeddedBuiltinDefinitions.cpp b/Analysis/src/EmbeddedBuiltinDefinitions.cpp index 50e090ca7..209bcdbeb 100644 --- a/Analysis/src/EmbeddedBuiltinDefinitions.cpp +++ b/Analysis/src/EmbeddedBuiltinDefinitions.cpp @@ -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; } diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 4072575a5..e94b4a290 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -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 { diff --git a/Analysis/src/Linter.cpp b/Analysis/src/Linter.cpp index c4f46c84f..073b05dc2 100644 --- a/Analysis/src/Linter.cpp +++ b/Analysis/src/Linter.cpp @@ -18,7 +18,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAttribute) LUAU_FASTFLAG(LuauNativeAttribute) -LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute, false) +LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute) namespace Luau { diff --git a/Analysis/src/NonStrictTypeChecker.cpp b/Analysis/src/NonStrictTypeChecker.cpp index 0ebc573d1..4fa0d9959 100644 --- a/Analysis/src/NonStrictTypeChecker.cpp +++ b/Analysis/src/NonStrictTypeChecker.cpp @@ -19,6 +19,8 @@ #include #include +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 {}; } diff --git a/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index 1480d2635..a2b440b90 100644 --- a/Analysis/src/Normalize.cpp +++ b/Analysis/src/Normalize.cpp @@ -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 { diff --git a/Analysis/src/Simplify.cpp b/Analysis/src/Simplify.cpp index 3a1e3bd10..507abdf23 100644 --- a/Analysis/src/Simplify.cpp +++ b/Analysis/src/Simplify.cpp @@ -15,7 +15,7 @@ LUAU_FASTINT(LuauTypeReductionRecursionLimit) LUAU_FASTFLAG(LuauSolverV2) LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8); -LUAU_FASTFLAGVARIABLE(LuauFlagBasicIntersectFollows, false); +LUAU_FASTFLAGVARIABLE(LuauFlagBasicIntersectFollows); namespace Luau { diff --git a/Analysis/src/Subtyping.cpp b/Analysis/src/Subtyping.cpp index 6c84c9333..914ded05b 100644 --- a/Analysis/src/Subtyping.cpp +++ b/Analysis/src/Subtyping.cpp @@ -21,8 +21,9 @@ #include -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(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)); diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index 5b191d30d..0bb7344a1 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -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 { diff --git a/Analysis/src/TypeArena.cpp b/Analysis/src/TypeArena.cpp index 6cf81471d..617bd3059 100644 --- a/Analysis/src/TypeArena.cpp +++ b/Analysis/src/TypeArena.cpp @@ -2,7 +2,7 @@ #include "Luau/TypeArena.h" -LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false); +LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena); namespace Luau { diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index d5eac1f20..0193f4f1a 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -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 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 diff --git a/Analysis/src/TypeFunctionRuntime.cpp b/Analysis/src/TypeFunctionRuntime.cpp index 84fa0feae..b8518636a 100644 --- a/Analysis/src/TypeFunctionRuntime.cpp +++ b/Analysis/src/TypeFunctionRuntime.cpp @@ -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 diff --git a/Analysis/src/TypeFunctionRuntimeBuilder.cpp b/Analysis/src/TypeFunctionRuntimeBuilder.cpp index e14c37739..ca0c1b72f 100644 --- a/Analysis/src/TypeFunctionRuntimeBuilder.cpp +++ b/Analysis/src/TypeFunctionRuntimeBuilder.cpp @@ -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 @@ class TypeFunctionSerializer return target; } - void serializeChildren(TypeId ty, TypeFunctionTypeId tfti) + void serializeChildren(const TypeId ty, TypeFunctionTypeId tfti) { - if (auto [p1, p2] = std::tuple{getMutable(ty), getMutable(tfti)}; p1 && p2) + if (auto [p1, p2] = std::tuple{get(ty), getMutable(tfti)}; p1 && p2) serializeChildren(p1, p2); - else if (auto [u1, u2] = std::tuple{getMutable(ty), getMutable(tfti)}; u1 && u2) + else if (auto [u1, u2] = std::tuple{get(ty), getMutable(tfti)}; u1 && u2) serializeChildren(u1, u2); - else if (auto [n1, n2] = std::tuple{getMutable(ty), getMutable(tfti)}; n1 && n2) + else if (auto [n1, n2] = std::tuple{get(ty), getMutable(tfti)}; n1 && n2) serializeChildren(n1, n2); - else if (auto [a1, a2] = std::tuple{getMutable(ty), getMutable(tfti)}; a1 && a2) + else if (auto [a1, a2] = std::tuple{get(ty), getMutable(tfti)}; a1 && a2) serializeChildren(a1, a2); - else if (auto [s1, s2] = std::tuple{getMutable(ty), getMutable(tfti)}; s1 && s2) + else if (auto [s1, s2] = std::tuple{get(ty), getMutable(tfti)}; s1 && s2) serializeChildren(s1, s2); - else if (auto [u1, u2] = std::tuple{getMutable(ty), getMutable(tfti)}; u1 && u2) + else if (auto [u1, u2] = std::tuple{get(ty), getMutable(tfti)}; u1 && u2) serializeChildren(u1, u2); - else if (auto [i1, i2] = std::tuple{getMutable(ty), getMutable(tfti)}; i1 && i2) + else if (auto [i1, i2] = std::tuple{get(ty), getMutable(tfti)}; i1 && i2) serializeChildren(i1, i2); - else if (auto [n1, n2] = std::tuple{getMutable(ty), getMutable(tfti)}; n1 && n2) + else if (auto [n1, n2] = std::tuple{get(ty), getMutable(tfti)}; n1 && n2) serializeChildren(n1, n2); - else if (auto [t1, t2] = std::tuple{getMutable(ty), getMutable(tfti)}; t1 && t2) + else if (auto [t1, t2] = std::tuple{get(ty), getMutable(tfti)}; t1 && t2) serializeChildren(t1, t2); - else if (auto [m1, m2] = std::tuple{getMutable(ty), getMutable(tfti)}; m1 && m2) + else if (auto [m1, m2] = std::tuple{get(ty), getMutable(tfti)}; m1 && m2) serializeChildren(m1, m2); - else if (auto [f1, f2] = std::tuple{getMutable(ty), getMutable(tfti)}; f1 && f2) + else if (auto [f1, f2] = std::tuple{get(ty), getMutable(tfti)}; f1 && f2) serializeChildren(f1, f2); - else if (auto [c1, c2] = std::tuple{getMutable(ty), getMutable(tfti)}; c1 && c2) + else if (auto [c1, c2] = std::tuple{get(ty), getMutable(tfti)}; c1 && c2) serializeChildren(c1, c2); else { // Either this or ty and tfti do not represent the same type @@ -274,12 +276,11 @@ class TypeFunctionSerializer } } - void serializeChildren(TypePackId tp, TypeFunctionTypePackId tftp) + void serializeChildren(const TypePackId tp, TypeFunctionTypePackId tftp) { - if (auto [tPack1, tPack2] = std::tuple{getMutable(tp), getMutable(tftp)}; tPack1 && tPack2) + if (auto [tPack1, tPack2] = std::tuple{get(tp), getMutable(tftp)}; tPack1 && tPack2) serializeChildren(tPack1, tPack2); - else if (auto [vPack1, vPack2] = std::tuple{getMutable(tp), getMutable(tftp)}; - vPack1 && vPack2) + else if (auto [vPack1, vPack2] = std::tuple{get(tp), getMutable(tftp)}; vPack1 && vPack2) serializeChildren(vPack1, vPack2); else { // Either this or ty and tfti do not represent the same type @@ -298,49 +299,49 @@ class TypeFunctionSerializer 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 @@ class TypeFunctionSerializer 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(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(m1->table)) + serializeChildren(tableTy, m2); + } + else + { + auto tmpTable = get(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 @@ class TypeFunctionSerializer 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); } diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index fab5a65db..5eb01b3e6 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -23,16 +23,18 @@ #include #include -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(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 metamethod = findMetatableEntry(lhsType, metamethodName, expr.location, /* addErrors= */ true); if (metamethod) { - if (const FunctionType* ftv = get(*metamethod)) + if (const FunctionType* ftv = get(FFlag::LuauMetatableFollow ? follow(*metamethod) : *metamethod)) { if (isEquality) { diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index b1e16c25a..11cc399e3 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -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 { diff --git a/Ast/include/Luau/Parser.h b/Ast/include/Luau/Parser.h index 5411379e3..475d19da8 100644 --- a/Ast/include/Luau/Parser.h +++ b/Ast/include/Luau/Parser.h @@ -217,7 +217,7 @@ class Parser AstType* parseTableType(bool inDeclarationContext = false); AstTypeOrPack parseSimpleType(bool allowPack, bool inDeclarationContext = false); - AstTypeOrPack parseTypeOrPack(); + AstTypeOrPack parseSimpleTypeOrPack(); AstType* parseType(bool inDeclarationContext = false); AstTypePack* parseTypePack(); diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 76ed2a5ac..1ca028f26 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -8,6 +8,7 @@ #include #include +#include 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 AstArraydata, 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> 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 Parser::parseTypeParams() } else if (lexer.current().type == '(') { - auto [type, typePack] = parseTypeOrPack(); + auto [type, typePack] = parseSimpleTypeOrPack(); if (typePack) parameters.push_back({{}, typePack}); diff --git a/Ast/src/TimeTrace.cpp b/Ast/src/TimeTrace.cpp index 8bccffce2..24bc707f1 100644 --- a/Ast/src/TimeTrace.cpp +++ b/Ast/src/TimeTrace.cpp @@ -26,7 +26,7 @@ #include -LUAU_FASTFLAGVARIABLE(DebugLuauTimeTracing, false) +LUAU_FASTFLAGVARIABLE(DebugLuauTimeTracing) namespace Luau { namespace TimeTrace diff --git a/CodeGen/src/BytecodeAnalysis.cpp b/CodeGen/src/BytecodeAnalysis.cpp index 8d2efebe0..85317b60c 100644 --- a/CodeGen/src/BytecodeAnalysis.cpp +++ b/CodeGen/src/BytecodeAnalysis.cpp @@ -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; } } diff --git a/CodeGen/src/CodeGen.cpp b/CodeGen/src/CodeGen.cpp index 667f5726a..2850dd152 100644 --- a/CodeGen/src/CodeGen.cpp +++ b/CodeGen/src/CodeGen.cpp @@ -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 diff --git a/CodeGen/src/CodeGenUtils.cpp b/CodeGen/src/CodeGenUtils.cpp index ad231e764..9bda7c814 100644 --- a/CodeGen/src/CodeGenUtils.cpp +++ b/CodeGen/src/CodeGenUtils.cpp @@ -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; diff --git a/CodeGen/src/IrRegAllocA64.cpp b/CodeGen/src/IrRegAllocA64.cpp index 4471aaa58..bd2147a79 100644 --- a/CodeGen/src/IrRegAllocA64.cpp +++ b/CodeGen/src/IrRegAllocA64.cpp @@ -10,7 +10,7 @@ #include -LUAU_FASTFLAGVARIABLE(DebugCodegenChaosA64, false) +LUAU_FASTFLAGVARIABLE(DebugCodegenChaosA64) namespace Luau { diff --git a/CodeGen/src/IrTranslateBuiltins.cpp b/CodeGen/src/IrTranslateBuiltins.cpp index 52efaef1c..cec182047 100644 --- a/CodeGen/src/IrTranslateBuiltins.cpp +++ b/CodeGen/src/IrTranslateBuiltins.cpp @@ -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}; } diff --git a/CodeGen/src/OptimizeConstProp.cpp b/CodeGen/src/OptimizeConstProp.cpp index f3271d3f2..fa1b18d3f 100644 --- a/CodeGen/src/OptimizeConstProp.cpp +++ b/CodeGen/src/OptimizeConstProp.cpp @@ -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(); diff --git a/Common/include/Luau/Bytecode.h b/Common/include/Luau/Bytecode.h index 82185e7fd..8d2813939 100644 --- a/Common/include/Luau/Bytecode.h +++ b/Common/include/Luau/Bytecode.h @@ -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 diff --git a/Common/include/Luau/Common.h b/Common/include/Luau/Common.h index 2f4f1df8a..b4bbf0f73 100644 --- a/Common/include/Luau/Common.h +++ b/Common/include/Luau/Common.h @@ -106,10 +106,10 @@ FValue* FValue::list = nullptr; { \ extern Luau::FValue flag; \ } -#define LUAU_FASTFLAGVARIABLE(flag, def) \ +#define LUAU_FASTFLAGVARIABLE(flag) \ namespace FFlag \ { \ - Luau::FValue flag(#flag, def, false); \ + Luau::FValue flag(#flag, false, false); \ } #define LUAU_FASTINT(flag) \ namespace FInt \ diff --git a/Compiler/include/Luau/Compiler.h b/Compiler/include/Luau/Compiler.h index 403fa6ddb..b37b58ffb 100644 --- a/Compiler/include/Luau/Compiler.h +++ b/Compiler/include/Luau/Compiler.h @@ -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 diff --git a/Compiler/include/luacode.h b/Compiler/include/luacode.h index 1440a699c..1eaf28d4e 100644 --- a/Compiler/include/luacode.h +++ b/Compiler/include/luacode.h @@ -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 diff --git a/Compiler/src/Builtins.cpp b/Compiler/src/Builtins.cpp index 90bf72c4e..d5d236296 100644 --- a/Compiler/src/Builtins.cpp +++ b/Compiler/src/Builtins.cpp @@ -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(); diff --git a/Compiler/src/Types.cpp b/Compiler/src/Types.cpp index 18dc248f8..7f5885a5f 100644 --- a/Compiler/src/Types.cpp +++ b/Compiler/src/Types.cpp @@ -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& generics, const DenseHashMap& typeAliases, bool resolveAliases, - const char* vectorType, + const char* hostVectorType, const DenseHashMap& 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& typeAliases, - const char* vectorType, + const char* hostVectorType, const DenseHashMap& 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& functionTypes; DenseHashMap& localTypes; DenseHashMap& exprTypes; - const char* vectorType; + const char* hostVectorType; const DenseHashMap& userdataTypes; const BuiltinAstTypes& builtinTypes; const DenseHashMap& builtinCalls; @@ -191,7 +196,7 @@ struct TypeMapVisitor : AstVisitor DenseHashMap& functionTypes, DenseHashMap& localTypes, DenseHashMap& exprTypes, - const char* vectorType, + const char* hostVectorType, const DenseHashMap& userdataTypes, const BuiltinAstTypes& builtinTypes, const DenseHashMap& 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& localTypes, DenseHashMap& exprTypes, AstNode* root, - const char* vectorType, + const char* hostVectorType, const DenseHashMap& userdataTypes, const BuiltinAstTypes& builtinTypes, const DenseHashMap& 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); } diff --git a/Compiler/src/Types.h b/Compiler/src/Types.h index a310bfcc6..46610db27 100644 --- a/Compiler/src/Types.h +++ b/Compiler/src/Types.h @@ -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& localTypes, DenseHashMap& exprTypes, AstNode* root, - const char* vectorType, + const char* hostVectorType, const DenseHashMap& userdataTypes, const BuiltinAstTypes& builtinTypes, const DenseHashMap& builtinCalls, diff --git a/Sources.cmake b/Sources.cmake index 103ea2806..4b99e8677 100644 --- a/Sources.cmake +++ b/Sources.cmake @@ -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 diff --git a/VM/include/lua.h b/VM/include/lua.h index dbb313c27..c4f5f714e 100644 --- a/VM/include/lua.h +++ b/VM/include/lua.h @@ -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); diff --git a/VM/include/lualib.h b/VM/include/lualib.h index 367a02819..5860c6132 100644 --- a/VM/include/lualib.h +++ b/VM/include/lualib.h @@ -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); diff --git a/VM/src/lapi.cpp b/VM/src/lapi.cpp index 87f85af85..4c42f8c1f 100644 --- a/VM/src/lapi.cpp +++ b/VM/src/lapi.cpp @@ -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); diff --git a/VM/src/lbuiltins.cpp b/VM/src/lbuiltins.cpp index e28bb1698..0bca4495b 100644 --- a/VM/src/lbuiltins.cpp +++ b/VM/src/lbuiltins.cpp @@ -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, luauF_writefp, + 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. diff --git a/VM/src/ldblib.cpp b/VM/src/ldblib.cpp index dfc61e4d2..cab4dd6fc 100644 --- a/VM/src/ldblib.cpp +++ b/VM/src/ldblib.cpp @@ -8,6 +8,8 @@ #include #include +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"); } } diff --git a/VM/src/linit.cpp b/VM/src/linit.cpp index aad6513f5..efcf19043 100644 --- a/VM/src/linit.cpp +++ b/VM/src/linit.cpp @@ -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}, }; diff --git a/VM/src/lmathlib.cpp b/VM/src/lmathlib.cpp index 879a95383..3a93abcf9 100644 --- a/VM/src/lmathlib.cpp +++ b/VM/src/lmathlib.cpp @@ -7,7 +7,7 @@ #include #include -LUAU_FASTFLAGVARIABLE(LuauMathMap, false) +LUAU_FASTFLAGVARIABLE(LuauMathMap) #undef PI #define PI (3.14159265358979323846) diff --git a/VM/src/lnumutils.h b/VM/src/lnumutils.h index 38bfb3228..de56bb090 100644 --- a/VM/src/lnumutils.h +++ b/VM/src/lnumutils.h @@ -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) { diff --git a/VM/src/lveclib.cpp b/VM/src/lveclib.cpp new file mode 100644 index 000000000..174e1ed46 --- /dev/null +++ b/VM/src/lveclib.cpp @@ -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 + +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; +} diff --git a/bench/tests/mesh-normal-vector.lua b/bench/tests/mesh-normal-vector.lua new file mode 100644 index 000000000..b34f48f89 --- /dev/null +++ b/bench/tests/mesh-normal-vector.lua @@ -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") diff --git a/bench/tests/vector-math.lua b/bench/tests/vector-math.lua new file mode 100644 index 000000000..f69147fd7 --- /dev/null +++ b/bench/tests/vector-math.lua @@ -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") diff --git a/tests/AstQuery.test.cpp b/tests/AstQuery.test.cpp index 5fc51b39a..e730171f2 100644 --- a/tests/AstQuery.test.cpp +++ b/tests/AstQuery.test.cpp @@ -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 diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index 73b8816f3..9d0824af8 100644 --- a/tests/Compiler.test.cpp +++ b/tests/Compiler.test.cpp @@ -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( diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index 0a88444a8..17e5a3612 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -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(type)) @@ -943,6 +970,10 @@ static void populateRTTI(lua_State* L, Luau::TypeId type) lua_pushstring(L, "function"); } + else if (auto c = Luau::get(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); diff --git a/tests/Differ.test.cpp b/tests/Differ.test.cpp index a2b2280bd..8050974e0 100644 --- a/tests/Differ.test.cpp +++ b/tests/Differ.test.cpp @@ -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(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 = { diff --git a/tests/Fixture.cpp b/tests/Fixture.cpp index bf254f80c..cf92ff66b 100644 --- a/tests/Fixture.cpp +++ b/tests/Fixture.cpp @@ -28,6 +28,8 @@ LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile) LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) +LUAU_FASTFLAGVARIABLE(DebugLuauForceAllNewSolverTests); + extern std::optional randomSeed; // tests/main.cpp namespace Luau diff --git a/tests/Fixture.h b/tests/Fixture.h index 4f2050c11..0db208d91 100644 --- a/tests/Fixture.h +++ b/tests/Fixture.h @@ -26,6 +26,12 @@ #include 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 { diff --git a/tests/Frontend.test.cpp b/tests/Frontend.test.cpp index 88f91708b..d7028fd7c 100644 --- a/tests/Frontend.test.cpp +++ b/tests/Frontend.test.cpp @@ -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 diff --git a/tests/Module.test.cpp b/tests/Module.test.cpp index 4519ba823..025fa7fd9 100644 --- a/tests/Module.test.cpp +++ b/tests/Module.test.cpp @@ -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); diff --git a/tests/NonstrictMode.test.cpp b/tests/NonstrictMode.test.cpp index 3acd39098..7ff6ea374 100644 --- a/tests/NonstrictMode.test.cpp +++ b/tests/NonstrictMode.test.cpp @@ -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 diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index dea628594..0ab402b5a 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -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 { diff --git a/tests/RuntimeLimits.test.cpp b/tests/RuntimeLimits.test.cpp index b4acf1384..dacbb43d6 100644 --- a/tests/RuntimeLimits.test.cpp +++ b/tests/RuntimeLimits.test.cpp @@ -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 diff --git a/tests/ToDot.test.cpp b/tests/ToDot.test.cpp index fd72579b2..cd7f6add0 100644 --- a/tests/ToDot.test.cpp +++ b/tests/ToDot.test.cpp @@ -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}}}}; diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp index fe87b6a70..422315f9c 100644 --- a/tests/ToString.test.cpp +++ b/tests/ToString.test.cpp @@ -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 diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index b6160b3c9..29d7e8a76 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -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> + )"); + + 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(); diff --git a/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index 53f134f30..9cdaf1a17 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/tests/TypeInfer.aliases.test.cpp @@ -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...) -> () @@ -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 = { data: T, children: {Tree2} } @@ -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 diff --git a/tests/TypeInfer.builtins.test.cpp b/tests/TypeInfer.builtins.test.cpp index 7f73f8e2c..44b0dd77e 100644 --- a/tests/TypeInfer.builtins.test.cpp +++ b/tests/TypeInfer.builtins.test.cpp @@ -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(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(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} diff --git a/tests/TypeInfer.classes.test.cpp b/tests/TypeInfer.classes.test.cpp index 16751559f..b81ac010a 100644 --- a/tests/TypeInfer.classes.test.cpp +++ b/tests/TypeInfer.classes.test.cpp @@ -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() diff --git a/tests/TypeInfer.definitions.test.cpp b/tests/TypeInfer.definitions.test.cpp index e1eaf5e92..5a530e833 100644 --- a/tests/TypeInfer.definitions.test.cpp +++ b/tests/TypeInfer.definitions.test.cpp @@ -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") { diff --git a/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index 3803df7c1..ad4f9a85d 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/tests/TypeInfer.functions.test.cpp @@ -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(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(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(); diff --git a/tests/TypeInfer.generics.test.cpp b/tests/TypeInfer.generics.test.cpp index a84a0206c..0809a6828 100644 --- a/tests/TypeInfer.generics.test.cpp +++ b/tests/TypeInfer.generics.test.cpp @@ -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 } @@ -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 = { 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 } @@ -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(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; diff --git a/tests/TypeInfer.intersectionTypes.test.cpp b/tests/TypeInfer.intersectionTypes.test.cpp index 50e285050..ca92083b5 100644 --- a/tests/TypeInfer.intersectionTypes.test.cpp +++ b/tests/TypeInfer.intersectionTypes.test.cpp @@ -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() 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() 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 diff --git a/tests/TypeInfer.loops.test.cpp b/tests/TypeInfer.loops.test.cpp index 0498437a9..41753b66d 100644 --- a/tests/TypeInfer.loops.test.cpp +++ b/tests/TypeInfer.loops.test.cpp @@ -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() diff --git a/tests/TypeInfer.modules.test.cpp b/tests/TypeInfer.modules.test.cpp index c31f3d8cf..4f7976904 100644 --- a/tests/TypeInfer.modules.test.cpp +++ b/tests/TypeInfer.modules.test.cpp @@ -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 }"); @@ -604,7 +605,7 @@ function createUpdater(renderer) function updater.enqueueForceUpdate(publicInstance, callback, _callerName) updater._renderer.render( updater._renderer, - updater._renderer._element, + updater._renderer._element, updater._renderer._context ) end @@ -617,7 +618,7 @@ function createUpdater(renderer) ) updater._renderer.render( updater._renderer, - updater._renderer._element, + updater._renderer._element, updater._renderer._context ) end @@ -626,7 +627,7 @@ function createUpdater(renderer) local currentState = updater._renderer._newState or publicInstance.state updater._renderer.render( updater._renderer, - updater._renderer._element, + updater._renderer._element, updater._renderer._context ) end @@ -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(); diff --git a/tests/TypeInfer.oop.test.cpp b/tests/TypeInfer.oop.test.cpp index 3ccc04c1d..a2143b357 100644 --- a/tests/TypeInfer.oop.test.cpp +++ b/tests/TypeInfer.oop.test.cpp @@ -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 = {} diff --git a/tests/TypeInfer.operators.test.cpp b/tests/TypeInfer.operators.test.cpp index d0715669d..5f4b730ee 100644 --- a/tests/TypeInfer.operators.test.cpp +++ b/tests/TypeInfer.operators.test.cpp @@ -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(); diff --git a/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index 514c31c82..b2cc6713a 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/tests/TypeInfer.provisional.test.cpp @@ -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 = { value: T, child: Node? } @@ -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 : (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}; diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 615bebcdf..064af97cb 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -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 diff --git a/tests/TypeInfer.singletons.test.cpp b/tests/TypeInfer.singletons.test.cpp index e2efc8fbd..3aa4efeea 100644 --- a/tests/TypeInfer.singletons.test.cpp +++ b/tests/TypeInfer.singletons.test.cpp @@ -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? diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index b2510fe04..ce9f6ccfa 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -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(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} diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index 42963f5e1..fd8e06a77 100644 --- a/tests/TypeInfer.test.cpp +++ b/tests/TypeInfer.test.cpp @@ -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(target: {T}, ...: {T} | T): {T} diff --git a/tests/TypeInfer.tryUnify.test.cpp b/tests/TypeInfer.tryUnify.test.cpp index 2a0a072a7..ccfa69233 100644 --- a/tests/TypeInfer.tryUnify.test.cpp +++ b/tests/TypeInfer.tryUnify.test.cpp @@ -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 diff --git a/tests/TypeInfer.typePacks.test.cpp b/tests/TypeInfer.typePacks.test.cpp index 8b489c449..9cf8f153e 100644 --- a/tests/TypeInfer.typePacks.test.cpp +++ b/tests/TypeInfer.typePacks.test.cpp @@ -787,7 +787,7 @@ local d: Y ()> 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 = { 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 = { 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 @@ -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 diff --git a/tests/TypeInfer.unionTypes.test.cpp b/tests/TypeInfer.unionTypes.test.cpp index 0303f546f..320124794 100644 --- a/tests/TypeInfer.unionTypes.test.cpp +++ b/tests/TypeInfer.unionTypes.test.cpp @@ -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() @@ -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() @@ -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(x : T?) : {T} diff --git a/tests/TypePath.test.cpp b/tests/TypePath.test.cpp index be6e84faf..bf831621b 100644 --- a/tests/TypePath.test.cpp +++ b/tests/TypePath.test.cpp @@ -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"})); diff --git a/tests/conformance/debug.lua b/tests/conformance/debug.lua index e044ea454..9fa02e274 100644 --- a/tests/conformance/debug.lua +++ b/tests/conformance/debug.lua @@ -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' diff --git a/tests/conformance/vector_library.lua b/tests/conformance/vector_library.lua new file mode 100644 index 000000000..f41650d1b --- /dev/null +++ b/tests/conformance/vector_library.lua @@ -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'