From a251bc68a2b70212e53941fd541d16ce523a1e01 Mon Sep 17 00:00:00 2001 From: Andy Friesen Date: Fri, 1 Nov 2024 12:06:07 -0700 Subject: [PATCH 1/5] 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 | 93 ++++-- 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, 2216 insertions(+), 406 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 44eef136..be091351 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 85f567af..e82592df 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 6b48b16e..93dabeae 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 c89d7793..829f6bb7 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 84d2d6e9..30fc2696 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; + std::optional frozenType = freezeTable(inputType, context); - // We'll mutate the table to make every property type read-only. - for (auto iter = tableTy->props.begin(); iter != tableTy->props.end();) + if (!frozenType) { - if (iter->second.isWriteOnly()) - iter = tableTy->props.erase(iter); - else - { - iter->second.writeTy = std::nullopt; - iter++; - } + if (resultTy) + asMutable(*resultTy)->ty.emplace(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 e242df8e..8153c3d5 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 31afabb2..34e08fe3 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 50e090ca..209bcdbe 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 4072575a..e94b4a29 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 c4f46c84..073b05dc 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 0ebc573d..4fa0d995 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 1480d263..a2b440b9 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 3a1e3bd1..507abdf2 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 6c84c933..914ded05 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 5b191d30..0bb7344a 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 6cf81471..617bd305 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 d5eac1f2..0193f4f1 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 84fa0fea..b8518636 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 e14c3773..ca0c1b72 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 @@ private: 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 @@ private: } } - 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 @@ private: state->ctx->ice->ice("Serializing user defined type function arguments: kind and tfkind do not represent the same type"); } - void serializeChildren(PrimitiveType* p1, TypeFunctionPrimitiveType* p2) + void serializeChildren(const PrimitiveType* p1, TypeFunctionPrimitiveType* p2) { // noop. } - void serializeChildren(UnknownType* u1, TypeFunctionUnknownType* u2) + void serializeChildren(const UnknownType* u1, TypeFunctionUnknownType* u2) { // noop. } - void serializeChildren(NeverType* n1, TypeFunctionNeverType* n2) + void serializeChildren(const NeverType* n1, TypeFunctionNeverType* n2) { // noop. } - void serializeChildren(AnyType* a1, TypeFunctionAnyType* a2) + void serializeChildren(const AnyType* a1, TypeFunctionAnyType* a2) { // noop. } - void serializeChildren(SingletonType* s1, TypeFunctionSingletonType* s2) + void serializeChildren(const SingletonType* s1, TypeFunctionSingletonType* s2) { // noop. } - void serializeChildren(UnionType* u1, TypeFunctionUnionType* u2) + void serializeChildren(const UnionType* u1, TypeFunctionUnionType* u2) { - for (TypeId& ty : u1->options) + for (const TypeId& ty : u1->options) u2->components.push_back(shallowSerialize(ty)); } - void serializeChildren(IntersectionType* i1, TypeFunctionIntersectionType* i2) + void serializeChildren(const IntersectionType* i1, TypeFunctionIntersectionType* i2) { - for (TypeId& ty : i1->parts) + for (const TypeId& ty : i1->parts) i2->components.push_back(shallowSerialize(ty)); } - void serializeChildren(NegationType* n1, TypeFunctionNegationType* n2) + void serializeChildren(const NegationType* n1, TypeFunctionNegationType* n2) { n2->type = shallowSerialize(n1->ty); } - void serializeChildren(TableType* t1, TypeFunctionTableType* t2) + void serializeChildren(const TableType* t1, TypeFunctionTableType* t2) { for (const auto& [k, p] : t1->props) { @@ -359,25 +360,34 @@ private: t2->indexer = TypeFunctionTableIndexer(shallowSerialize(t1->indexer->indexType), shallowSerialize(t1->indexer->indexResultType)); } - void serializeChildren(MetatableType* m1, TypeFunctionTableType* m2) + void serializeChildren(const MetatableType* m1, TypeFunctionTableType* m2) { - auto tmpTable = get(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 @@ private: c2->parent = shallowSerialize(*c1->parent); } - void serializeChildren(TypePack* t1, TypeFunctionTypePack* t2) + void serializeChildren(const TypePack* t1, TypeFunctionTypePack* t2) { - for (TypeId& ty : t1->head) + for (const TypeId& ty : t1->head) t2->head.push_back(shallowSerialize(ty)); if (t1->tail.has_value()) t2->tail = shallowSerialize(*t1->tail); } - void serializeChildren(VariadicTypePack* v1, TypeFunctionVariadicTypePack* v2) + void serializeChildren(const VariadicTypePack* v1, TypeFunctionVariadicTypePack* v2) { v2->type = shallowSerialize(v1->ty); } diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index fab5a65d..5eb01b3e 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 b1e16c25..11cc399e 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 5411379e..475d19da 100644 --- a/Ast/include/Luau/Parser.h +++ b/Ast/include/Luau/Parser.h @@ -217,7 +217,7 @@ private: AstType* parseTableType(bool inDeclarationContext = false); AstTypeOrPack parseSimpleType(bool allowPack, bool inDeclarationContext = false); - AstTypeOrPack parseTypeOrPack(); + AstTypeOrPack parseSimpleTypeOrPack(); AstType* parseType(bool inDeclarationContext = false); AstTypePack* parseTypePack(); diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 76ed2a5a..1ca028f2 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 8bccffce..24bc707f 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 8d2efebe..85317b60 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 667f5726..2850dd15 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 ad231e76..9bda7c81 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 4471aaa5..bd2147a7 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 52efaef1..cec18204 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 f3271d3f..fa1b18d3 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 82185e7f..8d281393 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 2f4f1df8..b4bbf0f7 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 403fa6dd..b37b58ff 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 1440a699..1eaf28d4 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 90bf72c4..d5d23629 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 18dc248f..7f5885a5 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 a310bfcc..46610db2 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 103ea280..4b99e867 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 dbb313c2..c4f5f714 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 367a0281..5860c613 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 87f85af8..4c42f8c1 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 e28bb169..0bca4495 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 dfc61e4d..cab4dd6f 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 aad6513f..efcf1904 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 879a9538..3a93abcf 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 38bfb322..de56bb09 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 00000000..174e1ed4 --- /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 00000000..b34f48f8 --- /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 00000000..f69147fd --- /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 5fc51b39..e730171f 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 73b8816f..9d0824af 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 0a88444a..17e5a361 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 a2b2280b..8050974e 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 bf254f80..cf92ff66 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 4f2050c1..0db208d9 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 88f91708..d7028fd7 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 4519ba82..025fa7fd 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 3acd3909..7ff6ea37 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 dea62859..0ab402b5 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 b4acf138..dacbb43d 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 fd72579b..cd7f6add 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 fe87b6a7..422315f9 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 b6160b3c..29d7e8a7 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 53f134f3..9cdaf1a1 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 7f73f8e2..44b0dd77 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 16751559..b81ac010 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 e1eaf5e9..5a530e83 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 3803df7c..ad4f9a85 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 a84a0206..0809a682 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 50e28505..ca92083b 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 0498437a..41753b66 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 c31f3d8c..4f797690 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 3ccc04c1..a2143b35 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 d0715669..5f4b730e 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 514c31c8..b2cc6713 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 615bebcd..064af97c 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 e2efc8fb..3aa4efee 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 b2510fe0..ce9f6ccf 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 42963f5e..fd8e06a7 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 2a0a072a..ccfa6923 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 8b489c44..9cf8f153 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 0303f546..32012479 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 be6e84fa..bf831621 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 e044ea45..9fa02e27 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 00000000..f41650d1 --- /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' From 9a4bc6aeb8a5178c7e80d80363c6ce2634446ecb Mon Sep 17 00:00:00 2001 From: checkraisefold Date: Tue, 5 Nov 2024 10:33:21 -0800 Subject: [PATCH 2/5] Fix definition module name & location (#1495) Closes #1441 Brings behavior to parity with the old solver by filling in definitionLocation and definitionModuleName for Luau-consuming programs/libraries to use. --- Analysis/src/ConstraintGenerator.cpp | 16 +++++++++++++++- Analysis/src/ConstraintSolver.cpp | 18 ++++++++++-------- tests/TypeInfer.modules.test.cpp | 21 +++++++++++++++++++-- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 8153c3d5..c8aa8209 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -34,6 +34,7 @@ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) LUAU_FASTFLAG(LuauTypestateBuiltins2) LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues) +LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations) namespace Luau { @@ -2844,6 +2845,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, ttv->state = TableState::Unsealed; ttv->definitionModuleName = module->name; + if (FFlag::LuauNewSolverPopulateTableLocations) + { + ttv->definitionLocation = expr->location; + } ttv->scope = scope.get(); interiorTypes.back().push_back(ty); @@ -3301,7 +3306,16 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty, ice->ice("Unexpected property access " + std::to_string(int(astIndexer->access))); } - return arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed}); + TypeId tableTy = arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed}); + TableType* ttv = getMutable(tableTy); + + if (FFlag::LuauNewSolverPopulateTableLocations) + { + ttv->definitionModuleName = module->name; + ttv->definitionLocation = tab->location; + } + + return tableTy; } TypeId ConstraintGenerator::resolveFunctionType( diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 34e08fe3..bcda4e23 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -33,6 +33,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings) LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500) LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) LUAU_FASTFLAGVARIABLE(LuauRemoveNotAnyHack) +LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations) namespace Luau { @@ -1108,10 +1109,15 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul target = follow(instantiated); } + else if (FFlag::LuauNewSolverPopulateTableLocations) + { + // This is a new type - redefine the location. + ttv->definitionLocation = constraint->location; + ttv->definitionModuleName = currentModuleName; + } ttv->instantiatedTypeParams = typeArguments; ttv->instantiatedTypePackParams = packArguments; - // TODO: Fill in definitionModuleName. } bindResult(target); @@ -1433,7 +1439,8 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNullis() || expr->is() || expr->is() || expr->is()) + else if (expr->is() || expr->is() || expr->is() || + expr->is()) { Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}}; u2.unify(actualArgTy, expectedArgTy); @@ -2326,12 +2333,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl return true; } -bool ConstraintSolver::tryDispatchIterableFunction( - TypeId nextTy, - TypeId tableTy, - const IterableConstraint& c, - NotNull constraint -) +bool ConstraintSolver::tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy, const IterableConstraint& c, NotNull constraint) { const FunctionType* nextFn = get(nextTy); // If this does not hold, we should've never called `tryDispatchIterableFunction` in the first place. diff --git a/tests/TypeInfer.modules.test.cpp b/tests/TypeInfer.modules.test.cpp index 4f797690..5d5df24a 100644 --- a/tests/TypeInfer.modules.test.cpp +++ b/tests/TypeInfer.modules.test.cpp @@ -14,6 +14,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauRequireCyclesDontAlwaysReturnAny) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauTypestateBuiltins2) +LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations) using namespace Luau; @@ -466,7 +467,15 @@ local b: B.T = a LUAU_REQUIRE_ERROR_COUNT(1, result); if (FFlag::LuauSolverV2) - CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string"); + { + if (FFlag::LuauNewSolverPopulateTableLocations) + CHECK( + toString(result.errors.at(0)) == + "Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; at [read \"x\"], number is not exactly string" + ); + else + CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string"); + } else { const std::string expected = R"(Type 'T' from 'game/A' could not be converted into 'T' from 'game/B' @@ -507,7 +516,15 @@ local b: B.T = a LUAU_REQUIRE_ERROR_COUNT(1, result); if (FFlag::LuauSolverV2) - CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string"); + { + if (FFlag::LuauNewSolverPopulateTableLocations) + CHECK( + toString(result.errors.at(0)) == + "Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; at [read \"x\"], number is not exactly string" + ); + else + CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string"); + } else { const std::string expected = R"(Type 'T' from 'game/B' could not be converted into 'T' from 'game/C' From f1d4621d591b6c4fadef99f446967479605b60d4 Mon Sep 17 00:00:00 2001 From: checkraisefold Date: Tue, 5 Nov 2024 15:21:18 -0800 Subject: [PATCH 3/5] Pre-populate/duplicate check class definitions (new solver) (#1493) Closes #1492 Tested and working with the test case in the aforementioned issue, along with the full defs of luau-lsp with no issues or type errors In normal Luau files, you can use type aliases and type functions before they are declared. The same extends to declaration files, **except** in the new solver. The old solver perfectly allows this, and in fact intentionally adds it: https://github.com/luau-lang/luau/blob/db809395bf5739c895a24dc73960b9e9ab6468c5/Analysis/src/TypeInfer.cpp#L1711-L1717 This causes *much* headache and pain for external projects that make use of declaration files; namely, luau-lsp generates them from MaximumADHD's API dump, which is not ordered by dependency. This means silent error-types popping up everywhere because types are used before they are declared. The workaround would be to make code to manually reorder class definitions based on their dependencies with a bunch of code, but this is clearly not ideal, and won't work for classes dependent on each other/recursive. The solution used here is the same as is used for type aliases - the name binding for the class is given a blocked type before running the rest of constraint generation on the block. Questions remain: - Should the logic be split off of `checkAliases`? - Should a bound type be used, or should the (blocked) binding type be directly emplaced with the class type? What are the ramifications of emplacing with the bound versus the raw type? One ramification was initially ran into through an assertion because the class `superTy`/`parent` was bound, and several pieces of code assume it is not, so it had to be made followed. - Is folllowing `superTy` to set `parent` the correct workaround for the assertions thrown, or should the code expecting `parent` to be a ClassType without following it be modified instead to follow `parent`? - Should `scope->privateTypeBindings` also be checked for the duplicate error? I would presume so, since having a class with the same name as a private alias or type function should error as well? The extraneous whitespace changes are clang-format ones done automatically that should've been done in the last release - I can remove them if necessary and let another sync or OSS cleanup commit fix it. --- Analysis/src/ConstraintGenerator.cpp | 47 ++++++++++++++++++++++++++-- tests/TypeInfer.definitions.test.cpp | 27 +++++++++------- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index c8aa8209..d05623a8 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -34,6 +34,7 @@ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) LUAU_FASTFLAG(LuauTypestateBuiltins2) LUAU_FASTFLAGVARIABLE(LuauNewSolverVisitErrorExprLvalues) +LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses) LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations) namespace Luau @@ -654,6 +655,7 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* block) { std::unordered_map aliasDefinitionLocations; + std::unordered_map classDefinitionLocations; // In order to enable mutually-recursive type aliases, we need to // populate the type bindings before we actually check any of the @@ -751,6 +753,32 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc scope->privateTypeBindings[function->name.value] = std::move(typeFunction); aliasDefinitionLocations[function->name.value] = function->location; } + else if (auto classDeclaration = stat->as()) + { + if (!FFlag::LuauNewSolverPrePopulateClasses) + continue; + + if (scope->exportedTypeBindings.count(classDeclaration->name.value)) + { + auto it = classDefinitionLocations.find(classDeclaration->name.value); + LUAU_ASSERT(it != classDefinitionLocations.end()); + reportError(classDeclaration->location, DuplicateTypeDefinition{classDeclaration->name.value, it->second}); + continue; + } + + // A class might have no name if the code is syntactically + // illegal. We mustn't prepopulate anything in this case. + if (classDeclaration->name == kParseNameError) + continue; + + ScopePtr defnScope = childScope(classDeclaration, scope); + + TypeId initialType = arena->addType(BlockedType{}); + TypeFun initialFun{initialType}; + scope->exportedTypeBindings[classDeclaration->name.value] = std::move(initialFun); + + classDefinitionLocations[classDeclaration->name.value] = classDeclaration->location; + } } } @@ -1646,6 +1674,11 @@ static bool isMetamethod(const Name& name) ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClass* declaredClass) { + // If a class with the same name was already defined, we skip over + auto bindingIt = scope->exportedTypeBindings.find(declaredClass->name.value); + if (FFlag::LuauNewSolverPrePopulateClasses && bindingIt == scope->exportedTypeBindings.end()) + return ControlFlow::None; + std::optional superTy = std::make_optional(builtinTypes->classType); if (declaredClass->superName) { @@ -1660,7 +1693,10 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas // We don't have generic classes, so this assertion _should_ never be hit. LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0); - superTy = lookupType->type; + if (FFlag::LuauNewSolverPrePopulateClasses) + superTy = follow(lookupType->type); + else + superTy = lookupType->type; if (!get(follow(*superTy))) { @@ -1683,7 +1719,14 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas ctv->metatable = metaTy; - scope->exportedTypeBindings[className] = TypeFun{{}, classTy}; + + if (FFlag::LuauNewSolverPrePopulateClasses) + { + TypeId classBindTy = bindingIt->second.type; + emplaceType(asMutable(classBindTy), classTy); + } + else + scope->exportedTypeBindings[className] = TypeFun{{}, classTy}; if (declaredClass->indexer) { diff --git a/tests/TypeInfer.definitions.test.cpp b/tests/TypeInfer.definitions.test.cpp index 5a530e83..2ab90ab5 100644 --- a/tests/TypeInfer.definitions.test.cpp +++ b/tests/TypeInfer.definitions.test.cpp @@ -9,6 +9,8 @@ using namespace Luau; +LUAU_FASTFLAG(LuauNewSolverPrePopulateClasses) + TEST_SUITE_BEGIN("DefinitionTests"); TEST_CASE_FIXTURE(Fixture, "definition_file_simple") @@ -492,11 +494,8 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_indexer") TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes") { - unfreeze(frontend.globals.globalTypes); - LoadDefinitionFileResult result = frontend.loadDefinitionFile( - frontend.globals, - frontend.globals.globalScope, - R"( + ScopedFastFlag _{FFlag::LuauNewSolverPrePopulateClasses, true}; + loadDefinition(R"( declare class Channel Messages: { Message } OnMessage: (message: Message) -> () @@ -506,13 +505,19 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes") Text: string Channel: Channel end - )", - "@test", - /* captureComments */ false - ); - freeze(frontend.globals.globalTypes); + )"); - REQUIRE(result.success); + CheckResult result = check(R"( + local a: Channel + local b = a.Messages[1] + local c = b.Channel + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + + CHECK_EQ(toString(requireType("a")), "Channel"); + CHECK_EQ(toString(requireType("b")), "Message"); + CHECK_EQ(toString(requireType("c")), "Channel"); } TEST_CASE_FIXTURE(Fixture, "definition_file_has_source_module_name_set") From 47543e5df11bb4e7e9f7b653c65cee54d957b307 Mon Sep 17 00:00:00 2001 From: aaron Date: Tue, 5 Nov 2024 15:25:38 -0800 Subject: [PATCH 4/5] Set the defining module even when the new solver cloned the type. (#1506) Follow up to #1495: a small fixup for the defining module and location to get set even when cloning was required. --- Analysis/src/ConstraintSolver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index bcda4e23..398f0aa5 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -1109,7 +1109,8 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul target = follow(instantiated); } - else if (FFlag::LuauNewSolverPopulateTableLocations) + + if (FFlag::LuauNewSolverPopulateTableLocations) { // This is a new type - redefine the location. ttv->definitionLocation = constraint->location; From 26b2307a8bc0c3783029da4e2bb34347f8ff9c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F?= <34089907+Barocena@users.noreply.github.com> Date: Thu, 7 Nov 2024 02:23:33 +0300 Subject: [PATCH 5/5] Replace old site urls (#1505) this PR replaces all the old site urls from luau-lang.org to luau.org --- CONTRIBUTING.md | 2 +- Config/include/Luau/LinterConfig.h | 2 +- README.md | 8 ++++---- VM/src/lapi.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26579740..d5d41c42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Some questions help improve the language, implementation or documentation by ins ## Documentation -A [separate site repository](https://github.com/luau-lang/site) hosts the language documentation, which is accessible on https://luau-lang.org. +A [separate site repository](https://github.com/luau-lang/site) hosts the language documentation, which is accessible on https://luau.org. Changes to this documentation that improve clarity, fix grammatical issues, explain aspects that haven't been explained before and the like are warmly welcomed. Please feel free to [create a pull request](https://help.github.com/articles/about-pull-requests/) to improve our documentation. Note that at this point the documentation is English-only. diff --git a/Config/include/Luau/LinterConfig.h b/Config/include/Luau/LinterConfig.h index 3a68c0d7..e9305009 100644 --- a/Config/include/Luau/LinterConfig.h +++ b/Config/include/Luau/LinterConfig.h @@ -15,7 +15,7 @@ struct HotComment; struct LintWarning { - // Make sure any new lint codes are documented here: https://luau-lang.org/lint + // Make sure any new lint codes are documented here: https://luau.org/lint // Note that in Studio, the active set of lint warnings is determined by FStringStudioLuauLints enum Code { diff --git a/README.md b/README.md index ba337585..edf4a553 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ Luau ![CI](https://github.com/luau-lang/luau/actions/workflows/build.yml/badge.s Luau (lowercase u, /ˈlu.aʊ/) is a fast, small, safe, gradually typed embeddable scripting language derived from [Lua](https://lua.org). -It is designed to be backwards compatible with Lua 5.1, as well as incorporating [some features](https://luau-lang.org/compatibility) from future Lua releases, but also expands the feature set (most notably with type annotations). Luau is largely implemented from scratch, with the language runtime being a very heavily modified version of Lua 5.1 runtime, with completely rewritten interpreter and other [performance innovations](https://luau-lang.org/performance). The runtime mostly preserves Lua 5.1 API, so existing bindings should be more or less compatible with a few caveats. +It is designed to be backwards compatible with Lua 5.1, as well as incorporating [some features](https://luau.org/compatibility) from future Lua releases, but also expands the feature set (most notably with type annotations). Luau is largely implemented from scratch, with the language runtime being a very heavily modified version of Lua 5.1 runtime, with completely rewritten interpreter and other [performance innovations](https://luau.org/performance). The runtime mostly preserves Lua 5.1 API, so existing bindings should be more or less compatible with a few caveats. Luau is used by Roblox game developers to write game code, as well as by Roblox engineers to implement large parts of the user-facing application code as well as portions of the editor (Roblox Studio) as plugins. Roblox chose to open-source Luau to foster collaboration within the Roblox community as well as to allow other companies and communities to benefit from the ongoing language and runtime innovation. As a consequence, Luau is now also used by games like Alan Wake 2 and Warframe. -This repository hosts source code for the language implementation and associated tooling. Documentation for the language is available at https://luau-lang.org/ and accepts contributions via [site repository](https://github.com/luau-lang/site); the language is evolved through RFCs that are located in [rfcs repository](https://github.com/luau-lang/rfcs). +This repository hosts source code for the language implementation and associated tooling. Documentation for the language is available at https://luau.org/ and accepts contributions via [site repository](https://github.com/luau-lang/site); the language is evolved through RFCs that are located in [rfcs repository](https://github.com/luau-lang/rfcs). # Usage @@ -15,7 +15,7 @@ Luau is an embeddable language, but it also comes with two command-line tools by `luau` is a command-line REPL and can also run input files. Note that REPL runs in a sandboxed environment and as such doesn't have access to the underlying file system except for ability to `require` modules. -`luau-analyze` is a command-line type checker and linter; given a set of input files, it produces errors/warnings according to the file configuration, which can be customized by using `--!` comments in the files or [`.luaurc`](https://rfcs.luau-lang.org/config-luaurc) files. For details please refer to [type checking]( https://luau-lang.org/typecheck) and [linting](https://luau-lang.org/lint) documentation. +`luau-analyze` is a command-line type checker and linter; given a set of input files, it produces errors/warnings according to the file configuration, which can be customized by using `--!` comments in the files or [`.luaurc`](https://rfcs.luau.org/config-luaurc) files. For details please refer to [type checking]( https://luau.org/typecheck) and [linting](https://luau.org/lint) documentation. # Installation @@ -28,7 +28,7 @@ Alternatively, you can use one of the packaged distributions (note that these ar - Alpine Linux: [Enable community repositories](https://wiki.alpinelinux.org/w/index.php?title=Enable_Community_Repository) and run `apk add luau` - Gentoo Linux: Luau is [officially packaged by Gentoo](https://packages.gentoo.org/packages/dev-lang/luau) and can be installed using `emerge dev-lang/luau`. You may have to unmask the package first before installing it (which can be done by including the `--autounmask=y` option in the `emerge` command). -After installing, you will want to validate the installation was successful by running the test case [here](https://luau-lang.org/getting-started). +After installing, you will want to validate the installation was successful by running the test case [here](https://luau.org/getting-started). ## Building diff --git a/VM/src/lapi.cpp b/VM/src/lapi.cpp index 4c42f8c1..d382a924 100644 --- a/VM/src/lapi.cpp +++ b/VM/src/lapi.cpp @@ -40,7 +40,7 @@ const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Ri "$URL: www.lua.org $\n"; const char* luau_ident = "$Luau: Copyright (C) 2019-2023 Roblox Corporation $\n" - "$URL: luau-lang.org $\n"; + "$URL: luau.org $\n"; #define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base))