From 66202dc4ac15f39a6ce8f732e2be19b636ee2af1 Mon Sep 17 00:00:00 2001 From: ayoungbloodrbx Date: Fri, 25 Jul 2025 15:33:42 -0700 Subject: [PATCH] Sync to upstream/release/684 (#1930) ## General - Support AstStatDeclareGlobal output as a source string (via @karl-police in #1889) - Luau heap dump correctly reports the size of a string, now including overhead for the string type - Prevent yields from Luau `xpcall` error handling function. ## Analysis - Avoid exponential blowup when normalizing union of normalized free variables. - Fix type pack-related bugs that caused infinite recursion when: - A generic type pack was bound to itself during subtyping. - In type pack flattening, when that same generic type pack was now being bound another generic type pack which contained it. - Properly simplify `any & (*error-type* | string)` to `*error-type* | *error-type* | string` instead of hanging due to creating a huge union type. --- Co-authored-by: Andy Friesen Co-authored-by: Ariel Weiss Co-authored-by: Hunter Goldstein Co-authored-by: Sora Kanosue Co-authored-by: Vyacheslav Egorov --------- Co-authored-by: Hunter Goldstein Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Menarul Alam Co-authored-by: Aviral Goel Co-authored-by: Vighnesh Co-authored-by: Vyacheslav Egorov Co-authored-by: Ariel Weiss Co-authored-by: Andy Friesen --- Analysis/include/Luau/BuiltinTypeFunctions.h | 2 +- Analysis/include/Luau/Constraint.h | 1 - Analysis/include/Luau/Instantiation.h | 5 + Analysis/include/Luau/OrderedSet.h | 2 +- Analysis/include/Luau/RecursionCounter.h | 8 +- Analysis/include/Luau/Subtyping.h | 2 + Analysis/include/Luau/ToString.h | 6 + Analysis/include/Luau/TypeIds.h | 4 +- Analysis/include/Luau/TypePath.h | 2 +- Analysis/include/Luau/VisitType.h | 17 +- Analysis/src/AstQuery.cpp | 4 +- Analysis/src/BuiltinDefinitions.cpp | 31 ++- Analysis/src/BuiltinTypeFunctions.cpp | 23 +- Analysis/src/Constraint.cpp | 6 +- Analysis/src/ConstraintGenerator.cpp | 12 +- Analysis/src/ConstraintSolver.cpp | 49 ++-- Analysis/src/EmbeddedBuiltinDefinitions.cpp | 33 +-- Analysis/src/Error.cpp | 2 +- Analysis/src/ExpectedTypeVisitor.cpp | 24 +- Analysis/src/FragmentAutocomplete.cpp | 5 +- Analysis/src/Frontend.cpp | 8 +- Analysis/src/Generalization.cpp | 12 +- Analysis/src/InferPolarity.cpp | 3 +- Analysis/src/Normalize.cpp | 7 + Analysis/src/OverloadResolution.cpp | 20 +- Analysis/src/Quantify.cpp | 3 +- Analysis/src/Scope.cpp | 4 +- Analysis/src/Simplify.cpp | 13 +- Analysis/src/Subtyping.cpp | 130 ++++++++-- Analysis/src/ToString.cpp | 47 +++- Analysis/src/Type.cpp | 2 +- Analysis/src/TypeChecker2.cpp | 21 +- Analysis/src/TypeFunction.cpp | 14 +- Analysis/src/TypeFunctionReductionGuesser.cpp | 5 + Analysis/src/TypeIds.cpp | 2 +- Analysis/src/TypeInfer.cpp | 13 +- Analysis/src/TypePack.cpp | 8 +- Analysis/src/TypePath.cpp | 12 +- Analysis/src/Unifier.cpp | 22 +- Analysis/src/Unifier2.cpp | 4 +- Analysis/src/UserDefinedTypeFunction.cpp | 4 +- Ast/src/Parser.cpp | 61 ++--- Common/include/Luau/ExperimentalFlags.h | 13 +- Compiler/src/Compiler.cpp | 18 +- Compiler/src/CostModel.cpp | 36 +-- Compiler/src/ValueTracking.cpp | 9 +- VM/src/lapi.cpp | 33 ++- VM/src/ldo.cpp | 71 +++++- VM/src/ldo.h | 6 +- VM/src/lgcdebug.cpp | 7 +- fuzz/protoprint.cpp | 237 +++++++++++++++--- tests/ClassFixture.cpp | 7 +- tests/Compiler.test.cpp | 11 - tests/Conformance.test.cpp | 70 ++++-- tests/ConstraintGeneratorFixture.cpp | 3 +- tests/CostModel.test.cpp | 33 --- tests/EqSatSimplification.test.cpp | 22 +- tests/Fixture.cpp | 6 +- tests/Fixture.h | 4 +- tests/FragmentAutocomplete.test.cpp | 70 ++++-- tests/Frontend.test.cpp | 3 +- tests/Module.test.cpp | 3 +- tests/NonstrictMode.test.cpp | 4 +- tests/Normalize.test.cpp | 4 +- tests/Parser.test.cpp | 11 +- tests/RuntimeLimits.test.cpp | 58 ++++- tests/Simplify.test.cpp | 24 ++ tests/Subtyping.test.cpp | 22 +- tests/ToString.test.cpp | 3 +- tests/TypeFunction.test.cpp | 11 +- tests/TypeFunction.user.test.cpp | 5 +- tests/TypeInfer.aliases.test.cpp | 5 - tests/TypeInfer.annotations.test.cpp | 6 +- tests/TypeInfer.generics.test.cpp | 72 +++++- tests/TypeInfer.intersectionTypes.test.cpp | 4 +- tests/TypeInfer.modules.test.cpp | 4 +- tests/TypeInfer.oop.test.cpp | 4 +- tests/TypeInfer.refinements.test.cpp | 5 +- tests/TypeInfer.tables.test.cpp | 21 +- tests/TypeInfer.test.cpp | 6 +- tests/TypeInfer.tryUnify.test.cpp | 6 +- tests/TypeInfer.typestates.test.cpp | 4 +- tests/TypeInfer.unionTypes.test.cpp | 6 +- tests/TypePath.test.cpp | 6 + tests/TypeVar.test.cpp | 6 + tests/conformance/calls.luau | 2 +- tests/conformance/coroutine.luau | 9 + 87 files changed, 1103 insertions(+), 520 deletions(-) diff --git a/Analysis/include/Luau/BuiltinTypeFunctions.h b/Analysis/include/Luau/BuiltinTypeFunctions.h index 7e75f53d..e10b72f0 100644 --- a/Analysis/include/Luau/BuiltinTypeFunctions.h +++ b/Analysis/include/Luau/BuiltinTypeFunctions.h @@ -53,4 +53,4 @@ struct BuiltinTypeFunctions const BuiltinTypeFunctions& builtinTypeFunctions(); -} +} // namespace Luau diff --git a/Analysis/include/Luau/Constraint.h b/Analysis/include/Luau/Constraint.h index 4176fe2c..f9b68eb2 100644 --- a/Analysis/include/Luau/Constraint.h +++ b/Analysis/include/Luau/Constraint.h @@ -341,7 +341,6 @@ struct Constraint DenseHashSet getMaybeMutatedFreeTypes_DEPRECATED() const; TypeIds getMaybeMutatedFreeTypes() const; - }; using ConstraintPtr = std::unique_ptr; diff --git a/Analysis/include/Luau/Instantiation.h b/Analysis/include/Luau/Instantiation.h index 3e61da7f..b3482ecd 100644 --- a/Analysis/include/Luau/Instantiation.h +++ b/Analysis/include/Luau/Instantiation.h @@ -92,6 +92,11 @@ struct GenericTypeFinder : TypeOnceVisitor { bool found = false; + GenericTypeFinder() + : TypeOnceVisitor("GenericTypeFinder") + { + } + bool visit(TypeId ty) override { return !found; diff --git a/Analysis/include/Luau/OrderedSet.h b/Analysis/include/Luau/OrderedSet.h index 0d70be4e..f13ecd1c 100644 --- a/Analysis/include/Luau/OrderedSet.h +++ b/Analysis/include/Luau/OrderedSet.h @@ -65,4 +65,4 @@ private: DenseHashSet elementSet{nullptr}; }; -} \ No newline at end of file +} // namespace Luau \ No newline at end of file diff --git a/Analysis/include/Luau/RecursionCounter.h b/Analysis/include/Luau/RecursionCounter.h index 0dc55700..962ecd02 100644 --- a/Analysis/include/Luau/RecursionCounter.h +++ b/Analysis/include/Luau/RecursionCounter.h @@ -12,8 +12,8 @@ namespace Luau struct RecursionLimitException : public InternalCompilerError { - RecursionLimitException() - : InternalCompilerError("Internal recursion counter limit exceeded") + RecursionLimitException(const std::string system) + : InternalCompilerError("Internal recursion counter limit exceeded in " + system) { } }; @@ -38,12 +38,12 @@ protected: struct RecursionLimiter : RecursionCounter { - RecursionLimiter(int* count, int limit) + RecursionLimiter(const std::string system, int* count, int limit) : RecursionCounter(count) { if (limit > 0 && *count > limit) { - throw RecursionLimitException(); + throw RecursionLimitException(system); } } }; diff --git a/Analysis/include/Luau/Subtyping.h b/Analysis/include/Luau/Subtyping.h index eeeaaba8..96a877f1 100644 --- a/Analysis/include/Luau/Subtyping.h +++ b/Analysis/include/Luau/Subtyping.h @@ -157,8 +157,10 @@ struct Subtyping Variance variance = Variance::Covariant; using SeenSet = Set, TypePairHash>; + using SeenTypePackSet = Set, TypePairHash>; SeenSet seenTypes{{}}; + SeenTypePackSet seenPacks{{}}; Subtyping( NotNull builtinTypes, diff --git a/Analysis/include/Luau/ToString.h b/Analysis/include/Luau/ToString.h index 6496c588..53b62fbb 100644 --- a/Analysis/include/Luau/ToString.h +++ b/Analysis/include/Luau/ToString.h @@ -2,6 +2,7 @@ #pragma once #include "Luau/Common.h" +#include "Luau/DenseHash.h" #include "Luau/TypeFwd.h" #include @@ -134,6 +135,10 @@ std::string dump(TypeId ty); std::string dump(const std::optional& ty); std::string dump(TypePackId ty); std::string dump(const std::optional& ty); +std::string dump(const std::vector& types); +std::string dump(DenseHashMap& types); +std::string dump(DenseHashMap& types); + std::string dump(const Constraint& c); std::string dump(const std::shared_ptr& scope, const char* name); @@ -153,4 +158,5 @@ inline std::string toString(const TypeOrPack& tyOrTp) std::string dump(const TypeOrPack& tyOrTp); +std::string toStringVector(const std::vector& types, ToStringOptions& opts); } // namespace Luau diff --git a/Analysis/include/Luau/TypeIds.h b/Analysis/include/Luau/TypeIds.h index ca40f00e..88bfbccb 100644 --- a/Analysis/include/Luau/TypeIds.h +++ b/Analysis/include/Luau/TypeIds.h @@ -32,8 +32,8 @@ public: TypeIds(const TypeIds&) = default; TypeIds& operator=(const TypeIds&) = default; - TypeIds(TypeIds&&) = default; - TypeIds& operator=(TypeIds&&) = default; + TypeIds(TypeIds&&) noexcept = default; + TypeIds& operator=(TypeIds&&) noexcept = default; void insert(TypeId ty); /// Erase every element that does not also occur in tys diff --git a/Analysis/include/Luau/TypePath.h b/Analysis/include/Luau/TypePath.h index 3462a456..785f8884 100644 --- a/Analysis/include/Luau/TypePath.h +++ b/Analysis/include/Luau/TypePath.h @@ -231,7 +231,7 @@ std::string toString(const TypePath::Path& path, bool prefixDot = false); /// Converts a Path to a human readable string for error reporting. std::string toStringHuman(const TypePath::Path& path); -// TODO: clip traverse_DEPRECATED along with `LuauReturnMappedGenericPacksFromSubtyping` +// TODO: clip traverse_DEPRECATED along with `LuauReturnMappedGenericPacksFromSubtyping2` std::optional traverse_DEPRECATED(TypeId root, const Path& path, NotNull builtinTypes); std::optional traverse_DEPRECATED(TypePackId root, const Path& path, NotNull builtinTypes); std::optional traverse( diff --git a/Analysis/include/Luau/VisitType.h b/Analysis/include/Luau/VisitType.h index c26e1aab..5e0a98d7 100644 --- a/Analysis/include/Luau/VisitType.h +++ b/Analysis/include/Luau/VisitType.h @@ -73,6 +73,8 @@ struct GenericTypeVisitor { using Set = S; + const std::string visitorName; + Set seen; bool skipBoundTypes = false; int recursionCounter = 0; @@ -80,8 +82,9 @@ struct GenericTypeVisitor GenericTypeVisitor() = default; - explicit GenericTypeVisitor(Set seen, bool skipBoundTypes = false) - : seen(std::move(seen)) + explicit GenericTypeVisitor(const std::string visitorName, Set seen, bool skipBoundTypes = false) + : visitorName(visitorName) + , seen(std::move(seen)) , skipBoundTypes(skipBoundTypes) { } @@ -215,7 +218,7 @@ struct GenericTypeVisitor void traverse(TypeId ty) { - RecursionLimiter limiter{&recursionCounter, FInt::LuauVisitRecursionLimit}; + RecursionLimiter limiter{visitorName, &recursionCounter, FInt::LuauVisitRecursionLimit}; if (visit_detail::hasSeen(seen, ty)) { @@ -527,8 +530,8 @@ struct GenericTypeVisitor */ struct TypeVisitor : GenericTypeVisitor> { - explicit TypeVisitor(bool skipBoundTypes = false) - : GenericTypeVisitor{{}, skipBoundTypes} + explicit TypeVisitor(const std::string visitorName, bool skipBoundTypes = false) + : GenericTypeVisitor{visitorName, {}, skipBoundTypes} { } }; @@ -536,8 +539,8 @@ struct TypeVisitor : GenericTypeVisitor> /// Visit each type under a given type. Each type will only be checked once even if there are multiple paths to it. struct TypeOnceVisitor : GenericTypeVisitor> { - explicit TypeOnceVisitor(bool skipBoundTypes = false) - : GenericTypeVisitor{DenseHashSet{nullptr}, skipBoundTypes} + explicit TypeOnceVisitor(const std::string visitorName, bool skipBoundTypes = false) + : GenericTypeVisitor{visitorName, DenseHashSet{nullptr}, skipBoundTypes} { } }; diff --git a/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index 602c6a0e..97d17641 100644 --- a/Analysis/src/AstQuery.cpp +++ b/Analysis/src/AstQuery.cpp @@ -583,7 +583,9 @@ std::optional getDocumentationSymbolAtPosition(const Source return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol); } else - return checkOverloadedDocumentationSymbol(module, propIt->second.type_DEPRECATED(), parentExpr, propIt->second.documentationSymbol); + return checkOverloadedDocumentationSymbol( + module, propIt->second.type_DEPRECATED(), parentExpr, propIt->second.documentationSymbol + ); } } else if (const ExternType* etv = get(parentTy)) diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index f336c482..1510912f 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -771,7 +771,9 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context) if (iter == end(context.arguments)) { - context.typechecker->reportError(CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location); + context.typechecker->reportError( + CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location + ); return true; } @@ -823,7 +825,8 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context) { TypeId actualTy = params[i + paramOffset]; TypeId expectedTy = expected[i]; - Location location = context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location; + Location location = + context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location; // use subtyping instead here SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope); @@ -845,7 +848,6 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context) } return true; - } else { @@ -863,7 +865,9 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context) if (!fmt) { - context.typechecker->reportError(CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location); + context.typechecker->reportError( + CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location + ); return true; } @@ -887,7 +891,8 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context) { TypeId actualTy = params[i + paramOffset]; TypeId expectedTy = expected[i]; - Location location = context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location; + Location location = + context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location; // use subtyping instead here SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope); @@ -895,15 +900,15 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context) { switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy)) { - case ErrorSuppression::Suppress: - break; - case ErrorSuppression::NormalizationFailed: - break; - case ErrorSuppression::DoNotSuppress: - Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result); + case ErrorSuppression::Suppress: + break; + case ErrorSuppression::NormalizationFailed: + break; + case ErrorSuppression::DoNotSuppress: + Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result); - if (!reasonings.suppressed) - context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location); + if (!reasonings.suppressed) + context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location); } } } diff --git a/Analysis/src/BuiltinTypeFunctions.cpp b/Analysis/src/BuiltinTypeFunctions.cpp index 81a251ec..2883bdeb 100644 --- a/Analysis/src/BuiltinTypeFunctions.cpp +++ b/Analysis/src/BuiltinTypeFunctions.cpp @@ -124,7 +124,7 @@ std::optional> tryDistributeTypeFunctionApp( return std::nullopt; } -} +} // namespace TypeFunctionReductionResult notTypeFunction( TypeId instance, @@ -1034,6 +1034,12 @@ TypeFunctionReductionResult eqTypeFunction( struct FindRefinementBlockers : TypeOnceVisitor { DenseHashSet found{nullptr}; + + FindRefinementBlockers() + : TypeOnceVisitor("FindRefinementBlockers") + { + } + bool visit(TypeId ty, const BlockedType&) override { found.insert(ty); @@ -1056,7 +1062,7 @@ struct ContainsRefinableType : TypeOnceVisitor { bool found = false; ContainsRefinableType() - : TypeOnceVisitor(/* skipBoundTypes */ true) + : TypeOnceVisitor("ContainsRefinableType", /* skipBoundTypes */ true) { } @@ -1163,7 +1169,7 @@ struct RefineTypeScrubber : public Substitution return !is(ty); } - TypeId clean(TypeId ty) override + TypeId clean(TypeId ty) override { // NOTE: this feels pretty similar to other places where we try to // filter over a set type, may be worth combining those in the future. @@ -1199,7 +1205,6 @@ struct RefineTypeScrubber : public Substitution } return ty; } - }; bool occurs(TypeId haystack, TypeId needle, DenseHashSet& seen) @@ -1431,7 +1436,6 @@ TypeFunctionReductionResult refineTypeFunction( return {resultTy, {}}; } - }; // refine target with each discriminant type in sequence (reverse of insertion order) @@ -1494,7 +1498,7 @@ struct CollectUnionTypeOptions : TypeOnceVisitor DenseHashSet blockingTypes{nullptr}; explicit CollectUnionTypeOptions(NotNull ctx) - : TypeOnceVisitor(/* skipBoundTypes */ true) + : TypeOnceVisitor("CollectUnionTypeOptions", /* skipBoundTypes */ true) , ctx(ctx) { } @@ -1764,7 +1768,8 @@ bool computeKeysOf_DEPRECATED(TypeId ty, Set& result, DenseHashSet< return false; } -namespace { +namespace +{ /** * Computes the keys of `ty` into `result` @@ -1847,7 +1852,7 @@ bool computeKeysOf(TypeId ty, Set>& result, DenseHash return false; } -} +} // namespace TypeFunctionReductionResult keyofFunctionImpl( const std::vector& typeParams, @@ -2824,4 +2829,4 @@ const BuiltinTypeFunctions& builtinTypeFunctions() return *result; } -} +} // namespace Luau diff --git a/Analysis/src/Constraint.cpp b/Analysis/src/Constraint.cpp index 66cc9443..0211083d 100644 --- a/Analysis/src/Constraint.cpp +++ b/Analysis/src/Constraint.cpp @@ -24,7 +24,8 @@ struct ReferenceCountInitializer_DEPRECATED : TypeOnceVisitor bool traverseIntoTypeFunctions = true; explicit ReferenceCountInitializer_DEPRECATED(DenseHashSet* result) - : result(result) + : TypeOnceVisitor("ReferenceCountInitializer_DEPRECATED") + , result(result) { } @@ -78,7 +79,8 @@ struct ReferenceCountInitializer : TypeOnceVisitor bool traverseIntoTypeFunctions = true; explicit ReferenceCountInitializer(NotNull result) - : result(result) + : TypeOnceVisitor("ReferenceCountInitializer") + , result(result) { } diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 445de472..21910e38 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -139,7 +139,10 @@ struct HasFreeType : TypeOnceVisitor { bool result = false; - HasFreeType() {} + HasFreeType() + : TypeOnceVisitor("TypeOnceVisitor") + { + } bool visit(TypeId ty) override { @@ -640,6 +643,11 @@ struct FindSimplificationBlockers : TypeOnceVisitor { bool found = false; + FindSimplificationBlockers() + : TypeOnceVisitor("FindSimplificationBlockers") + { + } + bool visit(TypeId) override { return !found; @@ -1026,7 +1034,7 @@ ControlFlow ConstraintGenerator::visitBlockWithoutChildScope(const ScopePtr& sco ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStat* stat) { - RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit}; + RecursionLimiter limiter{"ConstraintGenerator", &recursionCount, FInt::LuauCheckRecursionLimit}; if (auto s = stat->as()) return visit(scope, s); diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 7c13c70a..ea81acf1 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -34,7 +34,6 @@ LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) -LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2) LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck) LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint) LUAU_FASTFLAGVARIABLE(LuauRemoveTypeCallsForReadWriteProps) @@ -43,6 +42,7 @@ LUAU_FASTFLAGVARIABLE(LuauUseOrderedTypeSetsInConstraints) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying) LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint) +LUAU_FASTFLAGVARIABLE(LuauContainsAnyGenericFollowBeforeChecking) namespace Luau { @@ -285,7 +285,8 @@ struct InstantiationQueuer : TypeOnceVisitor Location location; explicit InstantiationQueuer(NotNull scope, const Location& location, ConstraintSolver* solver) - : solver(solver) + : TypeOnceVisitor("InstantiationQueuer") + , solver(solver) , scope(scope) , location(location) { @@ -432,7 +433,8 @@ void ConstraintSolver::run() { for (TypeId ty : constraintSet.freeTypes) { - if (auto it = mutatedFreeTypeToConstraint_DEPRECATED.find(ty); it == mutatedFreeTypeToConstraint_DEPRECATED.end() || it->second.empty()) + if (auto it = mutatedFreeTypeToConstraint_DEPRECATED.find(ty); + it == mutatedFreeTypeToConstraint_DEPRECATED.end() || it->second.empty()) generalizeOneType(ty); } } @@ -633,8 +635,7 @@ void ConstraintSolver::finalizeTypeFunctions() if (get(ty)) { TypeFunctionContext context{NotNull{this}, constraint->scope, NotNull{constraint}}; - FunctionGraphReductionResult result = - reduceTypeFunctions(t, constraint->location, NotNull{&context}, true); + FunctionGraphReductionResult result = reduceTypeFunctions(t, constraint->location, NotNull{&context}, true); for (TypeId r : result.reducedTypes) unblock(r, constraint->location); @@ -663,7 +664,8 @@ struct TypeSearcher : TypeVisitor } explicit TypeSearcher(TypeId needle, Polarity initialPolarity) - : needle(needle) + : TypeVisitor("TypeSearcher") + , needle(needle) , current(initialPolarity) { } @@ -1195,7 +1197,8 @@ struct InfiniteTypeFinder : TypeOnceVisitor bool foundInfiniteType = false; explicit InfiniteTypeFinder(ConstraintSolver* solver, const InstantiationSignature& signature, NotNull scope) - : solver(solver) + : TypeOnceVisitor("InfiniteTypeFinder") + , solver(solver) , signature(signature) , scope(scope) { @@ -1235,7 +1238,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul auto cTarget = follow(c.target); LUAU_ASSERT(get(cTarget)); // We do this check here to ensure that we don't bind an alias to itself - if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2 && occursCheck(cTarget, result)) + if (occursCheck(cTarget, result)) { reportError(OccursCheckFailed{}, constraint->location); bind(constraint, cTarget, builtinTypes->errorType); @@ -1656,6 +1659,11 @@ struct ContainsGenerics_DEPRECATED : public TypeOnceVisitor bool found = false; + ContainsGenerics_DEPRECATED() + : TypeOnceVisitor("ContainsGenerics_DEPRECATED") + { + } + bool visit(TypeId ty) override { return !found; @@ -1736,7 +1744,8 @@ struct ContainsGenerics : public TypeOnceVisitor NotNull> generics; explicit ContainsGenerics(NotNull> generics) - : generics{generics} + : TypeOnceVisitor("ContainsGenerics") + , generics{generics} { } @@ -2139,7 +2148,7 @@ bool ConstraintSolver::tryDispatchHasIndexer( Set& seen ) { - RecursionLimiter _rl{&recursionDepth, FInt::LuauSolverRecursionLimit}; + RecursionLimiter _rl{"ConstraintSolver::tryDispatchHasIndexer", &recursionDepth, FInt::LuauSolverRecursionLimit}; subjectType = follow(subjectType); indexType = follow(indexType); @@ -2315,6 +2324,11 @@ struct BlockedTypeFinder : TypeOnceVisitor { std::optional blocked; + BlockedTypeFinder() + : TypeOnceVisitor("ContainsGenerics_DEPRECATED") + { + } + bool visit(TypeId ty) override { // If we've already found one, stop traversing. @@ -2873,7 +2887,7 @@ struct FindAllUnionMembers : TypeOnceVisitor TypeIds blockedTys; FindAllUnionMembers() - : TypeOnceVisitor(/* skipBoundTypes */ true) + : TypeOnceVisitor("FindAllUnionMembers", /* skipBoundTypes */ true) { } @@ -2959,7 +2973,7 @@ struct ContainsAnyGeneric final : public TypeOnceVisitor bool found = false; explicit ContainsAnyGeneric() - : TypeOnceVisitor(true) + : TypeOnceVisitor("ContainsAnyGeneric", /* skipBoundTypes */ true) { } @@ -2971,7 +2985,10 @@ struct ContainsAnyGeneric final : public TypeOnceVisitor bool visit(TypePackId ty) override { - found = found || is(ty); + if (FFlag::LuauContainsAnyGenericFollowBeforeChecking) + found = found || is(follow(ty)); + else + found = found || is(ty); return !found; } @@ -3367,7 +3384,8 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( return {{}, builtinTypes->errorType}; } - TypeId indexType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? follow(*indexProp->second.readTy) : follow(indexProp->second.type_DEPRECATED()); + TypeId indexType = + FFlag::LuauRemoveTypeCallsForReadWriteProps ? follow(*indexProp->second.readTy) : follow(indexProp->second.type_DEPRECATED()); if (auto ft = get(indexType)) { @@ -3654,7 +3672,8 @@ struct Blocker : TypeOnceVisitor bool blocked = false; explicit Blocker(NotNull solver, NotNull constraint) - : solver(solver) + : TypeOnceVisitor("Blocker") + , solver(solver) , constraint(constraint) { } diff --git a/Analysis/src/EmbeddedBuiltinDefinitions.cpp b/Analysis/src/EmbeddedBuiltinDefinitions.cpp index 52e36d82..8ae1d314 100644 --- a/Analysis/src/EmbeddedBuiltinDefinitions.cpp +++ b/Analysis/src/EmbeddedBuiltinDefinitions.cpp @@ -1,7 +1,6 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/BuiltinDefinitions.h" -LUAU_FASTFLAG(LuauDeclareExternType) LUAU_FASTFLAG(LuauTypeFunOptional) namespace Luau @@ -262,8 +261,7 @@ declare buffer: { )BUILTIN_SRC"; -static const char* const kBuiltinDefinitionVectorSrc = (FFlag::LuauDeclareExternType) - ? R"BUILTIN_SRC( +static const char* const kBuiltinDefinitionVectorSrc = R"BUILTIN_SRC( -- While vector would have been better represented as a built-in primitive type, type solver extern type handling covers most of the properties declare extern type vector with @@ -291,35 +289,6 @@ declare vector: { one: vector, } -)BUILTIN_SRC" - : R"BUILTIN_SRC( - --- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties -declare class vector - x: number - y: number - z: number -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() diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index 146ddca2..be0fbdb8 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -870,7 +870,7 @@ struct ErrorConverter std::string operator()(const CannotCheckDynamicStringFormatCalls& e) const { return "We cannot statically check the type of `string.format` when called with a format string that is not statically known.\n" - "If you'd like to use an unchecked `string.format` call, you can cast the format string to `any` using `:: any`."; + "If you'd like to use an unchecked `string.format` call, you can cast the format string to `any` using `:: any`."; } diff --git a/Analysis/src/ExpectedTypeVisitor.cpp b/Analysis/src/ExpectedTypeVisitor.cpp index 1aa3a073..e1bebaaf 100644 --- a/Analysis/src/ExpectedTypeVisitor.cpp +++ b/Analysis/src/ExpectedTypeVisitor.cpp @@ -81,7 +81,9 @@ struct IndexerIndexCollector : public TypeOnceVisitor { NotNull indexes; - explicit IndexerIndexCollector(NotNull indexes) : TypeOnceVisitor(/* skipBoundTypes */ true), indexes(indexes) + explicit IndexerIndexCollector(NotNull indexes) + : TypeOnceVisitor("IndexerIndexCollector", /* skipBoundTypes */ true) + , indexes(indexes) { } @@ -100,7 +102,6 @@ struct IndexerIndexCollector : public TypeOnceVisitor { return true; } - }; struct IndexCollector : public TypeOnceVisitor @@ -108,7 +109,9 @@ struct IndexCollector : public TypeOnceVisitor NotNull arena; TypeIds indexes; - explicit IndexCollector(NotNull arena) : TypeOnceVisitor(/* skipBoundTypes */ true), arena(arena) + explicit IndexCollector(NotNull arena) + : TypeOnceVisitor("IndexCollector", /* skipBoundTypes */ true) + , arena(arena) { } @@ -140,10 +143,9 @@ struct IndexCollector : public TypeOnceVisitor return false; } - }; -} +} // namespace bool ExpectedTypeVisitor::visit(AstExprIndexExpr* expr) { @@ -156,17 +158,11 @@ bool ExpectedTypeVisitor::visit(AstExprIndexExpr* expr) ic.traverse(*ty); if (ic.indexes.size() > 1) { - applyExpectedType( - arena->addType(UnionType{ic.indexes.take()}), - expr->index - ); + applyExpectedType(arena->addType(UnionType{ic.indexes.take()}), expr->index); } else if (ic.indexes.size() == 1) { - applyExpectedType( - *ic.indexes.begin(), - expr->index - ); + applyExpectedType(*ic.indexes.begin(), expr->index); } } @@ -314,4 +310,4 @@ void ExpectedTypeVisitor::applyExpectedType(TypeId expectedType, const AstExpr* } } -} // namespace Luau \ No newline at end of file +} // namespace Luau diff --git a/Analysis/src/FragmentAutocomplete.cpp b/Analysis/src/FragmentAutocomplete.cpp index d1d0f7a1..8e5f99d0 100644 --- a/Analysis/src/FragmentAutocomplete.cpp +++ b/Analysis/src/FragmentAutocomplete.cpp @@ -619,11 +619,10 @@ struct UsageFinder : public AstVisitor { auto def = dfg->getDef(local); localBindingsReferenced.emplace_back(def, local->local); - symbolsToRefine.emplace_back(def, Symbol(local->local)); + symbolsToRefine.emplace_back(def, Symbol(local->local)); } else localBindingsReferenced.emplace_back(dfg->getDef(local), local->local); - } return true; } @@ -635,7 +634,7 @@ struct UsageFinder : public AstVisitor if (FFlag::LuauFragmentAutocompleteTracksRValueRefinements) { auto def = dfg->getDef(global); - symbolsToRefine.emplace_back(def, Symbol(global->name)); + symbolsToRefine.emplace_back(def, Symbol(global->name)); } return true; } diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 88917480..ed920643 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -1016,7 +1016,8 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) return; } - ModulePtr module = check(sourceModule, mode, requireCycles, environmentScope, /*forAutocomplete*/ false, item.recordJsonLog, std::move(typeCheckLimits)); + ModulePtr module = + check(sourceModule, mode, requireCycles, environmentScope, /*forAutocomplete*/ false, item.recordJsonLog, std::move(typeCheckLimits)); double duration = getTimestamp() - timestamp; @@ -1368,6 +1369,11 @@ ModulePtr check( struct InternalTypeFinder : TypeOnceVisitor { + InternalTypeFinder() + : TypeOnceVisitor("InternalTypeFinder") + { + } + bool visit(TypeId, const ExternType&) override { return false; diff --git a/Analysis/src/Generalization.cpp b/Analysis/src/Generalization.cpp index ebf96c61..c0c284a8 100644 --- a/Analysis/src/Generalization.cpp +++ b/Analysis/src/Generalization.cpp @@ -46,7 +46,7 @@ struct MutatingGeneralizer : TypeOnceVisitor DenseHashMap positiveTypes, DenseHashMap negativeTypes ) - : TypeOnceVisitor(/* skipBoundTypes */ true) + : TypeOnceVisitor("MutatingGeneralizer", /* skipBoundTypes */ true) , arena(arena) , builtinTypes(builtinTypes) , scope(scope) @@ -338,7 +338,7 @@ struct FreeTypeSearcher : TypeVisitor NotNull> cachedTypes; explicit FreeTypeSearcher(NotNull scope, NotNull> cachedTypes) - : TypeVisitor(/*skipBoundTypes*/ true) + : TypeVisitor("FreeTypeSearcher", /*skipBoundTypes*/ true) , scope(scope) , cachedTypes(cachedTypes) { @@ -649,7 +649,7 @@ struct TypeCacher : TypeOnceVisitor DenseHashSet uncacheablePacks{nullptr}; explicit TypeCacher(NotNull> cachedTypes) - : TypeOnceVisitor(/* skipBoundTypes */ false) + : TypeOnceVisitor("TypeCacher", /* skipBoundTypes */ false) , cachedTypes(cachedTypes) { } @@ -1136,7 +1136,6 @@ struct TypeRemover } } } - }; void removeType(NotNull arena, NotNull builtinTypes, TypeId haystack, TypeId needle) @@ -1145,7 +1144,7 @@ void removeType(NotNull arena, NotNull builtinTypes, Ty tr.process(haystack); } -} +} // namespace GeneralizationResult generalizeType( NotNull arena, @@ -1410,7 +1409,8 @@ struct GenericCounter : TypeVisitor Polarity polarity = Polarity::Positive; explicit GenericCounter(NotNull> cachedTypes) - : cachedTypes(cachedTypes) + : TypeVisitor("GenericCounter") + , cachedTypes(cachedTypes) { } diff --git a/Analysis/src/InferPolarity.cpp b/Analysis/src/InferPolarity.cpp index 57144a74..1c0222c6 100644 --- a/Analysis/src/InferPolarity.cpp +++ b/Analysis/src/InferPolarity.cpp @@ -23,7 +23,8 @@ struct InferPolarity : TypeVisitor Polarity polarity = Polarity::Positive; explicit InferPolarity(NotNull arena, NotNull scope) - : arena(arena) + : TypeVisitor("InferPolarity") + , arena(arena) , scope(scope) { } diff --git a/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index d4ae66eb..e7a6e672 100644 --- a/Analysis/src/Normalize.cpp +++ b/Analysis/src/Normalize.cpp @@ -25,6 +25,7 @@ LUAU_FASTFLAGVARIABLE(LuauNormalizationIntersectTablesPreservesExternTypes) LUAU_FASTFLAGVARIABLE(LuauNormalizationReorderFreeTypeIntersect) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) +LUAU_FASTFLAGVARIABLE(LuauNormalizationLimitTyvarUnionSize) namespace Luau { @@ -1530,6 +1531,12 @@ NormalizationResult Normalizer::unionNormals(NormalizedType& here, const Normali return NormalizationResult::True; } + if (FFlag::LuauNormalizationLimitTyvarUnionSize) + { + if (here.tyvars.size() * there.tyvars.size() >= size_t(FInt::LuauNormalizeUnionLimit)) + return NormalizationResult::HitLimits; + } + for (auto it = there.tyvars.begin(); it != there.tyvars.end(); it++) { TypeId tyvar = it->first; diff --git a/Analysis/src/OverloadResolution.cpp b/Analysis/src/OverloadResolution.cpp index 9d7b71d0..d1e68f6a 100644 --- a/Analysis/src/OverloadResolution.cpp +++ b/Analysis/src/OverloadResolution.cpp @@ -11,7 +11,7 @@ #include "Luau/Unifier2.h" LUAU_FASTFLAGVARIABLE(LuauArityMismatchOnUndersaturatedUnknownArguments) -LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) namespace Luau { @@ -247,9 +247,7 @@ std::pair OverloadResolver::checkOverload_ ) { TypeFunctionContext context{arena, builtinTypes, scope, simplifier, normalizer, typeFunctionRuntime, ice, limits}; - FunctionGraphReductionResult result = reduceTypeFunctions( - fnTy, callLoc, NotNull{&context}, /*force=*/true - ); + FunctionGraphReductionResult result = reduceTypeFunctions(fnTy, callLoc, NotNull{&context}, /*force=*/true); if (!result.errors.empty()) return {OverloadIsNonviable, result.errors}; @@ -330,7 +328,7 @@ std::pair OverloadResolver::checkOverload_ return {Analysis::Ok, {}}; } - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) { // If we have an arity mismatch with generic type pack parameters, then subPath matches Args :: Tail :: ... // and superPath matches Args :: ... @@ -405,15 +403,15 @@ std::pair OverloadResolver::checkOverload_ : argExprs->size() != 0 ? argExprs->back()->location : fnExpr->location; - std::optional failedSubTy = FFlag::LuauReturnMappedGenericPacksFromSubtyping + std::optional failedSubTy = FFlag::LuauReturnMappedGenericPacksFromSubtyping2 ? traverseForType(fnTy, reason.subPath, builtinTypes, NotNull{&sr.mappedGenericPacks}, arena) : traverseForType_DEPRECATED(fnTy, reason.subPath, builtinTypes); std::optional failedSuperTy = - FFlag::LuauReturnMappedGenericPacksFromSubtyping + FFlag::LuauReturnMappedGenericPacksFromSubtyping2 ? traverseForType(prospectiveFunction, reason.superPath, builtinTypes, NotNull{&sr.mappedGenericPacks}, arena) : traverseForType_DEPRECATED(prospectiveFunction, reason.superPath, builtinTypes); - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) maybeEmplaceError(&errors, argLocation, &reason, failedSubTy, failedSuperTy); else if (failedSubTy && failedSuperTy) { @@ -443,7 +441,7 @@ std::pair OverloadResolver::checkOverload_ } } } - else if (FFlag::LuauReturnMappedGenericPacksFromSubtyping && reason.superPath.components.size() > 1) + else if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2 && reason.superPath.components.size() > 1) { // traverseForIndex only has a value if path is of form [...PackSlice, Index] if (const auto index = @@ -466,11 +464,11 @@ std::pair OverloadResolver::checkOverload_ } } - std::optional failedSubPack = FFlag::LuauReturnMappedGenericPacksFromSubtyping + std::optional failedSubPack = FFlag::LuauReturnMappedGenericPacksFromSubtyping2 ? traverseForPack(fnTy, reason.subPath, builtinTypes, NotNull{&sr.mappedGenericPacks}, arena) : traverseForPack_DEPRECATED(fnTy, reason.subPath, builtinTypes); std::optional failedSuperPack = - FFlag::LuauReturnMappedGenericPacksFromSubtyping + FFlag::LuauReturnMappedGenericPacksFromSubtyping2 ? traverseForPack(prospectiveFunction, reason.superPath, builtinTypes, NotNull{&sr.mappedGenericPacks}, arena) : traverseForPack_DEPRECATED(prospectiveFunction, reason.superPath, builtinTypes); diff --git a/Analysis/src/Quantify.cpp b/Analysis/src/Quantify.cpp index 89f0ebf7..8d5cfbbf 100644 --- a/Analysis/src/Quantify.cpp +++ b/Analysis/src/Quantify.cpp @@ -21,7 +21,8 @@ struct Quantifier final : TypeOnceVisitor bool seenMutableType = false; explicit Quantifier(TypeLevel level) - : level(level) + : TypeOnceVisitor("Quantifier") + , level(level) { } diff --git a/Analysis/src/Scope.cpp b/Analysis/src/Scope.cpp index 93b23302..c3509cc0 100644 --- a/Analysis/src/Scope.cpp +++ b/Analysis/src/Scope.cpp @@ -229,9 +229,9 @@ void Scope::inheritAssignments(const ScopePtr& childScope) { if (!FFlag::LuauSolverV2) return; - + for (const auto& [k, a] : childScope->lvalueTypes) - lvalueTypes[k] = a; + lvalueTypes[k] = a; } } diff --git a/Analysis/src/Simplify.cpp b/Analysis/src/Simplify.cpp index 5621bf0a..19aee333 100644 --- a/Analysis/src/Simplify.cpp +++ b/Analysis/src/Simplify.cpp @@ -23,6 +23,7 @@ LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauRelateTablesAreNeverDisjoint) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAGVARIABLE(LuauMissingSeenSetRelate) +LUAU_FASTFLAGVARIABLE(LuauSimplifyAnyAndUnion) namespace Luau { @@ -1426,7 +1427,7 @@ std::optional TypeSimplifier::basicIntersect(TypeId left, TypeId right) TypeId TypeSimplifier::intersect(TypeId left, TypeId right) { - RecursionLimiter rl(&recursionDepth, 15); + RecursionLimiter rl("TypeSimplifier::intersect", &recursionDepth, 15); left = simplify(left); right = simplify(right); @@ -1442,6 +1443,10 @@ TypeId TypeSimplifier::intersect(TypeId left, TypeId right) return right; if (get(right) && !get(left)) return left; + if (FFlag::LuauSimplifyAnyAndUnion && get(left) && get(right)) + return union_(builtinTypes->errorType, right); + if (FFlag::LuauSimplifyAnyAndUnion && get(left) && get(right)) + return union_(builtinTypes->errorType, left); if (get(left)) return arena->addType(UnionType{{right, builtinTypes->errorType}}); if (get(right)) @@ -1514,7 +1519,7 @@ TypeId TypeSimplifier::intersect(TypeId left, TypeId right) TypeId TypeSimplifier::union_(TypeId left, TypeId right) { - RecursionLimiter rl(&recursionDepth, 15); + RecursionLimiter rl("TypeSimplifier::union", &recursionDepth, 15); left = simplify(left); right = simplify(right); @@ -1602,7 +1607,7 @@ TypeId TypeSimplifier::simplify(TypeId ty) TypeId TypeSimplifier::simplify(TypeId ty, DenseHashSet& seen) { - RecursionLimiter limiter(&recursionDepth, 60); + RecursionLimiter limiter("TypeSimplifier::simplify", &recursionDepth, 60); ty = follow(ty); @@ -1843,7 +1848,7 @@ std::optional TypeSimplifier::intersectWithSimpleDiscriminant(TypeId tar return builtinTypes->neverType; if (property->writeTy && is(follow(property->writeTy))) return builtinTypes->neverType; - + // If the property we get back is pointer identical to the // original property, return the underlying property as an // optimization. diff --git a/Analysis/src/Subtyping.cpp b/Analysis/src/Subtyping.cpp index 538e9ae4..241253f5 100644 --- a/Analysis/src/Subtyping.cpp +++ b/Analysis/src/Subtyping.cpp @@ -2,6 +2,7 @@ #include "Luau/Subtyping.h" +#include "iostream" #include "Luau/Common.h" #include "Luau/Error.h" #include "Luau/Normalize.h" @@ -21,7 +22,7 @@ LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100) LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) -LUAU_FASTFLAGVARIABLE(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAGVARIABLE(LuauReturnMappedGenericPacksFromSubtyping2) namespace Luau { @@ -78,7 +79,7 @@ static void assertReasoningValid_DEPRECATED(TID subTy, TID superTy, const Subtyp template static void assertReasoningValid(TID subTy, TID superTy, const SubtypingResult& result, NotNull builtinTypes, NotNull arena) { - LUAU_ASSERT(FFlag::LuauReturnMappedGenericPacksFromSubtyping); + LUAU_ASSERT(FFlag::LuauReturnMappedGenericPacksFromSubtyping2); if (!FFlag::DebugLuauSubtypingCheckPathValidity) return; @@ -513,7 +514,7 @@ SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy, NotNull p{subTy, superTy}; - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping && !env.mappedGenericPacks.empty()) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2 && !env.mappedGenericPacks.empty()) result.mappedGenericPacks = env.mappedGenericPacks; if (result.isCacheable) @@ -572,6 +573,30 @@ struct SeenSetPopper seenTypes->erase(pair); } }; + +struct SeenTypePackSetPopper +{ + Subtyping::SeenTypePackSet* seenTypes; + std::pair pair; + + SeenTypePackSetPopper(Subtyping::SeenTypePackSet* seenTypes, std::pair pair) + : seenTypes(seenTypes) + , pair(std::move(pair)) + { + LUAU_ASSERT(FFlag::LuauReturnMappedGenericPacksFromSubtyping2); + } + + SeenTypePackSetPopper(const SeenTypePackSetPopper&) = delete; + SeenTypePackSetPopper& operator=(const SeenTypePackSetPopper&) = delete; + SeenTypePackSetPopper(SeenTypePackSetPopper&&) = delete; + SeenTypePackSetPopper& operator=(SeenTypePackSetPopper&&) = delete; + + ~SeenTypePackSetPopper() + { + LUAU_ASSERT(FFlag::LuauReturnMappedGenericPacksFromSubtyping2); + seenTypes->erase(pair); + } +}; } // namespace SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy, NotNull scope) @@ -598,7 +623,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub const SubtypingResult* cachedResult = resultCache.find({subTy, superTy}); if (cachedResult) { - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) { for (const auto& [genericTp, boundTp] : cachedResult->mappedGenericPacks) env.mappedGenericPacks.try_insert(genericTp, boundTp); @@ -610,7 +635,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub cachedResult = env.tryFindSubtypingResult({subTy, superTy}); if (cachedResult) { - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) { for (const auto& [genericTp, boundTp] : cachedResult->mappedGenericPacks) env.mappedGenericPacks.try_insert(genericTp, boundTp); @@ -857,7 +882,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub else if (auto p = get2(subTy, superTy)) result = isCovariantWith(env, p, scope); - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) assertReasoningValid(subTy, superTy, result, builtinTypes, arena); else assertReasoningValid_DEPRECATED(subTy, superTy, result, builtinTypes); @@ -870,6 +895,20 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId subTp = follow(subTp); superTp = follow(superTp); + std::optional popper = std::nullopt; + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) + { + std::pair typePair = {subTp, superTp}; + if (!seenPacks.insert(typePair)) + { + SubtypingResult res; + res.isSubtype = true; + res.isCacheable = false; + return res; + } + popper.emplace(&seenPacks, std::move(typePair)); + } + auto [subHead, subTail] = flatten(subTp); auto [superHead, superTail] = flatten(superTp); @@ -908,15 +947,32 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId // // (X) -> () <: (T) -> () - // Possible optimization: If headSize == 0 then we can just use subTp as-is. - std::vector headSlice = FFlag::LuauReturnMappedGenericPacksFromSubtyping - ? std::vector(begin(superHead) + headSize, end(superHead)) - : std::vector(begin(superHead), begin(superHead) + headSize); - TypePackId superTailPack = arena->addTypePack(std::move(headSlice), superTail); + TypePackId superTailPack; + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) + { + if (headSize == 0) + superTailPack = superTp; + else if (headSize == superHead.size()) + superTailPack = superTail ? *superTail : builtinTypes->emptyTypePack; + else + { + auto superHeadIter = begin(superHead); + for (size_t i = 0; i < headSize; ++i) + ++superHeadIter; + std::vector headSlice(std::move(superHeadIter), end(superHead)); + superTailPack = arena->addTypePack(std::move(headSlice), superTail); + } + } + else + { + // Possible optimization: If headSize == 0 then we can just use subTp as-is. + std::vector headSlice = std::vector(begin(superHead), begin(superHead) + headSize); + superTailPack = arena->addTypePack(std::move(headSlice), superTail); + } if (TypePackId* other = env.getMappedPackBounds(*subTail)) { - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) { const TypePack* tp = get(*other); if (const VariadicTypePack* vtp = tp ? get(tp->tail) : nullptr; vtp && vtp->hidden) @@ -982,15 +1038,32 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId // // (X...) -> () <: (T) -> () - // Possible optimization: If headSize == 0 then we can just use subTp as-is. - std::vector headSlice = FFlag::LuauReturnMappedGenericPacksFromSubtyping - ? std::vector(begin(subHead) + headSize, end(subHead)) - : std::vector(begin(subHead), begin(subHead) + headSize); - TypePackId subTailPack = arena->addTypePack(std::move(headSlice), subTail); + TypePackId subTailPack; + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) + { + if (headSize == 0) + subTailPack = subTp; + else if (headSize == subHead.size()) + subTailPack = subTail ? *subTail : builtinTypes->emptyTypePack; + else + { + auto subHeadIter = begin(subHead); + for (size_t i = 0; i < headSize; ++i) + ++subHeadIter; + std::vector headSlice(std::move(subHeadIter), end(subHead)); + subTailPack = arena->addTypePack(std::move(headSlice), subTail); + } + } + else + { + // Possible optimization: If headSize == 0 then we can just use subTp as-is. + std::vector headSlice = std::vector(begin(subHead), begin(subHead) + headSize); + subTailPack = arena->addTypePack(std::move(headSlice), subTail); + } if (TypePackId* other = env.getMappedPackBounds(*superTail)) { - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) { const TypePack* tp = get(*other); if (const VariadicTypePack* vtp = tp ? get(tp->tail) : nullptr; vtp && vtp->hidden) @@ -1142,7 +1215,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId SubtypingResult result = SubtypingResult::all(results); - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) assertReasoningValid(subTp, superTp, result, builtinTypes, arena); else assertReasoningValid_DEPRECATED(subTp, superTp, result, builtinTypes); @@ -1177,7 +1250,7 @@ SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, SubTy& } } - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) assertReasoningValid(subTy, superTy, result, builtinTypes, arena); else assertReasoningValid_DEPRECATED(subTy, superTy, result, builtinTypes); @@ -1198,7 +1271,7 @@ SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, SubTy&& su reasoning.variance = SubtypingVariance::Invariant; } - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) assertReasoningValid(subTy, superTy, result, builtinTypes, arena); else assertReasoningValid_DEPRECATED(subTy, superTy, result, builtinTypes); @@ -1862,7 +1935,9 @@ SubtypingResult Subtyping::isCovariantWith( if (FFlag::LuauRemoveTypeCallsForReadWriteProps) res.andAlso(isInvariantWith(env, *subProp.readTy, *superProp.readTy, scope).withBothComponent(TypePath::Property::read(name))); else - res.andAlso(isInvariantWith(env, subProp.type_DEPRECATED(), superProp.type_DEPRECATED(), scope).withBothComponent(TypePath::Property::read(name))); + res.andAlso( + isInvariantWith(env, subProp.type_DEPRECATED(), superProp.type_DEPRECATED(), scope).withBothComponent(TypePath::Property::read(name)) + ); } else { @@ -2131,6 +2206,13 @@ bool Subtyping::bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePac if (TypePackId* m = env.getMappedPackBounds(subTp)) return *m == superTp; + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) + { + // We shouldn't bind generic type packs to themselves + if (subTp == superTp) + return true; + } + env.mappedGenericPacks[subTp] = superTp; return true; diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index 18067dc8..8729d3ab 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -52,7 +52,11 @@ namespace struct FindCyclicTypes final : TypeVisitor { - FindCyclicTypes() = default; + FindCyclicTypes() + : TypeVisitor("FindCyclicTypes") + { + } + FindCyclicTypes(const FindCyclicTypes&) = delete; FindCyclicTypes& operator=(const FindCyclicTypes&) = delete; @@ -589,7 +593,7 @@ struct TypeStringifier if (FFlag::LuauSolverAgnosticStringification && FInt::DebugLuauVerboseTypeNames >= 1) state.emit(ftv.polarity); else if (FFlag::LuauSolverV2 && FInt::DebugLuauVerboseTypeNames >= 1) - state.emit(ftv.polarity); + state.emit(ftv.polarity); if (FInt::DebugLuauVerboseTypeNames >= 2) @@ -827,7 +831,9 @@ struct TypeStringifier std::string openbrace = "@@@"; std::string closedbrace = "@@@?!"; - switch (state.opts.hideTableKind ? ((FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticStringification) ? TableState::Sealed : TableState::Unsealed) : ttv.state) + switch (state.opts.hideTableKind + ? ((FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticStringification) ? TableState::Sealed : TableState::Unsealed) + : ttv.state) { case TableState::Sealed: if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticStringification) @@ -1899,6 +1905,39 @@ std::string dump(const std::optional& ty) return "nullopt"; } +std::string dump(const std::vector& types) +{ + return toStringVector(types, dumpOptions()); +} + +std::string dump(DenseHashMap& types) +{ + std::string s = "{"; + ToStringOptions& opts = dumpOptions(); + for (const auto& [key, value] : types) + { + if (s.length() == 1) + s += ", "; + s += toString(key, opts) + " : " + toString(value, opts); + } + s += "}"; + return s; +} + +std::string dump(DenseHashMap& types) +{ + std::string s = "{"; + ToStringOptions& opts = dumpOptions(); + for (const auto& [key, value] : types) + { + if (s.length() == 1) + s += ", "; + s += toString(key, opts) + " : " + toString(value, opts); + } + s += "}"; + return s; +} + std::string dump(const ScopePtr& scope, const char* name) { auto binding = scope->linearSearchForBinding(name); @@ -2005,7 +2044,7 @@ std::string toString(const Constraint& constraint, ToStringOptions& opts) return tos(c.resultType) + " ~ hasIndexer " + tos(c.subjectType) + " " + tos(c.indexType); } else if constexpr (std::is_same_v) - return tos(c.propType) + " ~ assignProp " + tos(c.lhsType) + " " + c.propName + " " + tos(c.rhsType); + return tos(c.propType) + " ~ assignProp " + tos(c.lhsType) + " " + c.propName + " " + tos(c.rhsType); else if constexpr (std::is_same_v) return "assignIndex " + tos(c.lhsType) + " " + tos(c.indexType) + " " + tos(c.rhsType); else if constexpr (std::is_same_v) diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index b232665a..bd82ff1b 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -446,7 +446,7 @@ bool maybeSingleton(TypeId ty) bool hasLength(TypeId ty, DenseHashSet& seen, int* recursionCount) { - RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit); + RecursionLimiter _rl("Type::hasLength", recursionCount, FInt::LuauTypeInferRecursionLimit); ty = follow(ty); diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 26679903..bfd81696 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -37,7 +37,7 @@ LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) LUAU_FASTFLAGVARIABLE(LuauSuppressErrorsForMultipleNonviableOverloads) -LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauInferActualIfElseExprType) LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete) @@ -161,6 +161,11 @@ struct TypeFunctionFinder : TypeOnceVisitor DenseHashSet mentionedFunctions{nullptr}; DenseHashSet mentionedFunctionPacks{nullptr}; + TypeFunctionFinder() + : TypeOnceVisitor("TypeFunctionFinder") + { + } + bool visit(TypeId ty, const TypeFunctionInstanceType&) override { mentionedFunctions.insert(ty); @@ -182,6 +187,7 @@ struct InternalTypeFunctionFinder : TypeOnceVisitor DenseHashSet mentionedFunctionPacks{nullptr}; explicit InternalTypeFunctionFinder(std::vector& declStack) + : TypeOnceVisitor("InternalTypeFunctionFinder") { TypeFunctionFinder f; for (TypeId fn : declStack) @@ -2052,7 +2058,8 @@ void TypeChecker2::visit(AstExprFunction* fn) TypeFunctionReductionGuessResult result = guesser.guessTypeFunctionReductionForFunctionExpr(*fn, inferredFtv, retTy); if (result.shouldRecommendAnnotation && !get(result.guessedReturnType)) reportError( - ExplicitFunctionAnnotationRecommended{std::move(result.guessedFunctionAnnotations), result.guessedReturnType}, fn->location + ExplicitFunctionAnnotationRecommended{std::move(result.guessedFunctionAnnotations), result.guessedReturnType}, + fn->location ); } } @@ -2207,7 +2214,9 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) auto name = getIdentifierOfBaseVar(expr->left); reportError( CannotInferBinaryOperation{ - expr->op, std::move(name), isComparison ? CannotInferBinaryOperation::OpKind::Comparison : CannotInferBinaryOperation::OpKind::Operation + expr->op, + std::move(name), + isComparison ? CannotInferBinaryOperation::OpKind::Comparison : CannotInferBinaryOperation::OpKind::Operation }, expr->location ); @@ -2920,11 +2929,11 @@ Reasonings TypeChecker2::explainReasonings_(TID subTy, TID superTy, Location loc continue; std::optional optSubLeaf = - FFlag::LuauReturnMappedGenericPacksFromSubtyping + FFlag::LuauReturnMappedGenericPacksFromSubtyping2 ? traverse(subTy, reasoning.subPath, builtinTypes, NotNull{&r.mappedGenericPacks}, subtyping->arena) : traverse_DEPRECATED(subTy, reasoning.subPath, builtinTypes); std::optional optSuperLeaf = - FFlag::LuauReturnMappedGenericPacksFromSubtyping + FFlag::LuauReturnMappedGenericPacksFromSubtyping2 ? traverse(superTy, reasoning.superPath, builtinTypes, NotNull{&r.mappedGenericPacks}, subtyping->arena) : traverse_DEPRECATED(superTy, reasoning.superPath, builtinTypes); @@ -3071,7 +3080,7 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT else if (auto binExpr = expr->as(); binExpr && binExpr->op == AstExprBinary::Or) { // In this case: `{ ... } or { ... }` is literal _enough_ that - // we should do this covariant check. + // we should do this covariant check. auto relaxedExpectedLhs = module->internalTypes.addType(UnionType{{builtinTypes->falsyType, expectedType}}); bool passes = testPotentialLiteralIsSubtype(binExpr->left, relaxedExpectedLhs); passes &= testPotentialLiteralIsSubtype(binExpr->right, expectedType); diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index d2c1ed03..e3bf1012 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -60,6 +60,12 @@ struct InstanceCollector : TypeOnceVisitor std::vector typeFunctionInstanceStack; std::vector cyclicInstance; + + InstanceCollector() + : TypeOnceVisitor("InstanceCollector") + { + } + bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override { // TypeVisitor performs a depth-first traversal in the absence of @@ -146,6 +152,11 @@ struct UnscopedGenericFinder : TypeOnceVisitor std::vector scopeGenTps; bool foundUnscoped = false; + UnscopedGenericFinder() + : TypeOnceVisitor("UnscopedGenericFinder") + { + } + bool visit(TypeId ty) override { // Once we have found an unscoped generic, we will stop the traversal @@ -660,8 +671,7 @@ struct TypeFunctionReducer if (tryGuessing(subject)) return; - TypeFunctionReductionResult result = - tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, ctx); + TypeFunctionReductionResult result = tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, ctx); handleTypeFunctionReduction(subject, std::move(result)); } } diff --git a/Analysis/src/TypeFunctionReductionGuesser.cpp b/Analysis/src/TypeFunctionReductionGuesser.cpp index c00a8619..f74249d1 100644 --- a/Analysis/src/TypeFunctionReductionGuesser.cpp +++ b/Analysis/src/TypeFunctionReductionGuesser.cpp @@ -24,6 +24,11 @@ struct InstanceCollector2 : TypeOnceVisitor DenseHashSet cyclicInstance{nullptr}; DenseHashSet instanceArguments{nullptr}; + InstanceCollector2() + : TypeOnceVisitor("InstanceCollector2") + { + } + bool visit(TypeId ty, const TypeFunctionInstanceType& it) override { // TypeOnceVisitor performs a depth-first traversal in the absence of diff --git a/Analysis/src/TypeIds.cpp b/Analysis/src/TypeIds.cpp index b81ecf6a..6c379f76 100644 --- a/Analysis/src/TypeIds.cpp +++ b/Analysis/src/TypeIds.cpp @@ -157,4 +157,4 @@ std::vector TypeIds::take() return std::move(order); } -} +} // namespace Luau diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 35a66b9b..6475ffd0 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -442,7 +442,7 @@ struct InplaceDemoter : TypeOnceVisitor TypeArena* arena; InplaceDemoter(TypeLevel level, TypeArena* arena) - : TypeOnceVisitor(/* skipBoundTypes= */ true) + : TypeOnceVisitor("InplaceDemoter", /* skipBoundTypes= */ true) , newLevel(level) , arena(arena) { @@ -2150,7 +2150,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( for (TypeId t : utv) { - RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit); + RecursionLimiter _rl("TypeInfer::UnionType", &recursionCount, FInt::LuauTypeInferRecursionLimit); // Not needed when we normalize types. if (get(follow(t))) @@ -2189,7 +2189,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( for (TypeId t : itv->parts) { - RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit); + RecursionLimiter _rl("TypeInfer::IntersectionType", &recursionCount, FInt::LuauTypeInferRecursionLimit); if (std::optional ty = getIndexTypeFromType(scope, t, name, location, /* addErrors= */ false)) parts.push_back(*ty); @@ -4129,7 +4129,9 @@ void TypeChecker::checkArgumentList( auto [minParams, optMaxParams] = getParameterExtents(&state.log, paramPack); state.reportError(TypeError{ location, - CountMismatch{minParams, optMaxParams, std::distance(begin(argPack), end(argPack)), CountMismatch::Context::Arg, false, std::move(namePath)} + CountMismatch{ + minParams, optMaxParams, std::distance(begin(argPack), end(argPack)), CountMismatch::Context::Arg, false, std::move(namePath) + } }); }; @@ -4248,7 +4250,8 @@ void TypeChecker::checkArgumentList( namePath = *path; state.reportError(TypeError{ - funName.location, CountMismatch{minParams, optMaxParams, paramIndex, CountMismatch::Context::Arg, isVariadic, std::move(namePath)} + funName.location, + CountMismatch{minParams, optMaxParams, paramIndex, CountMismatch::Context::Arg, isVariadic, std::move(namePath)} }); return; } diff --git a/Analysis/src/TypePack.cpp b/Analysis/src/TypePack.cpp index 44d5433c..b163d78c 100644 --- a/Analysis/src/TypePack.cpp +++ b/Analysis/src/TypePack.cpp @@ -4,7 +4,7 @@ #include "Luau/Error.h" #include "Luau/TxnLog.h" -#include +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) namespace Luau { @@ -455,10 +455,13 @@ std::pair, std::optional> flatten(TypePackId tp, std::pair, std::optional> flatten(TypePackId tp, const DenseHashMap& mappedGenericPacks) { + LUAU_ASSERT(FFlag::LuauReturnMappedGenericPacksFromSubtyping2); + tp = mappedGenericPacks.contains(tp) ? *mappedGenericPacks.find(tp) : tp; std::vector flattened; std::optional tail = std::nullopt; + DenseHashSet seenGenericPacks{nullptr}; while (tp) { @@ -467,9 +470,10 @@ std::pair, std::optional> flatten(TypePackId tp, for (; it != end(tp); ++it) flattened.push_back(*it); - if (const auto tpTail = it.tail(); tpTail && mappedGenericPacks.contains(*tpTail)) + if (const auto tpTail = it.tail(); tpTail && !seenGenericPacks.contains(*tpTail) && mappedGenericPacks.contains(*tpTail)) { tp = *mappedGenericPacks.find(*tpTail); + seenGenericPacks.insert(*tpTail); continue; } diff --git a/Analysis/src/TypePath.cpp b/Analysis/src/TypePath.cpp index ea5606a1..b1b83d55 100644 --- a/Analysis/src/TypePath.cpp +++ b/Analysis/src/TypePath.cpp @@ -16,7 +16,7 @@ #include LUAU_FASTFLAG(LuauSolverV2); -LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) // Maximum number of steps to follow when traversing a path. May not always // equate to the number of components in a path, depending on the traversal @@ -285,7 +285,7 @@ struct TraversalState TypeOrPack current; NotNull builtinTypes; - // TODO: make these NotNull when LuauReturnMappedGenericPacksFromSubtyping is clipped + // TODO: make these NotNull when LuauReturnMappedGenericPacksFromSubtyping2 is clipped const DenseHashMap* mappedGenericPacks; TypeArena* arena; int steps = 0; @@ -417,7 +417,7 @@ struct TraversalState { auto currentPack = get(current); LUAU_ASSERT(currentPack); - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) { if (const auto tp = get(*currentPack)) { @@ -576,7 +576,7 @@ struct TraversalState if (auto tail = it.tail()) { - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping && mappedGenericPacks && mappedGenericPacks->contains(*tail)) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2 && mappedGenericPacks && mappedGenericPacks->contains(*tail)) updateCurrent(*mappedGenericPacks->find(*tail)); else updateCurrent(*tail); @@ -595,9 +595,9 @@ struct TraversalState if (checkInvariants()) return false; - // TODO: clip this check once LuauReturnMappedGenericPacksFromSubtyping is clipped + // TODO: clip this check once LuauReturnMappedGenericPacksFromSubtyping2 is clipped // arena and mappedGenericPacks should be NonNull once that happens - if (FFlag::LuauReturnMappedGenericPacksFromSubtyping) + if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2) LUAU_ASSERT(arena && mappedGenericPacks); else if (!arena || !mappedGenericPacks) return false; diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index 3708628e..e632b03a 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -33,7 +33,8 @@ struct PromoteTypeLevels final : TypeOnceVisitor TypeLevel minLevel; PromoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel) - : log(log) + : TypeOnceVisitor("PromoteTypeLevels") + , log(log) , typeArena(typeArena) , minLevel(minLevel) { @@ -145,7 +146,8 @@ void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLev struct SkipCacheForType final : TypeOnceVisitor { SkipCacheForType(const DenseHashMap& skipCacheForType, const TypeArena* typeArena) - : skipCacheForType(skipCacheForType) + : TypeOnceVisitor("SkipCacheForType") + , skipCacheForType(skipCacheForType) , typeArena(typeArena) { } @@ -404,7 +406,7 @@ static bool isBlocked(const TxnLog& log, TypePackId tp) void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection, const LiteralProperties* literalProperties) { - RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + RecursionLimiter _ra("Unifier::tryUnify_", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit); ++sharedState.counters.iterationCount; @@ -1448,7 +1450,7 @@ void Unifier::tryUnify(TypePackId subTp, TypePackId superTp, bool isFunctionCall */ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCall) { - RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + RecursionLimiter _ra("Unifier::tryUnify_", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit); ++sharedState.counters.iterationCount; @@ -2030,7 +2032,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, { if (FFlag::LuauUnifierRecursionOnRestart) { - RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + RecursionLimiter _ra("Unifier::tryUnifyTables", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit); tryUnify(subTy, superTy, false, isIntersection); return; } @@ -2048,7 +2050,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, { if (errors.empty()) { - RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + RecursionLimiter _ra("Unifier::tryUnifyTables", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit); tryUnifyTables(subTy, superTy, isIntersection); } @@ -2120,7 +2122,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, { if (FFlag::LuauUnifierRecursionOnRestart) { - RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + RecursionLimiter _ra("Unifier::tryUnifyTables", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit); tryUnify(subTy, superTy, false, isIntersection); return; } @@ -2140,7 +2142,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, { if (errors.empty()) { - RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + RecursionLimiter _ra("Unifier::tryUnifyTables", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit); tryUnifyTables(subTy, superTy, isIntersection); } @@ -2732,7 +2734,7 @@ bool Unifier::occursCheck(TypeId needle, TypeId haystack, bool reversed) bool Unifier::occursCheck(DenseHashSet& seen, TypeId needle, TypeId haystack) { - RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + RecursionLimiter _ra("Unifier::occursCheck", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit); bool occurrence = false; @@ -2806,7 +2808,7 @@ bool Unifier::occursCheck(DenseHashSet& seen, TypePackId needle, Typ if (!log.getMutable(needle) && !(hideousFixMeGenericsAreActuallyFree && log.is(needle))) ice("Expected needle pack to be free"); - RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + RecursionLimiter _ra("Unifier::occursCheck", &sharedState.counters.recursionCount, sharedState.counters.recursionLimit); while (!log.getMutable(haystack)) { diff --git a/Analysis/src/Unifier2.cpp b/Analysis/src/Unifier2.cpp index 1708f08d..c5400953 100644 --- a/Analysis/src/Unifier2.cpp +++ b/Analysis/src/Unifier2.cpp @@ -722,7 +722,7 @@ TypeId Unifier2::mkIntersection(TypeId left, TypeId right) OccursCheckResult Unifier2::occursCheck(DenseHashSet& seen, TypeId needle, TypeId haystack) { - RecursionLimiter _ra(&recursionCount, recursionLimit); + RecursionLimiter _ra("Unifier2::occursCheck", &recursionCount, recursionLimit); OccursCheckResult occurrence = OccursCheckResult::Pass; @@ -784,7 +784,7 @@ OccursCheckResult Unifier2::occursCheck(DenseHashSet& seen, TypePack if (!getMutable(needle)) ice->ice("Expected needle pack to be free"); - RecursionLimiter _ra(&recursionCount, recursionLimit); + RecursionLimiter _ra("Unifier2::occursCheck", &recursionCount, recursionLimit); while (!getMutable(haystack)) { diff --git a/Analysis/src/UserDefinedTypeFunction.cpp b/Analysis/src/UserDefinedTypeFunction.cpp index a22147df..eed5ae8b 100644 --- a/Analysis/src/UserDefinedTypeFunction.cpp +++ b/Analysis/src/UserDefinedTypeFunction.cpp @@ -39,7 +39,7 @@ private: T oldValue; }; -} +} // namespace struct FindUserTypeFunctionBlockers : TypeOnceVisitor { @@ -48,7 +48,7 @@ struct FindUserTypeFunctionBlockers : TypeOnceVisitor std::vector blockingTypes; explicit FindUserTypeFunctionBlockers(NotNull ctx) - : TypeOnceVisitor(/* skipBoundTypes */ true) + : TypeOnceVisitor("FindUserTypeFunctionBlockers", /* skipBoundTypes */ true) , ctx(ctx) { } diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 4f73fe1b..6b3db051 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -18,7 +18,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100) // flag so that we don't break production games by reverting syntax changes. // See docs/SyntaxChanges.md for an explanation. LUAU_FASTFLAGVARIABLE(LuauSolverV2) -LUAU_FASTFLAGVARIABLE(LuauDeclareExternType) LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer) LUAU_FASTFLAGVARIABLE(LuauParseAttributeFixUninit) LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false) @@ -1249,49 +1248,45 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray superName = std::nullopt; if (AstName(lexer.current().name) == "extends") { nextLexeme(); - superName = parseName(FFlag::LuauDeclareExternType ? "supertype name" : "superclass name").name; + superName = parseName("supertype name").name; } - if (FFlag::LuauDeclareExternType) + if (foundExtern) { - if (foundExtern) - { - if (AstName(lexer.current().name) != "with") - report( - lexer.current().location, - "Expected `with` keyword before listing properties of the external type, but got %s instead", - lexer.current().name - ); - else - nextLexeme(); - } + if (AstName(lexer.current().name) != "with") + report( + lexer.current().location, + "Expected `with` keyword before listing properties of the external type, but got %s instead", + lexer.current().name + ); + else + nextLexeme(); } + TempVector props(scratchDeclaredClassProps); AstTableIndexer* indexer = nullptr; @@ -1357,10 +1352,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArraylocation, "Cannot have more than one indexer on an extern type"); - else - report(badIndexer->location, "Cannot have more than one class indexer"); + report(badIndexer->location, "Cannot have more than one indexer on an extern type"); } else { @@ -1427,10 +1419,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArraylocation, "Cannot have more than one indexer on an extern type"); - else - report(badIndexer->location, "Cannot have more than one class indexer"); + report(badIndexer->location, "Cannot have more than one indexer on an extern type"); } else { @@ -1466,13 +1455,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray(Location(start, type->location), globalName->name, globalName->location, type); } - else if (FFlag::LuauDeclareExternType) - { - return reportStatError(start, {}, {}, "declare must be followed by an identifier, 'function', or 'extern type'"); - } else { - return reportStatError(start, {}, {}, "declare must be followed by an identifier, 'function', or 'class'"); + return reportStatError(start, {}, {}, "declare must be followed by an identifier, 'function', or 'extern type'"); } } diff --git a/Common/include/Luau/ExperimentalFlags.h b/Common/include/Luau/ExperimentalFlags.h index a9835216..c1e9c7d8 100644 --- a/Common/include/Luau/ExperimentalFlags.h +++ b/Common/include/Luau/ExperimentalFlags.h @@ -12,13 +12,14 @@ inline bool isAnalysisFlagExperimental(const char* flag) // or critical bugs that are found after the code has been submitted. This list is intended _only_ for flags that affect // Luau's type checking. Flags that may change runtime behavior (e.g.: parser or VM flags) are not appropriate for this list. static const char* const kList[] = { - "LuauInstantiateInSubtyping", // requires some fixes to lua-apps code - "LuauFixIndexerSubtypingOrdering", // requires some small fixes to lua-apps code since this fixes a false negative - "StudioReportLuauAny2", // takes telemetry data for usage of any types - "LuauTableCloneClonesType3", // requires fixes in lua-apps code, terrifyingly - "LuauNormalizationReorderFreeTypeIntersect", // requires fixes in lua-apps code, also terrifyingly + "LuauInstantiateInSubtyping", // requires some fixes to lua-apps code + "LuauFixIndexerSubtypingOrdering", // requires some small fixes to lua-apps code since this fixes a false negative + "StudioReportLuauAny2", // takes telemetry data for usage of any types + "LuauTableCloneClonesType3", // requires fixes in lua-apps code, terrifyingly + "LuauNormalizationReorderFreeTypeIntersect", // requires fixes in lua-apps code, also terrifyingly "LuauSolverV2", - "UseNewLuauTypeSolverDefaultEnabled", // This can change the default solver used in cli applications, so it also needs to be disabled. Will require fixes in lua-apps code + "UseNewLuauTypeSolverDefaultEnabled", // This can change the default solver used in cli applications, so it also needs to be disabled. Will + // require fixes in lua-apps code // makes sure we always have at least one entry nullptr, }; diff --git a/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index 82e0d4fd..8d2b32de 100644 --- a/Compiler/src/Compiler.cpp +++ b/Compiler/src/Compiler.cpp @@ -26,10 +26,8 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25) LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300) LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) -LUAU_FASTFLAGVARIABLE(LuauCompileInlineNonConstInit) LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo) -LUAU_FASTFLAGVARIABLE(LuauCompileFixTypeFunctionSkip) namespace Luau { @@ -696,10 +694,7 @@ struct Compiler // if the argument is a local that isn't mutated, we will simply reuse the existing register if (int reg = le ? getExprLocalReg(le) : -1; reg >= 0 && (!lv || !lv->written)) { - if (FFlag::LuauCompileInlineNonConstInit) - args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc, lv ? lv->init : nullptr}); - else - args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc}); + args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc, lv ? lv->init : nullptr}); } else { @@ -725,7 +720,7 @@ struct Compiler { pushLocal(arg.local, arg.reg, arg.allocpc); - if (FFlag::LuauCompileInlineNonConstInit && arg.init) + if (arg.init) { if (Variable* lv = variables.find(arg.local)) lv->init = arg.init; @@ -785,11 +780,8 @@ struct Compiler if (Constant* var = locstants.find(local)) var->type = Constant::Type_Unknown; - if (FFlag::LuauCompileInlineNonConstInit) - { - if (Variable* lv = variables.find(local)) - lv->init = nullptr; - } + if (Variable* lv = variables.find(local)) + lv->init = nullptr; } foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body); @@ -3956,7 +3948,7 @@ struct Compiler bool visit(AstStatTypeFunction* node) override { - return !FFlag::LuauCompileFixTypeFunctionSkip; + return false; } }; diff --git a/Compiler/src/CostModel.cpp b/Compiler/src/CostModel.cpp index 4dc6580b..1b99bf52 100644 --- a/Compiler/src/CostModel.cpp +++ b/Compiler/src/CostModel.cpp @@ -8,8 +8,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauCompileCostModelConstants) - namespace Luau { namespace Compile @@ -43,25 +41,6 @@ static uint64_t parallelMulSat(uint64_t a, int b) return r | (s - (s >> 7)); } -inline bool getNumber_DEPRECATED(AstExpr* node, double& result) -{ - // since constant model doesn't use constant folding atm, we perform the basic extraction that's sufficient to handle positive/negative literals - if (AstExprConstantNumber* ne = node->as()) - { - result = ne->value; - return true; - } - - if (AstExprUnary* ue = node->as(); ue && ue->op == AstExprUnary::Minus) - if (AstExprConstantNumber* ne = ue->expr->as()) - { - result = -ne->value; - return true; - } - - return false; -} - struct Cost { static const uint64_t kLiteral = ~0ull; @@ -132,7 +111,7 @@ struct CostVisitor : AstVisitor Cost model(AstExpr* node) { - if (FFlag::LuauCompileCostModelConstants && constants.contains(node)) + if (constants.contains(node)) return Cost(0, Cost::kLiteral); if (AstExprGroup* expr = node->as()) @@ -280,17 +259,8 @@ struct CostVisitor : AstVisitor int tripCount = -1; double from, to, step = 1; - if (FFlag::LuauCompileCostModelConstants) - { - if (getNumber(node->from, from) && getNumber(node->to, to) && (!node->step || getNumber(node->step, step))) - tripCount = getTripCount(from, to, step); - } - else - { - if (getNumber_DEPRECATED(node->from, from) && getNumber_DEPRECATED(node->to, to) && - (!node->step || getNumber_DEPRECATED(node->step, step))) - tripCount = getTripCount(from, to, step); - } + if (getNumber(node->from, from) && getNumber(node->to, to) && (!node->step || getNumber(node->step, step))) + tripCount = getTripCount(from, to, step); loop(node->body, 1, tripCount < 0 ? 3 : tripCount); return false; diff --git a/Compiler/src/ValueTracking.cpp b/Compiler/src/ValueTracking.cpp index fde9beb8..5896422b 100644 --- a/Compiler/src/ValueTracking.cpp +++ b/Compiler/src/ValueTracking.cpp @@ -3,8 +3,6 @@ #include "Luau/Lexer.h" -LUAU_FASTFLAG(LuauCompileInlineNonConstInit) - namespace Luau { namespace Compile @@ -85,11 +83,8 @@ struct ValueVisitor : AstVisitor bool visit(AstExprFunction* node) override { - if (FFlag::LuauCompileInlineNonConstInit) - { - for (AstLocal* arg : node->args) - variables[arg].init = nullptr; - } + for (AstLocal* arg : node->args) + variables[arg].init = nullptr; return true; } diff --git a/VM/src/lapi.cpp b/VM/src/lapi.cpp index a65933b5..03196d03 100644 --- a/VM/src/lapi.cpp +++ b/VM/src/lapi.cpp @@ -15,6 +15,8 @@ #include +LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauSafeStackCheck, false) + /* * This file contains most implementations of core Lua APIs from lua.h. * @@ -139,7 +141,36 @@ int lua_checkstack(lua_State* L, int size) res = 0; // stack overflow else if (size > 0) { - luaD_checkstack(L, size); + if (DFFlag::LuauSafeStackCheck) + { + if (stacklimitreached(L, size)) + { + struct CallContext + { + int size; + + static void run(lua_State* L, void* ud) + { + CallContext* ctx = (CallContext*)ud; + + luaD_growstack(L, ctx->size); + } + } ctx = {size}; + + // there could be no memory to extend the stack + if (luaD_rawrunprotected(L, &CallContext::run, &ctx) != LUA_OK) + return 0; + } + else + { + condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK, 0)); + } + } + else + { + luaD_checkstack(L, size); + } + expandstacklimit(L, L->top + size); } return res; diff --git a/VM/src/ldo.cpp b/VM/src/ldo.cpp index 814a8cd2..ec326da1 100644 --- a/VM/src/ldo.cpp +++ b/VM/src/ldo.cpp @@ -17,6 +17,8 @@ #include +LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauErrorYield, false) + // keep max stack allocation request under 1GB #define MAX_STACK_SIZE (int(1024 / sizeof(TValue)) * 1024 * 1024) @@ -249,6 +251,23 @@ void luaD_checkCstack(lua_State* L) luaD_throw(L, LUA_ERRERR); // error while handling stack error } +static void performcall(lua_State* L, StkId func, int nresults) +{ + if (luau_precall(L, func, nresults) == PCRLUA) + { // is a Lua function? + L->ci->flags |= LUA_CALLINFO_RETURN; // luau_execute will stop after returning from the stack frame + + bool oldactive = L->isactive; + L->isactive = true; + luaC_threadbarrier(L); + + luau_execute(L); // call it + + if (!oldactive) + L->isactive = false; + } +} + /* ** Call a function (C or Lua). The function to be called is at *func. ** The arguments are on the stack, right after the function. @@ -277,18 +296,25 @@ void luaD_call(lua_State* L, StkId func, int nresults) ptrdiff_t funcoffset = savestack(L, func); ptrdiff_t cioffset = saveci(L, L->ci); - if (luau_precall(L, func, nresults) == PCRLUA) - { // is a Lua function? - L->ci->flags |= LUA_CALLINFO_RETURN; // luau_execute will stop after returning from the stack frame + if (DFFlag::LuauErrorYield) + { + performcall(L, func, nresults); + } + else + { + if (luau_precall(L, func, nresults) == PCRLUA) + { // is a Lua function? + L->ci->flags |= LUA_CALLINFO_RETURN; // luau_execute will stop after returning from the stack frame - bool oldactive = L->isactive; - L->isactive = true; - luaC_threadbarrier(L); + bool oldactive = L->isactive; + L->isactive = true; + luaC_threadbarrier(L); - luau_execute(L); // call it + luau_execute(L); // call it - if (!oldactive) - L->isactive = false; + if (!oldactive) + L->isactive = false; + } } bool yielded = L->status == LUA_YIELD || L->status == LUA_BREAK; @@ -314,6 +340,27 @@ void luaD_call(lua_State* L, StkId func, int nresults) luaC_checkGC(L); } +// Non-yieldable version of luaD_call, used primarily to call an error handler which cannot yield +void luaD_callny(lua_State* L, StkId func, int nresults) +{ + if (++L->nCcalls >= LUAI_MAXCCALLS) + luaD_checkCstack(L); + + LUAU_ASSERT(L->nCcalls > L->baseCcalls); + + ptrdiff_t funcoffset = savestack(L, func); + + performcall(L, func, nresults); + + LUAU_ASSERT(L->status != LUA_YIELD && L->status != LUA_BREAK); + + if (nresults != LUA_MULTRET) + L->top = restorestack(L, funcoffset) + nresults; + + L->nCcalls--; + luaC_checkGC(L); +} + static void seterrorobj(lua_State* L, int errcode, StkId oldtop) { switch (errcode) @@ -595,7 +642,11 @@ static void callerrfunc(lua_State* L, void* ud) setobj2s(L, L->top, L->top - 1); setobj2s(L, L->top - 1, errfunc); incr_top(L); - luaD_call(L, L->top - 2, 1); + + if (DFFlag::LuauErrorYield) + luaD_callny(L, L->top - 2, 1); + else + luaD_call(L, L->top - 2, 1); } static void restore_stack_limit(lua_State* L) diff --git a/VM/src/ldo.h b/VM/src/ldo.h index 707af0ee..0a7203b5 100644 --- a/VM/src/ldo.h +++ b/VM/src/ldo.h @@ -10,15 +10,16 @@ // returns target stack for 'n' extra elements to reallocate // if possible, stack size growth factor is 2x #define getgrownstacksize(L, n) ((n) <= L->stacksize ? 2 * L->stacksize : L->stacksize + (n)) +#define stacklimitreached(L, n) ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) #define luaD_checkstackfornewci(L, n) \ - if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \ + if (stacklimitreached(L, (n))) \ luaD_reallocstack(L, getgrownstacksize(L, (n)), 1); \ else \ condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK, 1)); #define luaD_checkstack(L, n) \ - if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \ + if (stacklimitreached(L, (n))) \ luaD_growstack(L, n); \ else \ condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK, 0)); @@ -55,6 +56,7 @@ typedef void (*Pfunc)(lua_State* L, void* ud); LUAI_FUNC CallInfo* luaD_growCI(lua_State* L); LUAI_FUNC void luaD_call(lua_State* L, StkId func, int nresults); +LUAI_FUNC void luaD_callny(lua_State* L, StkId func, int nresults); LUAI_FUNC int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t oldtop, ptrdiff_t ef); LUAI_FUNC void luaD_reallocCI(lua_State* L, int newsize); LUAI_FUNC void luaD_reallocstack(lua_State* L, int newsize, int fornewci); diff --git a/VM/src/lgcdebug.cpp b/VM/src/lgcdebug.cpp index 9cef3ae9..2270bad2 100644 --- a/VM/src/lgcdebug.cpp +++ b/VM/src/lgcdebug.cpp @@ -14,6 +14,8 @@ #include #include +LUAU_FASTFLAGVARIABLE(LuauHeapDumpStringSizeOverhead) + static void validateobjref(global_State* g, GCObject* f, GCObject* t) { LUAU_ASSERT(!isdead(g, t)); @@ -651,7 +653,10 @@ static void enumedges(EnumContext* ctx, GCObject* from, TValue* data, size_t siz static void enumstring(EnumContext* ctx, TString* ts) { - enumnode(ctx, obj2gco(ts), ts->len, NULL); + if (FFlag::LuauHeapDumpStringSizeOverhead) + enumnode(ctx, obj2gco(ts), sizestring(ts->len), NULL); + else + enumnode(ctx, obj2gco(ts), ts->len, NULL); } static void enumtable(EnumContext* ctx, LuaTable* h) diff --git a/fuzz/protoprint.cpp b/fuzz/protoprint.cpp index 2aa5244e..58812e07 100644 --- a/fuzz/protoprint.cpp +++ b/fuzz/protoprint.cpp @@ -2,28 +2,200 @@ #include "luau.pb.h" static const std::string kNames[] = { - "_G", "_VERSION", "__add", "__call", "__concat", "__div", "__eq", "__idiv", "__index", - "__iter", "__le", "__len", "__lt", "__mod", "__mode", "__mul", "__namecall", "__newindex", - "__pow", "__sub", "__type", "__unm", "abs", "acos", "arshift", "asin", "assert", - "atan", "atan2", "band", "bit32", "bnot", "boolean", "bor", "btest", "buffer", - "bxor", "byte", "ceil", "char", "charpattern", "clamp", "clear", "clock", "clone", - "close", "codepoint", "codes", "collectgarbage", "concat", "copy", "coroutine", "cos", "cosh", - "countlz", "countrz", "create", "date", "debug", "deg", "difftime", "error", "exp", - "extract", "fill", "find", "floor", "fmod", "foreach", "foreachi", "format", "freeze", - "frexp", "fromstring", "function", "gcinfo", "getfenv", "getmetatable", "getn", "gmatch", "gsub", - "huge", "info", "insert", "ipairs", "isfrozen", "isyieldable", "ldexp", "len", "loadstring", - "log", "log10", "lower", "lrotate", "lshift", "match", "math", "max", "maxn", - "min", "modf", "move", "newproxy", "next", "nil", "noise", "number", "offset", - "os", "pack", "packsize", "pairs", "pcall", "pi", "pow", "print", "rad", - "random", "randomseed", "rawequal", "rawget", "rawlen", "rawset", "readf32", "readf64", "readi16", - "readi32", "readi8", "readstring", "readu16", "readu32", "readu8", "remove", "rep", "replace", - "require", "resume", "reverse", "round", "rrotate", "rshift", "running", "select", "setfenv", - "setmetatable", "sign", "sin", "sinh", "sort", "split", "sqrt", "status", "string", - "sub", "table", "tan", "tanh", "thread", "time", "tonumber", "tostring", "tostring", - "traceback", "type", "typeof", "unpack", "upper", "userdata", "utf8", "vector", "wrap", - "writef32", "writef64", "writei16", "writei32", "writei8", "writestring", "writeu16", "writeu32", "writeu8", - "xpcall", "yield", "types", "unknown", "never", "any", "singleton", "optional", "generic", - "negationof", "unionof", "intersectionof", "newtable", "newfunction", + "_G", + "_VERSION", + "__add", + "__call", + "__concat", + "__div", + "__eq", + "__idiv", + "__index", + "__iter", + "__le", + "__len", + "__lt", + "__mod", + "__mode", + "__mul", + "__namecall", + "__newindex", + "__pow", + "__sub", + "__type", + "__unm", + "abs", + "acos", + "arshift", + "asin", + "assert", + "atan", + "atan2", + "band", + "bit32", + "bnot", + "boolean", + "bor", + "btest", + "buffer", + "bxor", + "byte", + "ceil", + "char", + "charpattern", + "clamp", + "clear", + "clock", + "clone", + "close", + "codepoint", + "codes", + "collectgarbage", + "concat", + "copy", + "coroutine", + "cos", + "cosh", + "countlz", + "countrz", + "create", + "date", + "debug", + "deg", + "difftime", + "error", + "exp", + "extract", + "fill", + "find", + "floor", + "fmod", + "foreach", + "foreachi", + "format", + "freeze", + "frexp", + "fromstring", + "function", + "gcinfo", + "getfenv", + "getmetatable", + "getn", + "gmatch", + "gsub", + "huge", + "info", + "insert", + "ipairs", + "isfrozen", + "isyieldable", + "ldexp", + "len", + "loadstring", + "log", + "log10", + "lower", + "lrotate", + "lshift", + "match", + "math", + "max", + "maxn", + "min", + "modf", + "move", + "newproxy", + "next", + "nil", + "noise", + "number", + "offset", + "os", + "pack", + "packsize", + "pairs", + "pcall", + "pi", + "pow", + "print", + "rad", + "random", + "randomseed", + "rawequal", + "rawget", + "rawlen", + "rawset", + "readf32", + "readf64", + "readi16", + "readi32", + "readi8", + "readstring", + "readu16", + "readu32", + "readu8", + "remove", + "rep", + "replace", + "require", + "resume", + "reverse", + "round", + "rrotate", + "rshift", + "running", + "select", + "setfenv", + "setmetatable", + "sign", + "sin", + "sinh", + "sort", + "split", + "sqrt", + "status", + "string", + "sub", + "table", + "tan", + "tanh", + "thread", + "time", + "tonumber", + "tostring", + "tostring", + "traceback", + "type", + "typeof", + "unpack", + "upper", + "userdata", + "utf8", + "vector", + "wrap", + "writef32", + "writef64", + "writei16", + "writei32", + "writei8", + "writestring", + "writeu16", + "writeu32", + "writeu8", + "xpcall", + "yield", + "types", + "unknown", + "never", + "any", + "singleton", + "optional", + "generic", + "negationof", + "unionof", + "intersectionof", + "newtable", + "newfunction", }; static const std::string kTypes[] = { @@ -46,25 +218,8 @@ static const std::string kExternTypes[] = { }; static const std::string kBuiltinTypes[] = { - "len", - "unm", - "add", - "sub", - "mul", - "div", - "idiv", - "pow", - "mod", - "concat", - "lt", - "le", - "eq", - "keyof", - "rawkeyof", - "index", - "rawget", - "setmetatable", - "getmetatable", + "len", "unm", "add", "sub", "mul", "div", "idiv", "pow", "mod", "concat", + "lt", "le", "eq", "keyof", "rawkeyof", "index", "rawget", "setmetatable", "getmetatable", }; struct ProtoToLuau diff --git a/tests/ClassFixture.cpp b/tests/ClassFixture.cpp index 43e225e9..5f84b737 100644 --- a/tests/ClassFixture.cpp +++ b/tests/ClassFixture.cpp @@ -12,7 +12,6 @@ namespace Luau ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete) : BuiltinsFixture(prepareAutocomplete) { - } Frontend& ExternTypeFixture::getFrontend() @@ -37,7 +36,8 @@ Frontend& ExternTypeFixture::getFrontend() }; getMutable(connectionType)->props = { - {"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}}}; + {"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}} + }; TypeId baseClassType = arena.addType(ExternType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}}); getMutable(baseClassType)->props = { @@ -114,7 +114,8 @@ Frontend& ExternTypeFixture::getFrontend() {arena.addType(IntersectionType{{ makeFunction(arena, vector2InstanceType, {vector2InstanceType}, {vector2InstanceType}), makeFunction(arena, vector2InstanceType, {getBuiltins()->numberType}, {vector2InstanceType}), - }})}}}; + }})}} + }; globals.globalScope->exportedTypeBindings["Vector2"] = TypeFun{{}, vector2InstanceType}; addGlobalBinding(globals, "Vector2", vector2Type, "@test"); diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index db8ffdee..f0b46e60 100644 --- a/tests/Compiler.test.cpp +++ b/tests/Compiler.test.cpp @@ -17,15 +17,12 @@ namespace Luau std::string rep(const std::string& s, size_t n); } -LUAU_FASTFLAG(LuauCompileInlineNonConstInit) LUAU_FASTINT(LuauCompileInlineDepth) LUAU_FASTINT(LuauCompileInlineThreshold) LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost) LUAU_FASTINT(LuauCompileLoopUnrollThreshold) LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost) LUAU_FASTINT(LuauRecursionLimit) -LUAU_FASTFLAG(LuauCompileFixTypeFunctionSkip) -LUAU_FASTFLAG(LuauCompileCostModelConstants) using namespace Luau; @@ -2990,8 +2987,6 @@ TEST_CASE("TypeFunction") TEST_CASE("NoTypeFunctionsInBytecode") { - ScopedFastFlag luauCompileFixTypeFunctionSkip{FFlag::LuauCompileFixTypeFunctionSkip, true}; - Luau::BytecodeBuilder bcb; bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code); Luau::compileOrThrow(bcb, R"( @@ -3616,8 +3611,6 @@ RETURN R4 1 TEST_CASE("CostModelRemarks") { - ScopedFastFlag luauCompileCostModelConstants{FFlag::LuauCompileCostModelConstants, true}; - CHECK_EQ( compileWithRemarks(R"( local a, b = ... @@ -7624,8 +7617,6 @@ RETURN R1 1 TEST_CASE("InlineNonConstInitializers") { - ScopedFastFlag luauCompileInlineNonConstInit{FFlag::LuauCompileInlineNonConstInit, true}; - CHECK_EQ( "\n" + compileFunction( R"( @@ -7655,8 +7646,6 @@ RETURN R0 0 TEST_CASE("InlineNonConstInitializers2") { - ScopedFastFlag luauCompileInlineNonConstInit{FFlag::LuauCompileInlineNonConstInit, true}; - CHECK_EQ( "\n" + compileFunction( R"( diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index 58c7869c..df8b74b9 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -34,9 +34,13 @@ void luaC_validate(lua_State* L); // internal functions, declared in lvm.h - not exposed via lua.h void luau_callhook(lua_State* L, lua_Hook hook, void* userdata); +LUAU_FASTFLAG(LuauHeapDumpStringSizeOverhead) LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) +LUAU_DYNAMIC_FASTFLAG(LuauErrorYield) +LUAU_DYNAMIC_FASTFLAG(LuauSafeStackCheck) + static lua_CompileOptions defaultOptions() { @@ -754,6 +758,8 @@ TEST_CASE("Closure") TEST_CASE("Calls") { + ScopedFastFlag luauSafeStackCheck{DFFlag::LuauSafeStackCheck, true}; + runConformance("calls.luau"); } @@ -816,6 +822,8 @@ TEST_CASE("UTF8") TEST_CASE("Coroutine") { + ScopedFastFlag luauErrorYield{DFFlag::LuauErrorYield, true}; + runConformance("coroutine.luau"); } @@ -2094,13 +2102,33 @@ int slowlyOverflowStack(lua_State* L) TEST_CASE("ApiStack") { - StateRef globalState(luaL_newstate(), lua_close); - lua_State* L = globalState.get(); + ScopedFastFlag luauSafeStackCheck{DFFlag::LuauSafeStackCheck, true}; - lua_pushcfunction(L, slowlyOverflowStack, "foo"); - int result = lua_pcall(L, 0, 0, 0); - REQUIRE(result == LUA_ERRRUN); - CHECK(strcmp(luaL_checkstring(L, -1), "stack overflow (test)") == 0); + StateRef globalState(lua_newstate(blockableRealloc, nullptr), lua_close); + lua_State* GL = globalState.get(); + + { + lua_State* L = lua_newthread(GL); + + lua_pushcfunction(L, slowlyOverflowStack, "foo"); + int result = lua_pcall(L, 0, 0, 0); + REQUIRE(result == LUA_ERRRUN); + CHECK(strcmp(luaL_checkstring(L, -1), "stack overflow (test)") == 0); + } + + { + lua_State* L = lua_newthread(GL); + + REQUIRE(lua_checkstack(L, 100) == 1); + + blockableReallocAllowed = false; + REQUIRE(lua_checkstack(L, 1000) == 0); + blockableReallocAllowed = true; + + REQUIRE(lua_checkstack(L, 1000) == 1); + + REQUIRE(lua_checkstack(L, LUAI_MAXCSTACK * 2) == 0); + } } TEST_CASE("ApiAlloc") @@ -2315,6 +2343,8 @@ TEST_CASE("StringConversion") TEST_CASE("GCDump") { + ScopedFastFlag luauHeapDumpStringSizeOverhead{FFlag::LuauHeapDumpStringSizeOverhead, true}; + // internal function, declared in lgc.h - not exposed via lua.h extern void luaC_dump(lua_State * L, void* file, const char* (*categoryName)(lua_State* L, uint8_t memcat)); extern void luaC_enumheap( @@ -2362,14 +2392,15 @@ local function f() x[1] = math.abs(42) end function foo() - coroutine.yield() + x[2] = '' + for i = 1, 10000 do x[2] ..= '1234567890' end end foo() return f )"); lua_pushstring(CL, "=GCDump"); - lua_loadstring(CL); - lua_resume(CL, nullptr, 0); + REQUIRE(lua_loadstring(CL) == 1); + REQUIRE(lua_resume(CL, nullptr, 0) == LUA_OK); #ifdef _WIN32 const char* path = "NUL"; @@ -2380,6 +2411,7 @@ return f FILE* f = fopen(path, "w"); REQUIRE(f); + luaC_fullgc(L); luaC_dump(L, f, nullptr); fclose(f); @@ -2395,14 +2427,10 @@ return f struct EnumContext { - EnumContext() - : nodes{nullptr} - , edges{nullptr} - { - } + Luau::DenseHashMap nodes{nullptr}; + Luau::DenseHashMap edges{nullptr}; - Luau::DenseHashMap nodes; - Luau::DenseHashMap edges; + bool seenTargetString = false; } ctx; luaC_enumheap( @@ -2425,6 +2453,14 @@ return f else if (tt == LUA_TTHREAD) CHECK(sv == "thread at unnamed:1 =GCDump"); } + else if (tt == LUA_TSTRING && size >= 100000) + { + CHECK(!context.seenTargetString); + context.seenTargetString = true; + + // The only string we have in this test that is 100000 characters long should include string data overhead + CHECK(size > 100000); + } context.nodes[gco] = {gco, tt, memcat, size, name ? name : ""}; }, @@ -2437,6 +2473,7 @@ return f CHECK(!ctx.nodes.empty()); CHECK(!ctx.edges.empty()); + CHECK(ctx.seenTargetString); } TEST_CASE("Interrupt") @@ -3296,6 +3333,7 @@ TEST_CASE("HugeFunctionLoadFailure") REQUIRE_EQ(largeAllocationToFail, expectedTotalLargeAllocations); } + TEST_CASE("IrInstructionLimit") { if (!codegen || !luau_codegen_supported()) diff --git a/tests/ConstraintGeneratorFixture.cpp b/tests/ConstraintGeneratorFixture.cpp index 27a4b7de..20d1ffe8 100644 --- a/tests/ConstraintGeneratorFixture.cpp +++ b/tests/ConstraintGeneratorFixture.cpp @@ -61,7 +61,8 @@ void ConstraintGeneratorFixture::solve(const std::string& code) {}, &logger, NotNull{dfg.get()}, - {}}; + {} + }; cs.run(); } diff --git a/tests/CostModel.test.cpp b/tests/CostModel.test.cpp index 4abd69fc..41473229 100644 --- a/tests/CostModel.test.cpp +++ b/tests/CostModel.test.cpp @@ -5,8 +5,6 @@ #include "doctest.h" -LUAU_FASTFLAG(LuauCompileCostModelConstants) - using namespace Luau; namespace Luau @@ -133,37 +131,6 @@ end CHECK_EQ(5, Luau::Compile::computeCost(model, args2, 1)); } -TEST_CASE("ControlFlow") -{ - ScopedFastFlag luauCompileCostModelConstants{FFlag::LuauCompileCostModelConstants, false}; - - uint64_t model = modelFunction(R"( -function test(a) - while a < 0 do - a += 1 - end - for i=10,1,-1 do - a += 1 - end - for i in pairs({}) do - a += 1 - if a % 2 == 0 then continue end - end - repeat - a += 1 - if a % 2 == 0 then break end - until a > 10 - return a -end -)"); - - const bool args1[] = {false}; - const bool args2[] = {true}; - - CHECK_EQ(76, Luau::Compile::computeCost(model, args1, 1)); - CHECK_EQ(73, Luau::Compile::computeCost(model, args2, 1)); -} - TEST_CASE("Conditional") { uint64_t model = modelFunction(R"( diff --git a/tests/EqSatSimplification.test.cpp b/tests/EqSatSimplification.test.cpp index ff741c0b..24358e7b 100644 --- a/tests/EqSatSimplification.test.cpp +++ b/tests/EqSatSimplification.test.cpp @@ -135,7 +135,9 @@ TEST_CASE_FIXTURE(ESFixture, "string | never") TEST_CASE_FIXTURE(ESFixture, "string | never | number") { - CHECK("number | string" == simplifyStr(arena->addType(UnionType{{getBuiltins()->stringType, getBuiltins()->neverType, getBuiltins()->numberType}}))); + CHECK( + "number | string" == simplifyStr(arena->addType(UnionType{{getBuiltins()->stringType, getBuiltins()->neverType, getBuiltins()->numberType}})) + ); } TEST_CASE_FIXTURE(ESFixture, "string & string") @@ -161,9 +163,9 @@ TEST_CASE_FIXTURE(ESFixture, "never & string") TEST_CASE_FIXTURE(ESFixture, "string & (unknown | never)") { CHECK( - "string" == simplifyStr(arena->addType( - IntersectionType{{getBuiltins()->stringType, arena->addType(UnionType{{getBuiltins()->unknownType, getBuiltins()->neverType}})}} - )) + "string" == simplifyStr(arena->addType(IntersectionType{ + {getBuiltins()->stringType, arena->addType(UnionType{{getBuiltins()->unknownType, getBuiltins()->neverType}})} + })) ); } @@ -386,7 +388,8 @@ TEST_CASE_FIXTURE(ESFixture, "union") { CHECK( "number" == - simplifyStr(arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().unionFunc, {getBuiltins()->numberType, getBuiltins()->numberType}})) + simplifyStr(arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().unionFunc, {getBuiltins()->numberType, getBuiltins()->numberType}}) + ) ); } @@ -418,9 +421,9 @@ TEST_CASE_FIXTURE(ESFixture, "blocked & ~number & function") TEST_CASE_FIXTURE(ESFixture, "(number | boolean | string | nil | table) & (false | nil)") { - const TypeId t1 = arena->addType( - UnionType{{getBuiltins()->numberType, getBuiltins()->booleanType, getBuiltins()->stringType, getBuiltins()->nilType, getBuiltins()->tableType}} - ); + const TypeId t1 = arena->addType(UnionType{ + {getBuiltins()->numberType, getBuiltins()->booleanType, getBuiltins()->stringType, getBuiltins()->nilType, getBuiltins()->tableType} + }); CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, getBuiltins()->falsyType}}))); } @@ -689,7 +692,8 @@ TEST_CASE_FIXTURE(ESFixture, "lt == boolean") TEST_CASE_FIXTURE(ESFixture, "unknown & ~string") { CHECK_EQ( - "~string", simplifyStr(arena->addType(IntersectionType{{getBuiltins()->unknownType, arena->addType(NegationType{getBuiltins()->stringType})}})) + "~string", + simplifyStr(arena->addType(IntersectionType{{getBuiltins()->unknownType, arena->addType(NegationType{getBuiltins()->stringType})}})) ); } diff --git a/tests/Fixture.cpp b/tests/Fixture.cpp index 9a318845..a0a35bdd 100644 --- a/tests/Fixture.cpp +++ b/tests/Fixture.cpp @@ -696,9 +696,10 @@ Frontend& Fixture::getFrontend() &fileResolver, &configResolver, FrontendOptions{ - /* retainFullTypeGraphs= */ true, /* forAutocomplete */ false, /* runLintChecks */ false, /* randomConstraintResolutionSeed */ randomSeed} + /* retainFullTypeGraphs= */ true, /* forAutocomplete */ false, /* runLintChecks */ false, /* randomConstraintResolutionSeed */ randomSeed + } ); - + builtinTypes = f.builtinTypes; // Fixture::Fixture begins here configResolver.defaultConfig.mode = Mode::Strict; @@ -732,7 +733,6 @@ Frontend& Fixture::getFrontend() BuiltinsFixture::BuiltinsFixture(bool prepareAutocomplete) : Fixture(prepareAutocomplete) { - } Frontend& BuiltinsFixture::getFrontend() diff --git a/tests/Fixture.h b/tests/Fixture.h index 91b7bdd5..f7dcddf5 100644 --- a/tests/Fixture.h +++ b/tests/Fixture.h @@ -165,7 +165,7 @@ struct Fixture NullModuleResolver moduleResolver; std::unique_ptr sourceModule; InternalErrorReporter ice; - + std::string decorateWithTypes(const std::string& code); @@ -185,8 +185,10 @@ struct Fixture // TODO: test theory about dynamic dispatch NotNull getBuiltins(); virtual Frontend& getFrontend(); + private: bool hasDumpedErrors = false; + protected: bool forAutocomplete = false; std::optional frontend; diff --git a/tests/FragmentAutocomplete.test.cpp b/tests/FragmentAutocomplete.test.cpp index b94083bc..401f4107 100644 --- a/tests/FragmentAutocomplete.test.cpp +++ b/tests/FragmentAutocomplete.test.cpp @@ -162,7 +162,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType Position cursorPos, std::function assertions, std::optional fragmentEndPosition = std::nullopt - ) + ) { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; this->getFrontend().setLuauSolverSelectionFromWorkspace(SolverMode::New); @@ -179,7 +179,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType Position cursorPos, std::function assertions, std::optional fragmentEndPosition = std::nullopt - ) + ) { ScopedFastFlag sff{FFlag::LuauSolverV2, false}; this->getFrontend().setLuauSolverSelectionFromWorkspace(SolverMode::Old); @@ -238,7 +238,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType ParseResult parseResult = parseHelper(document); FrontendOptions options; FragmentContext context{document, parseResult, options, fragmentEndPosition}; - return Luau::tryFragmentAutocomplete(this->getFrontend(). module, cursorPos, context, nullCallback); + return Luau::tryFragmentAutocomplete(this->getFrontend().module, cursorPos, context, nullCallback); } SourceModule& getSource() @@ -3792,11 +3792,17 @@ if result.type == "ok" then result. end )"; - autocompleteFragmentInOldSolver(source, dest, Position{8, 11}, [](auto& result){ - REQUIRE(result.result); - CHECK_EQ(result.result->acResults.entryMap.count("type"), 1); - CHECK_EQ(result.result->acResults.entryMap.count("value"), 1); - }); + autocompleteFragmentInOldSolver( + source, + dest, + Position{8, 11}, + [](auto& result) + { + REQUIRE(result.result); + CHECK_EQ(result.result->acResults.entryMap.count("type"), 1); + CHECK_EQ(result.result->acResults.entryMap.count("value"), 1); + } + ); } TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tagged_union_completion_second_branch_of_union_old_solver") @@ -3825,11 +3831,17 @@ if result.type == "err" then end )"; - autocompleteFragmentInOldSolver(source, dest, Position{8, 11}, [](auto& result){ - REQUIRE(result.result); - CHECK_EQ(result.result->acResults.entryMap.count("type"), 1); - CHECK_EQ(result.result->acResults.entryMap.count("error"), 1); - }); + autocompleteFragmentInOldSolver( + source, + dest, + Position{8, 11}, + [](auto& result) + { + REQUIRE(result.result); + CHECK_EQ(result.result->acResults.entryMap.count("type"), 1); + CHECK_EQ(result.result->acResults.entryMap.count("error"), 1); + } + ); } TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tagged_union_completion_first_branch_of_union_new_solver") @@ -3858,11 +3870,17 @@ if result.type == "ok" then result. end )"; - autocompleteFragmentInNewSolver(source, dest, Position{8, 11}, [](auto& result){ - REQUIRE(result.result); - CHECK_EQ(result.result->acResults.entryMap.count("type"), 1); - CHECK_EQ(result.result->acResults.entryMap.count("value"), 1); - }); + autocompleteFragmentInNewSolver( + source, + dest, + Position{8, 11}, + [](auto& result) + { + REQUIRE(result.result); + CHECK_EQ(result.result->acResults.entryMap.count("type"), 1); + CHECK_EQ(result.result->acResults.entryMap.count("value"), 1); + } + ); } TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tagged_union_completion_second_branch_of_union_new_solver") @@ -3891,11 +3909,17 @@ if result.type == "err" then end )"; - autocompleteFragmentInNewSolver(source, dest, Position{8, 11}, [](auto& result){ - REQUIRE(result.result); - CHECK_EQ(result.result->acResults.entryMap.count("type"), 1); - CHECK_EQ(result.result->acResults.entryMap.count("error"), 1); - }); + autocompleteFragmentInNewSolver( + source, + dest, + Position{8, 11}, + [](auto& result) + { + REQUIRE(result.result); + CHECK_EQ(result.result->acResults.entryMap.count("type"), 1); + CHECK_EQ(result.result->acResults.entryMap.count("error"), 1); + } + ); } TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "inline_prop_read_on_requires_provides_results") diff --git a/tests/Frontend.test.cpp b/tests/Frontend.test.cpp index cab7b19a..afa5aa3b 100644 --- a/tests/Frontend.test.cpp +++ b/tests/Frontend.test.cpp @@ -623,7 +623,8 @@ TEST_CASE_FIXTURE(FrontendFixture, "produce_errors_for_unchanged_file_with_error getFrontend().check("Modules/A"); - fileResolver.source["Modules/A"] = "local p = 4 -- We have fixed the problem, but we didn't tell the getFrontend(). so it will not recheck this file!"; + fileResolver.source["Modules/A"] = + "local p = 4 -- We have fixed the problem, but we didn't tell the getFrontend(). so it will not recheck this file!"; CheckResult secondResult = getFrontend().check("Modules/A"); CHECK_EQ(1, secondResult.errors.size()); diff --git a/tests/Module.test.cpp b/tests/Module.test.cpp index 4b40e248..b94b79b7 100644 --- a/tests/Module.test.cpp +++ b/tests/Module.test.cpp @@ -201,7 +201,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena") TableType* exportsTable = getMutable(*exports); REQUIRE(exportsTable != nullptr); - TypeId signType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *exportsTable->props["sign"].readTy : exportsTable->props["sign"].type_DEPRECATED(); + TypeId signType = + FFlag::LuauRemoveTypeCallsForReadWriteProps ? *exportsTable->props["sign"].readTy : exportsTable->props["sign"].type_DEPRECATED(); REQUIRE(signType != nullptr); CHECK(!isInArena(signType, module->interfaceTypes)); diff --git a/tests/NonstrictMode.test.cpp b/tests/NonstrictMode.test.cpp index 683028ba..bde95018 100644 --- a/tests/NonstrictMode.test.cpp +++ b/tests/NonstrictMode.test.cpp @@ -206,7 +206,9 @@ TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any") CHECK_EQ(*getBuiltins()->anyType, *ttv->props["one"].type_DEPRECATED()); CHECK_EQ(*getBuiltins()->anyType, *ttv->props["two"].type_DEPRECATED()); - CHECK_MESSAGE(get(follow(ttv->props["three"].type_DEPRECATED())), "Should be a function: " << *ttv->props["three"].type_DEPRECATED()); + CHECK_MESSAGE( + get(follow(ttv->props["three"].type_DEPRECATED())), "Should be a function: " << *ttv->props["three"].type_DEPRECATED() + ); } TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_iterator_variables_are_any") diff --git a/tests/Normalize.test.cpp b/tests/Normalize.test.cpp index 5a98f477..b3c2785e 100644 --- a/tests/Normalize.test.cpp +++ b/tests/Normalize.test.cpp @@ -16,7 +16,7 @@ LUAU_FASTINT(LuauNormalizeIntersectionLimit) LUAU_FASTINT(LuauNormalizeUnionLimit) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) LUAU_FASTFLAG(LuauNormalizationReorderFreeTypeIntersect) -LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) using namespace Luau; namespace @@ -1223,7 +1223,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_union_type_pack_cycle") ScopedFastFlag sff[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauSimplifyOutOfLine2, true}, - {FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}, + {FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}, }; ScopedFastInt sfi{FInt::LuauTypeInferRecursionLimit, 0}; diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index 7640b2fd..ee2de442 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -18,7 +18,6 @@ LUAU_FASTINT(LuauTypeLengthLimit) LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauParseStringIndexer) -LUAU_FASTFLAG(LuauDeclareExternType) LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix) // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix @@ -1999,8 +1998,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_class_declarations") TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations") { - ScopedFastFlag sff{FFlag::LuauDeclareExternType, true}; - AstStatBlock* stat = parseEx(R"( declare extern type Foo with prop: number @@ -2051,8 +2048,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations") TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations_missing_with") { - ScopedFastFlag sff{FFlag::LuauDeclareExternType, true}; - ParseResult result = tryParse(R"( declare extern type Foo prop: number @@ -2108,8 +2103,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations_missing_with") TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations") { - ScopedFastFlag sff{FFlag::LuauDeclareExternType, true}; - AstStatBlock* stat = parseEx(R"( declare extern type Foo with prop: number @@ -2160,8 +2153,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations") TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations_missing_with") { - ScopedFastFlag sff{FFlag::LuauDeclareExternType, true}; - ParseResult result = tryParse(R"( declare extern type Foo prop: number @@ -2281,7 +2272,7 @@ TEST_CASE_FIXTURE(Fixture, "class_indexer") [number]: number end )", - (FFlag::LuauDeclareExternType) ? "Cannot have more than one indexer on an extern type" : "Cannot have more than one class indexer" + "Cannot have more than one indexer on an extern type" ); REQUIRE_EQ(1, p1.root->body.size); diff --git a/tests/RuntimeLimits.test.cpp b/tests/RuntimeLimits.test.cpp index dacbb43d..e51310d6 100644 --- a/tests/RuntimeLimits.test.cpp +++ b/tests/RuntimeLimits.test.cpp @@ -19,6 +19,9 @@ using namespace Luau; LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) +LUAU_FASTFLAG(LuauSimplifyAnyAndUnion) struct LimitFixture : BuiltinsFixture { @@ -47,7 +50,7 @@ TEST_CASE_FIXTURE(LimitFixture, "typescript_port_of_Result_type") { DOES_NOT_PASS_NEW_SOLVER_GUARD(); - constexpr const char* src = R"LUA( + constexpr const char* src = R"LUAU( --!strict -- Big thanks to Dionysusnu by letting us use this code as part of our test suite! @@ -272,11 +275,62 @@ TEST_CASE_FIXTURE(LimitFixture, "typescript_port_of_Result_type") return { Result = Result, } - )LUA"; + )LUAU"; CheckResult result = check(src); CHECK(hasError(result)); } +TEST_CASE_FIXTURE(LimitFixture, "Signal_exerpt" * doctest::timeout(0.5)) +{ + ScopedFastFlag sff[] = { + // These flags are required to surface the problem. + {FFlag::LuauSolverV2, true}, + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauPushFunctionTypesInFunctionStatement, true}, + + // And this flag is the one that fixes it. + {FFlag::LuauSimplifyAnyAndUnion, true}, + }; + + constexpr const char* src = R"LUAU( + local Signal = {} + Signal.ClassName = "Signal" + export type Signal = typeof(setmetatable( + {} :: {}, + {} :: typeof({ __index = Signal }) + )) + function Signal.new(): Signal + return nil :: any + end + + function Signal.Connect(self: Signal) + end + + function Signal.DisconnectAll(self: Signal): () + self._handlerListHead = false + end + + function Signal.Fire(self: Signal): () + local connection + rawget(connection, "_signal") + end + + function Signal.Wait(self: Signal) + connection = self:Connect(function() + connection:Disconnect() + end) + end + + function Signal.Once(self: Signal, fn: SignalHandler): Connection + connection = self:Connect(function() end) + end + )LUAU"; + + CheckResult result = check(src); + + (void)result; +} + TEST_SUITE_END(); diff --git a/tests/Simplify.test.cpp b/tests/Simplify.test.cpp index d6dc1ecf..a63ad778 100644 --- a/tests/Simplify.test.cpp +++ b/tests/Simplify.test.cpp @@ -9,6 +9,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauSimplifyAnyAndUnion) LUAU_DYNAMIC_FASTINT(LuauSimplificationComplexityLimit) namespace @@ -62,6 +63,7 @@ struct SimplifyFixture : Fixture TypeId anotherChildClassTy = nullptr; TypeId unrelatedClassTy = nullptr; + // This only affects type stringification. ScopedFastFlag sff{FFlag::LuauSolverV2, true}; SimplifyFixture() @@ -619,4 +621,26 @@ TEST_CASE_FIXTURE(SimplifyFixture, "cyclic_never_union_and_string") CHECK(getBuiltins()->stringType == union_(leftType, getBuiltins()->stringType)); } +TEST_CASE_FIXTURE(SimplifyFixture, "any & (error | string)") +{ + ScopedFastFlag sff{FFlag::LuauSimplifyAnyAndUnion, true}; + + TypeId errStringTy = arena->addType(UnionType{{getBuiltins()->errorType, getBuiltins()->stringType}}); + + auto res = intersect(builtinTypes->anyType, errStringTy); + + CHECK("*error-type* | string" == toString(res)); +} + +TEST_CASE_FIXTURE(SimplifyFixture, "(error | string) & any") +{ + ScopedFastFlag sff{FFlag::LuauSimplifyAnyAndUnion, true}; + + TypeId errStringTy = arena->addType(UnionType{{getBuiltins()->errorType, getBuiltins()->stringType}}); + + auto res = intersect(errStringTy, builtinTypes->anyType); + + CHECK("*error-type* | string" == toString(res)); +} + TEST_SUITE_END(); diff --git a/tests/Subtyping.test.cpp b/tests/Subtyping.test.cpp index db54bb15..7655acef 100644 --- a/tests/Subtyping.test.cpp +++ b/tests/Subtyping.test.cpp @@ -17,7 +17,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) using namespace Luau; @@ -1240,11 +1240,19 @@ TEST_CASE_FIXTURE(SubtypeFixture, "(...unknown) -> () <: (T...) -> ()") TEST_CASE_FIXTURE(SubtypeFixture, "bill") { TypeId a = arena.addType(TableType{ - {{"a", getBuiltins()->stringType}}, TableIndexer{getBuiltins()->stringType, getBuiltins()->numberType}, TypeLevel{}, nullptr, TableState::Sealed + {{"a", getBuiltins()->stringType}}, + TableIndexer{getBuiltins()->stringType, getBuiltins()->numberType}, + TypeLevel{}, + nullptr, + TableState::Sealed }); TypeId b = arena.addType(TableType{ - {{"a", getBuiltins()->stringType}}, TableIndexer{getBuiltins()->stringType, getBuiltins()->numberType}, TypeLevel{}, nullptr, TableState::Sealed + {{"a", getBuiltins()->stringType}}, + TableIndexer{getBuiltins()->stringType, getBuiltins()->numberType}, + TypeLevel{}, + nullptr, + TableState::Sealed }); CHECK(isSubtype(a, b).isSubtype); @@ -1387,7 +1395,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "({ x: T }) -> T <: ({ method: ({ x: T } TEST_CASE_FIXTURE(SubtypeFixture, "subtyping_reasonings_to_follow_a_reduced_type_function_instance") { - ScopedFastFlag sff{FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}; + ScopedFastFlag sff{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; TypeId longTy = arena.addType(UnionType{ {getBuiltins()->booleanType, @@ -1631,9 +1639,9 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation") TypeId bTy = arena.addType(GenericType{"B"}); getMutable(bTy)->scope = moduleScope.get(); - TypeId genericFunctionTy = - arena.addType(FunctionType{{aTy, bTy}, {}, arena.addTypePack({aTy, bTy}), arena.addTypePack({join(meet(aTy, getBuiltins()->truthyType), bTy)})} - ); + TypeId genericFunctionTy = arena.addType( + FunctionType{{aTy, bTy}, {}, arena.addTypePack({aTy, bTy}), arena.addTypePack({join(meet(aTy, getBuiltins()->truthyType), bTy)})} + ); const TypeId truthyTy = getBuiltins()->truthyType; diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp index e4eae04e..6ddfaf5b 100644 --- a/tests/ToString.test.cpp +++ b/tests/ToString.test.cpp @@ -61,7 +61,8 @@ TEST_CASE_FIXTURE(Fixture, "free_types_stringify_the_same_regardless_of_solver") { ScopedFastFlag sff{FFlag::LuauSolverAgnosticStringification, true}; TypeArena a; - TypeId t = a.addType(FreeType{getFrontend().globals.globalScope.get(), getFrontend().builtinTypes->neverType, getFrontend().builtinTypes->unknownType}); + TypeId t = + a.addType(FreeType{getFrontend().globals.globalScope.get(), getFrontend().builtinTypes->neverType, getFrontend().builtinTypes->unknownType}); CHECK_EQ("'a", toString(t)); } diff --git a/tests/TypeFunction.test.cpp b/tests/TypeFunction.test.cpp index 5d459961..020e5f30 100644 --- a/tests/TypeFunction.test.cpp +++ b/tests/TypeFunction.test.cpp @@ -744,10 +744,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_oss_crash_gh1161") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag sff[] = { - {FFlag::LuauEagerGeneralization4, true}, - {FFlag::LuauStuckTypeFunctionsStillDispatch, true} - }; + ScopedFastFlag sff[] = {{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauStuckTypeFunctionsStillDispatch, true}}; CheckResult result = check(R"( local EnumVariants = { @@ -1787,7 +1784,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_should_not_assert_on_empty_string_prop LUAU_REQUIRE_NO_ERRORS(results); CHECK_EQ(R"("" | "one")", toString(requireTypeAlias("FoobarKeys"))); CHECK_EQ(R"("" | "two")", toString(requireTypeAlias("TableKeys"))); - } struct TFFixture @@ -1796,7 +1792,10 @@ struct TFFixture NotNull arena{&arena_}; BuiltinTypes builtinTypes_; - NotNull getBuiltins(){ return NotNull{&builtinTypes_};} + NotNull getBuiltins() + { + return NotNull{&builtinTypes_}; + } ScopePtr globalScope = std::make_shared(getBuiltins()->anyTypePack); diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index 0c887117..86439b66 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -2417,10 +2417,7 @@ local function ok(idx: get<>): number return idx end )"); LUAU_REQUIRE_ERROR_COUNT(4, result); - CHECK( - toString(result.errors[1]) == - R"(Type function instance get<> is uninhabited)" - ); + CHECK(toString(result.errors[1]) == R"(Type function instance get<> is uninhabited)"); } TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_unreferenced_do_not_block") diff --git a/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index c72309dd..81d97dc8 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/tests/TypeInfer.aliases.test.cpp @@ -10,7 +10,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("TypeAliases"); @@ -1244,8 +1243,6 @@ TEST_CASE_FIXTURE(Fixture, "exported_type_function_location_is_accessible_on_mod TEST_CASE_FIXTURE(Fixture, "fuzzer_cursed_type_aliases") { - ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2, true}; - // This used to crash under the new solver: we would like this to continue // to not crash. LUAU_REQUIRE_ERRORS(check(R"( @@ -1282,8 +1279,6 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_dont_crash_on_duplicate_with_typeof") TEST_CASE_FIXTURE(Fixture, "fuzzer_more_cursed_aliases") { - ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2, true}; - LUAU_REQUIRE_ERRORS(check(R"( export type t138 = t0 export type t0 = t0 diff --git a/tests/TypeInfer.annotations.test.cpp b/tests/TypeInfer.annotations.test.cpp index 08383ac4..b8747d04 100644 --- a/tests/TypeInfer.annotations.test.cpp +++ b/tests/TypeInfer.annotations.test.cpp @@ -272,7 +272,8 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_of_value_a_via_typeof_with_assignment") LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK( - result.errors[0] == (TypeError{Location{Position{2, 29}, Position{2, 30}}, TypeMismatch{getBuiltins()->nilType, getBuiltins()->numberType}}) + result.errors[0] == + (TypeError{Location{Position{2, 29}, Position{2, 30}}, TypeMismatch{getBuiltins()->nilType, getBuiltins()->numberType}}) ); } else @@ -447,7 +448,8 @@ TEST_CASE_FIXTURE(Fixture, "self_referential_type_alias") if (FFlag::LuauRemoveTypeCallsForReadWriteProps) REQUIRE(incr->readTy); - const FunctionType* incrFunc = FFlag::LuauRemoveTypeCallsForReadWriteProps ? get(*incr->readTy) : get(incr->type_DEPRECATED()); + const FunctionType* incrFunc = + FFlag::LuauRemoveTypeCallsForReadWriteProps ? get(*incr->readTy) : get(incr->type_DEPRECATED()); REQUIRE(incrFunc); std::optional firstArg = first(incrFunc->argTypes); diff --git a/tests/TypeInfer.generics.test.cpp b/tests/TypeInfer.generics.test.cpp index d9b66bbc..94398aea 100644 --- a/tests/TypeInfer.generics.test.cpp +++ b/tests/TypeInfer.generics.test.cpp @@ -9,7 +9,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauIntersectNotNil) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) @@ -18,6 +18,7 @@ LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) +LUAU_FASTFLAG(LuauContainsAnyGenericFollowBeforeChecking) using namespace Luau; @@ -1004,7 +1005,7 @@ local TheDispatcher: Dispatcher = { TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_few") { - ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}; + ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; CheckResult result = check(R"( function test(a: number) @@ -1032,7 +1033,7 @@ wrapper(test) TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_many") { - ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}; + ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; CheckResult result = check(R"( function test2(a: number, b: string) @@ -1076,7 +1077,7 @@ wrapper(test2, 1, "") TEST_CASE_FIXTURE(Fixture, "generic_argument_pack_type_inferred_from_return") { - ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}; + ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; CheckResult result = check(R"( function test2(a: number) @@ -1106,7 +1107,7 @@ wrapper(test2, 1) TEST_CASE_FIXTURE(Fixture, "generic_argument_pack_type_inferred_from_return_no_error") { - ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}; + ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; CheckResult result = check(R"( function test2(a: number) @@ -1124,7 +1125,7 @@ wrapper(test2, "hello") TEST_CASE_FIXTURE(Fixture, "nested_generic_argument_type_packs") { - ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}; + ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; CheckResult result = check(R"( function test2(a: number) @@ -1851,4 +1852,63 @@ end LUAU_REQUIRE_NO_ERRORS(result); } +TEST_CASE_FIXTURE(Fixture, "generic_type_packs_shouldnt_be_bound_to_themselves") +{ + ScopedFastFlag flags[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauSubtypingCheckFunctionGenericCounts, true}, + {FFlag::LuauEagerGeneralization4, true} + }; + + CheckResult result = check(R"( +export type t1 = { + foo: (self: t1, bar: (T...) -> ()) -> () +} + +export type t2 = { + baz: (self: t2) -> t1, +} + +export type t3 = { + f: (self: t3, T...)-> (), + g: t1, + h: t1<(Player, T...)> +} + +local t2 = {} + +function t2.new(): t2 +end + +local function create_t3(): t3 + local t2_1 = t2.new() + local t2_2 = t2.new() + local my_t3 = { + f = function(_self: t3, ...: T...) end, + g = t2_1:baz(), + h = t2_2:baz() + } + return my_t3 +end + )"); + + // Note: we just need this test not to crash + LUAU_REQUIRE_ERROR_COUNT(5, result); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "follow_bound_type_packs_in_generic_type_visitor") +{ + ScopedFastFlag _{FFlag::LuauContainsAnyGenericFollowBeforeChecking, true}; + // Note: we just need this test not to crash + check(R"( +function (_(_,_,nil)) +(if l0 then typeof else `{_:_()}`,typeof).n0(l0) +function _:_():typeof() +end +function _:_().typeof() +end +end + )"); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.intersectionTypes.test.cpp b/tests/TypeInfer.intersectionTypes.test.cpp index 701f1deb..4aa367ae 100644 --- a/tests/TypeInfer.intersectionTypes.test.cpp +++ b/tests/TypeInfer.intersectionTypes.test.cpp @@ -12,7 +12,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauRefineTablesWithReadType) -LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) @@ -1162,7 +1162,7 @@ could not be converted into TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_4") { - ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}; + ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; CheckResult result = check(R"( function f() diff --git a/tests/TypeInfer.modules.test.cpp b/tests/TypeInfer.modules.test.cpp index 3ea0832f..34338005 100644 --- a/tests/TypeInfer.modules.test.cpp +++ b/tests/TypeInfer.modules.test.cpp @@ -13,7 +13,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping) +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) using namespace Luau; @@ -823,7 +823,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cycles_dont_make_everything_any") TEST_CASE_FIXTURE(BuiltinsFixture, "cross_module_function_mutation") { - ScopedFastFlag _[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauReturnMappedGenericPacksFromSubtyping, true}}; + ScopedFastFlag _[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}}; fileResolver.source["game/A"] = R"( function test2(a: number, b: string) diff --git a/tests/TypeInfer.oop.test.cpp b/tests/TypeInfer.oop.test.cpp index 4f709507..0e199476 100644 --- a/tests/TypeInfer.oop.test.cpp +++ b/tests/TypeInfer.oop.test.cpp @@ -559,7 +559,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "textbook_class_pattern") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag sff[] ={ + ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, }; @@ -591,7 +591,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "textbook_class_pattern_2") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag sff[] ={ + ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, }; diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 44cfb485..d40da80a 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -127,7 +127,8 @@ struct RefinementExternTypeFixture : BuiltinsFixture }; TypeId optionalPart = arena.addType(UnionType{{part, getBuiltins()->nilType}}); - TypeId weldConstraint = getFrontend().globals.globalTypes.addType(ExternType{"WeldConstraint", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); + TypeId weldConstraint = + getFrontend().globals.globalTypes.addType(ExternType{"WeldConstraint", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); getMutable(weldConstraint)->props = { {"Part0", Property{optionalPart}}, {"Part1", Property{optionalPart}}, @@ -2453,7 +2454,7 @@ end) )")); } -TEST_CASE_FIXTURE(Fixture, "refinements_table_intersection_limits" * doctest::timeout(1.0)) +TEST_CASE_FIXTURE(Fixture, "refinements_table_intersection_limits" * doctest::timeout(1.5)) { CheckResult result = check(R"( --!strict diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index 86a23d95..7a305834 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -40,6 +40,7 @@ LUAU_FASTFLAG(LuauDfgForwardNilFromAndOr) LUAU_FASTFLAG(LuauInferActualIfElseExprType) LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) +LUAU_FASTFLAG(LuauNormalizationLimitTyvarUnionSize) TEST_SUITE_BEGIN("TableTests"); @@ -6129,7 +6130,7 @@ TEST_CASE_FIXTURE(Fixture, "cli_119126_regression") )"); LUAU_REQUIRE_ERROR_COUNT(2, results); - for (const auto& err: results.errors) + for (const auto& err : results.errors) { auto e = get(err); REQUIRE(e); @@ -6173,5 +6174,23 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1914_access_after_assignment_with_assert CHECK_EQ("number", toString(requireType("myAge"))); } +TEST_CASE_FIXTURE(BuiltinsFixture, "cli_162179_avoid_exponential_blowup_in_normalization" * doctest::timeout(1.0)) +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSimplifyOutOfLine2, true}, + {FFlag::LuauNormalizationLimitTyvarUnionSize, true}, + }; + + const std::string source = + "local res = {\n" + rep("\"foo\",\n", 100) + "}\n" + + "local function check(index: number)\n" + + " if res[index] == \"foo\" then\n" + + " print(\"found a foo!\")\n" + + " end\n" + + "end"; + + LUAU_REQUIRE_NO_ERRORS(check(source)); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index 5ea3441a..f7867536 100644 --- a/tests/TypeInfer.test.cpp +++ b/tests/TypeInfer.test.cpp @@ -1984,7 +1984,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_table_freeze_constraint_solving") TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_assert_table_freeze_constraint_solving") { - ScopedFastFlag _ {FFlag::LuauSolverV2, true}; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; // This is the original fuzzer version of the above issue. CheckResult results = check(R"( local function l0() @@ -2383,7 +2383,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_remover_heap_use_after_free") local l249 = require(module0) _,_ = {[`{_}`]=_,[_._G._]=(_)(),[_["" + _]._G]={_=_,_=_,[_._G[_]._]=_G,},},_,(_)() )")); - } TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_assign_index_constraint") @@ -2449,7 +2448,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1815_verbatim") {FFlag::LuauInferActualIfElseExprType, true}, // This is needed so that we don't hide the string literal free types // behind a `union<_, _>` - {FFlag::LuauSimplifyOutOfLine2, true}, + {FFlag::LuauSimplifyOutOfLine2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; @@ -2476,7 +2475,6 @@ TEST_CASE_FIXTURE(Fixture, "oss_1815_verbatim") REQUIRE(err3); CHECK_EQ("\"foo\"", toString(err3->wantedType)); CHECK_EQ("\"doge2\"", toString(err3->givenType)); - } TEST_CASE_FIXTURE(Fixture, "if_then_else_bidirectional_inference") diff --git a/tests/TypeInfer.tryUnify.test.cpp b/tests/TypeInfer.tryUnify.test.cpp index 2b0efd68..25d33b4b 100644 --- a/tests/TypeInfer.tryUnify.test.cpp +++ b/tests/TypeInfer.tryUnify.test.cpp @@ -47,7 +47,8 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "compatible_functions_are_unified") }}; Type functionTwo{TypeVariant{FunctionType( - arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}), arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}) + arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}), + arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}) )}}; state.tryUnify(&functionTwo, &functionOne); @@ -291,7 +292,8 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "free_tail_is_grown_properly") { TypePackId threeNumbers = arena.addTypePack(TypePack{{getBuiltins()->numberType, getBuiltins()->numberType, getBuiltins()->numberType}, std::nullopt}); - TypePackId numberAndFreeTail = arena.addTypePack(TypePack{{getBuiltins()->numberType}, arena.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}})}); + TypePackId numberAndFreeTail = + arena.addTypePack(TypePack{{getBuiltins()->numberType}, arena.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}})}); CHECK(state.canUnify(numberAndFreeTail, threeNumbers).empty()); } diff --git a/tests/TypeInfer.typestates.test.cpp b/tests/TypeInfer.typestates.test.cpp index ae8a3aa3..602443ed 100644 --- a/tests/TypeInfer.typestates.test.cpp +++ b/tests/TypeInfer.typestates.test.cpp @@ -710,9 +710,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring") TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring_in_loop") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, {FFlag::LuauDfgAllowUpdatesInLoops, true} - }; + ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauDfgAllowUpdatesInLoops, true}}; CheckResult result = check(R"( --!strict diff --git a/tests/TypeInfer.unionTypes.test.cpp b/tests/TypeInfer.unionTypes.test.cpp index de7971c6..3f90356b 100644 --- a/tests/TypeInfer.unionTypes.test.cpp +++ b/tests/TypeInfer.unionTypes.test.cpp @@ -633,7 +633,11 @@ TEST_CASE_FIXTURE(Fixture, "indexing_into_a_cyclic_union_doesnt_crash") u.options.push_back(badCyclicUnionTy); u.options.push_back(arena.addType(TableType{ - {}, TableIndexer{getBuiltins()->numberType, getBuiltins()->numberType}, TypeLevel{}, getFrontend().globals.globalScope.get(), TableState::Sealed + {}, + TableIndexer{getBuiltins()->numberType, getBuiltins()->numberType}, + TypeLevel{}, + getFrontend().globals.globalScope.get(), + TableState::Sealed })); asMutable(badCyclicUnionTy)->ty.emplace(std::move(u)); diff --git a/tests/TypePath.test.cpp b/tests/TypePath.test.cpp index 4024a494..2a6908f0 100644 --- a/tests/TypePath.test.cpp +++ b/tests/TypePath.test.cpp @@ -18,6 +18,8 @@ using namespace Luau::TypePath; LUAU_FASTFLAG(LuauSolverV2); LUAU_DYNAMIC_FASTINT(LuauTypePathMaximumTraverseSteps); +LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2); + struct TypePathFixture : Fixture { ScopedFastFlag sff1{FFlag::LuauSolverV2, true}; @@ -494,6 +496,8 @@ TEST_CASE_FIXTURE(TypePathFixture, "tail") TEST_CASE_FIXTURE(TypePathFixture, "pack_slice_has_tail") { + ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); @@ -510,6 +514,8 @@ TEST_CASE_FIXTURE(TypePathFixture, "pack_slice_has_tail") TEST_CASE_FIXTURE(TypePathFixture, "pack_slice_finite_pack") { + ScopedFastFlag _{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true}; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); diff --git a/tests/TypeVar.test.cpp b/tests/TypeVar.test.cpp index b71fee79..7274ed48 100644 --- a/tests/TypeVar.test.cpp +++ b/tests/TypeVar.test.cpp @@ -365,6 +365,12 @@ struct VisitCountTracker final : TypeOnceVisitor std::unordered_map tyVisits; std::unordered_map tpVisits; + VisitCountTracker() + : TypeOnceVisitor("VisitCountTracker") + { + } + + void cycle(TypeId) override {} void cycle(TypePackId) override {} diff --git a/tests/conformance/calls.luau b/tests/conformance/calls.luau index 63ad81e1..3c8f70c1 100644 --- a/tests/conformance/calls.luau +++ b/tests/conformance/calls.luau @@ -241,7 +241,7 @@ if not limitedstack then function recurse(n, ...) return n <= 1 and (1 + #{...}) or recurse(n-1, table.unpack(table.create(4000, 1))) + 1 end local ok, msg = pcall(recurse, 19000) - assert(not ok and string.find(msg, "not enough memory")) + assert(not ok and string.find(msg, "too many results to unpack")) end return('OK') diff --git a/tests/conformance/coroutine.luau b/tests/conformance/coroutine.luau index b4f81bba..4c5534d7 100644 --- a/tests/conformance/coroutine.luau +++ b/tests/conformance/coroutine.luau @@ -382,4 +382,13 @@ do assert(st and msg == nil) end +do + local co = coroutine.wrap(xpcall) + + co(0, coroutine.yield, 0) + local status, err = pcall(co, 0, 0, 0) + assert(status == false) + assert(err == "cannot resume dead coroutine") +end + return 'OK'