From 640ebbc0a51bef0daa7c9b8c943d522dacb6a9a8 Mon Sep 17 00:00:00 2001 From: ariel Date: Fri, 28 Feb 2025 14:42:30 -0800 Subject: [PATCH] Sync to upstream/release/663 (#1699) Hey folks, another week means another Luau release! This one features a number of bug fixes in the New Type Solver including improvements to user-defined type functions and a bunch of work to untangle some of the outstanding issues we've been seeing with constraint solving not completing in real world use. We're also continuing to make progress on crashes and other problems that affect the stability of fragment autocomplete, as we work towards delivering consistent, low-latency autocomplete for any editor environment. ## New Type Solver - Fix a bug in user-defined type functions where `print` would incorrectly insert `\1` a number of times. - Fix a bug where attempting to refine an optional generic with a type test will cause a false positive type error (fixes #1666) - Fix a bug where the `refine` type family would not skip over `*no-refine*` discriminants (partial resolution for #1424) - Fix a constraint solving bug where recursive function calls would consistently produce cyclic constraints leading to incomplete or inaccurate type inference. - Implement `readparent` and `writeparent` for class types in user-defined type functions, replacing the incorrectly included `parent` method. - Add initial groundwork (under a debug flag) for eager free type generalization, moving us towards further improvements to constraint solving incomplete errors. ## Fragment Autocomplete - Ease up some assertions to improve stability of mixed-mode use of the two type solvers (i.e. using Fragment Autocomplete on a type graph originally produced by the old type solver) - Resolve a bug with type compatibility checks causing internal compiler errors in autocomplete. ## Lexer and Parser - Improve the accuracy of the roundtrippable AST parsing mode by correctly placing closing parentheses on type groupings. - Add a getter for `offset` in the Lexer by @aduermael in #1688 - Add a second entry point to the parser to parse an expression, `parseExpr` ## Internal Contributors Co-authored-by: Andy Friesen Co-authored-by: Ariel Weiss Co-authored-by: Aviral Goel Co-authored-by: Hunter Goldstein Co-authored-by: James McNellis Co-authored-by: Talha Pathan Co-authored-by: Vighnesh Vijay 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: Alexander Youngblood Co-authored-by: Menarul Alam Co-authored-by: Aviral Goel Co-authored-by: Vighnesh Co-authored-by: Vyacheslav Egorov --- Analysis/include/Luau/ConstraintGenerator.h | 3 + Analysis/include/Luau/ConstraintSolver.h | 5 + Analysis/include/Luau/FragmentAutocomplete.h | 60 ++++ Analysis/include/Luau/TableLiteralInference.h | 2 + Analysis/include/Luau/TxnLog.h | 3 +- Analysis/include/Luau/TypeFunctionRuntime.h | 6 +- Analysis/include/Luau/Unifier.h | 10 - Analysis/src/Autocomplete.cpp | 4 + Analysis/src/AutocompleteCore.cpp | 104 +++++-- Analysis/src/Constraint.cpp | 10 +- Analysis/src/ConstraintGenerator.cpp | 62 ++-- Analysis/src/ConstraintSolver.cpp | 247 ++++++++++++---- Analysis/src/EmbeddedBuiltinDefinitions.cpp | 72 +---- Analysis/src/FragmentAutocomplete.cpp | 40 ++- Analysis/src/Frontend.cpp | 1 + Analysis/src/NonStrictTypeChecker.cpp | 115 -------- Analysis/src/Transpiler.cpp | 11 +- Analysis/src/TypeChecker2.cpp | 13 +- Analysis/src/TypeFunction.cpp | 32 +- Analysis/src/TypeFunctionRuntime.cpp | 56 +++- Analysis/src/TypeFunctionRuntimeBuilder.cpp | 20 +- Analysis/src/TypePath.cpp | 8 +- Analysis/src/TypeUtils.cpp | 7 +- Analysis/src/Unifier.cpp | 275 +++--------------- Ast/include/Luau/ParseResult.h | 13 + Ast/include/Luau/Parser.h | 8 + Ast/src/Parser.cpp | 58 ++-- CLI/src/Repl.cpp | 2 - CLI/src/ReplEntry.cpp | 3 + CodeGen/src/CodeGen.cpp | 1 - CodeGen/src/CodeGenLower.h | 4 +- CodeGen/src/IrLoweringA64.cpp | 2 - CodeGen/src/IrLoweringX64.cpp | 2 - CodeGen/src/IrTranslateBuiltins.cpp | 51 +--- CodeGen/src/IrUtils.cpp | 2 - CodeGen/src/OptimizeConstProp.cpp | 68 ++--- Compiler/src/BuiltinFolding.cpp | 8 +- Compiler/src/Builtins.cpp | 5 +- VM/src/lbuflib.cpp | 32 +- VM/src/lbuiltins.cpp | 67 ++--- VM/src/lmathlib.cpp | 9 +- VM/src/lveclib.cpp | 4 +- tests/AnyTypeSummary.test.cpp | 24 +- tests/AstJsonEncoder.test.cpp | 6 +- tests/Compiler.test.cpp | 3 - tests/Conformance.test.cpp | 16 - tests/ConstraintGeneratorFixture.cpp | 1 + tests/Fixture.cpp | 3 - tests/FragmentAutocomplete.test.cpp | 27 +- tests/NonStrictTypeChecker.test.cpp | 5 - tests/Parser.test.cpp | 12 +- tests/Transpiler.test.cpp | 24 +- tests/TxnLog.test.cpp | 37 +-- tests/TypeFunction.user.test.cpp | 22 ++ tests/TypeInfer.definitions.test.cpp | 2 - tests/TypeInfer.functions.test.cpp | 47 +++ tests/TypeInfer.modules.test.cpp | 23 +- tests/TypeInfer.provisional.test.cpp | 10 +- tests/TypeInfer.refinements.test.cpp | 67 +++++ tests/TypeInfer.tables.test.cpp | 19 +- tests/TypeInfer.test.cpp | 11 +- tests/TypeInfer.tryUnify.test.cpp | 132 --------- 62 files changed, 927 insertions(+), 1069 deletions(-) diff --git a/Analysis/include/Luau/ConstraintGenerator.h b/Analysis/include/Luau/ConstraintGenerator.h index d1a9cfcc..8a072e82 100644 --- a/Analysis/include/Luau/ConstraintGenerator.h +++ b/Analysis/include/Luau/ConstraintGenerator.h @@ -96,6 +96,9 @@ struct ConstraintGenerator // will enqueue them during solving. std::vector unqueuedConstraints; + // Map a function's signature scope back to its signature type. + DenseHashMap scopeToFunction{nullptr}; + // The private scope of type aliases for which the type parameters belong to. DenseHashMap astTypeAliasDefiningScopes{nullptr}; diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h index 0cff0ae4..f561072d 100644 --- a/Analysis/include/Luau/ConstraintSolver.h +++ b/Analysis/include/Luau/ConstraintSolver.h @@ -88,6 +88,7 @@ struct ConstraintSolver NotNull typeFunctionRuntime; // The entire set of constraints that the solver is trying to resolve. std::vector> constraints; + NotNull> scopeToFunction; NotNull rootScope; ModuleName currentModuleName; @@ -119,6 +120,7 @@ struct ConstraintSolver DenseHashMap unresolvedConstraints{{}}; std::unordered_map, DenseHashSet> maybeMutatedFreeTypes; + std::unordered_map> mutatedFreeTypeToConstraint; // Irreducible/uninhabited type functions or type pack functions. DenseHashSet uninhabitedTypeFunctions{{}}; @@ -144,6 +146,7 @@ struct ConstraintSolver NotNull typeFunctionRuntime, NotNull rootScope, std::vector> constraints, + NotNull> scopeToFunction, ModuleName moduleName, NotNull moduleResolver, std::vector requireCycles, @@ -171,6 +174,8 @@ struct ConstraintSolver bool isDone() const; private: + void generalizeOneType(TypeId ty); + /** * Bind a type variable to another type. * diff --git a/Analysis/include/Luau/FragmentAutocomplete.h b/Analysis/include/Luau/FragmentAutocomplete.h index bf67b8b6..4c32f90b 100644 --- a/Analysis/include/Luau/FragmentAutocomplete.h +++ b/Analysis/include/Luau/FragmentAutocomplete.h @@ -82,5 +82,65 @@ FragmentAutocompleteResult fragmentAutocomplete( std::optional fragmentEndPosition = std::nullopt ); +enum class FragmentAutocompleteStatus +{ + Success, + FragmentTypeCheckFail, + InternalIce +}; + +struct FragmentAutocompleteStatusResult +{ + FragmentAutocompleteStatus status; + std::optional result; +}; + +struct FragmentContext +{ + std::string_view newSrc; + const ParseResult& newAstRoot; + std::optional opts; + std::optional DEPRECATED_fragmentEndPosition; +}; + +/** + * @brief Attempts to compute autocomplete suggestions from the fragment context. + * + * This function computes autocomplete suggestions using outdated frontend typechecking data + * by patching the fragment context of the new script source content. + * + * @param frontend The Luau Frontend data structure, which may contain outdated typechecking data. + * + * @param moduleName The name of the target module, specifying which script the caller wants to request autocomplete for. + * + * @param cursorPosition The position in the script where the caller wants to trigger autocomplete. + * + * @param context The fragment context that this API will use to patch the outdated typechecking data. + * + * @param stringCompletionCB A callback function that provides autocomplete suggestions for string contexts. + * + * @return + * The status indicating whether `fragmentAutocomplete` ran successfully or failed, along with the reason for failure. + * Also includes autocomplete suggestions if the status is successful. + * + * @usage + * FragmentAutocompleteStatusResult acStatusResult; + * if (shouldFragmentAC) + * acStatusResult = Luau::tryFragmentAutocomplete(...); + * + * if (acStatusResult.status != Successful) + * { + * frontend.check(moduleName, options); + * acStatusResult.acResult = Luau::autocomplete(...); + * } + * return convertResultWithContext(acStatusResult.acResult); + */ +FragmentAutocompleteStatusResult tryFragmentAutocomplete( + Frontend& frontend, + const ModuleName& moduleName, + Position cursorPosition, + FragmentContext context, + StringCompletionCallback stringCompletionCB +); } // namespace Luau diff --git a/Analysis/include/Luau/TableLiteralInference.h b/Analysis/include/Luau/TableLiteralInference.h index dd9ecf97..6be1e872 100644 --- a/Analysis/include/Luau/TableLiteralInference.h +++ b/Analysis/include/Luau/TableLiteralInference.h @@ -6,6 +6,8 @@ #include "Luau/NotNull.h" #include "Luau/TypeFwd.h" +#include + namespace Luau { diff --git a/Analysis/include/Luau/TxnLog.h b/Analysis/include/Luau/TxnLog.h index 951f89ee..de8665e5 100644 --- a/Analysis/include/Luau/TxnLog.h +++ b/Analysis/include/Luau/TxnLog.h @@ -65,11 +65,10 @@ T* getMutable(PendingTypePack* pending) // Log of what TypeIds we are rebinding, to be committed later. struct TxnLog { - explicit TxnLog(bool useScopes = false) + explicit TxnLog() : typeVarChanges(nullptr) , typePackChanges(nullptr) , ownedSeen() - , useScopes(useScopes) , sharedSeen(&ownedSeen) { } diff --git a/Analysis/include/Luau/TypeFunctionRuntime.h b/Analysis/include/Luau/TypeFunctionRuntime.h index 5759268d..e6cc4d26 100644 --- a/Analysis/include/Luau/TypeFunctionRuntime.h +++ b/Analysis/include/Luau/TypeFunctionRuntime.h @@ -216,7 +216,11 @@ struct TypeFunctionClassType std::optional metatable; // metaclass? - std::optional parent; + // this was mistaken, and we should actually be keeping separate read/write types here. + std::optional parent_DEPRECATED; + + std::optional readParent; + std::optional writeParent; TypeId classTy; diff --git a/Analysis/include/Luau/Unifier.h b/Analysis/include/Luau/Unifier.h index 3de841ed..8d0f2806 100644 --- a/Analysis/include/Luau/Unifier.h +++ b/Analysis/include/Luau/Unifier.h @@ -93,10 +93,6 @@ struct Unifier Unifier(NotNull normalizer, NotNull scope, const Location& location, Variance variance, TxnLog* parentLog = nullptr); - // Configure the Unifier to test for scope subsumption via embedded Scope - // pointers rather than TypeLevels. - void enableNewSolver(); - // Test whether the two type vars unify. Never commits the result. ErrorVec canUnify(TypeId subTy, TypeId superTy); ErrorVec canUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false); @@ -169,7 +165,6 @@ private: std::optional findTablePropertyRespectingMeta(TypeId lhsType, Name name); - TxnLog combineLogsIntoIntersection(std::vector logs); TxnLog combineLogsIntoUnion(std::vector logs); public: @@ -195,11 +190,6 @@ private: // Available after regular type pack unification errors std::optional firstPackErrorPos; - - // If true, we do a bunch of small things differently to work better with - // the new type inference engine. Most notably, we use the Scope hierarchy - // directly rather than using TypeLevels. - bool useNewSolver = false; }; void promoteTypeLevels(TxnLog& log, const TypeArena* arena, TypeLevel minLevel, Scope* outerScope, bool useScope, TypePackId tp); diff --git a/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index eb7e2298..bdfa04bf 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/Analysis/src/Autocomplete.cpp @@ -2,6 +2,7 @@ #include "Luau/Autocomplete.h" #include "Luau/AstQuery.h" +#include "Luau/TimeTrace.h" #include "Luau/TypeArena.h" #include "Luau/Module.h" #include "Luau/Frontend.h" @@ -15,6 +16,9 @@ namespace Luau AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback) { + LUAU_TIMETRACE_SCOPE("Luau::autocomplete", "Autocomplete"); + LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str()); + const SourceModule* sourceModule = frontend.getSourceModule(moduleName); if (!sourceModule) return {}; diff --git a/Analysis/src/AutocompleteCore.cpp b/Analysis/src/AutocompleteCore.cpp index c007ad50..faabdf47 100644 --- a/Analysis/src/AutocompleteCore.cpp +++ b/Analysis/src/AutocompleteCore.cpp @@ -10,6 +10,7 @@ #include "Luau/Common.h" #include "Luau/FileResolver.h" #include "Luau/Frontend.h" +#include "Luau/TimeTrace.h" #include "Luau/ToString.h" #include "Luau/Subtyping.h" #include "Luau/TypeInfer.h" @@ -24,7 +25,8 @@ LUAU_FASTINT(LuauTypeInferIterationLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAGVARIABLE(LuauAutocompleteRefactorsForIncrementalAutocomplete) -LUAU_FASTFLAGVARIABLE(LuauAutocompleteUseLimits) + +LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility) static const std::unordered_set kStatementStartingKeywords = {"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"}; @@ -146,44 +148,91 @@ static std::optional findExpectedTypeAt(const Module& module, AstNode* n return *it; } -static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull scope, TypeArena* typeArena, NotNull builtinTypes) +static bool checkTypeMatch( + const Module& module, + TypeId subTy, + TypeId superTy, + NotNull scope, + TypeArena* typeArena, + NotNull builtinTypes +) { InternalErrorReporter iceReporter; UnifierSharedState unifierState(&iceReporter); SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes); Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}}; - - if (FFlag::LuauSolverV2) + if (FFlag::LuauAutocompleteUsesModuleForTypeCompatibility) { - TypeCheckLimits limits; - TypeFunctionRuntime typeFunctionRuntime{ - NotNull{&iceReporter}, NotNull{&limits} - }; // TODO: maybe subtyping checks should not invoke user-defined type function runtime + if (module.checkedInNewSolver) + { + TypeCheckLimits limits; + TypeFunctionRuntime typeFunctionRuntime{ + NotNull{&iceReporter}, NotNull{&limits} + }; // TODO: maybe subtyping checks should not invoke user-defined type function runtime - unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; - unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit; + unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; + unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit; - Subtyping subtyping{ - builtinTypes, NotNull{typeArena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter} - }; + Subtyping subtyping{ + builtinTypes, + NotNull{typeArena}, + NotNull{simplifier.get()}, + NotNull{&normalizer}, + NotNull{&typeFunctionRuntime}, + NotNull{&iceReporter} + }; - return subtyping.isSubtype(subTy, superTy, scope).isSubtype; + return subtyping.isSubtype(subTy, superTy, scope).isSubtype; + } + else + { + Unifier unifier(NotNull{&normalizer}, scope, Location(), Variance::Covariant); + + // Cost of normalization can be too high for autocomplete response time requirements + unifier.normalize = false; + unifier.checkInhabited = false; + + unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; + unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit; + + return unifier.canUnify(subTy, superTy).empty(); + } } else { - Unifier unifier(NotNull{&normalizer}, scope, Location(), Variance::Covariant); - - // Cost of normalization can be too high for autocomplete response time requirements - unifier.normalize = false; - unifier.checkInhabited = false; - - if (FFlag::LuauAutocompleteUseLimits) + if (FFlag::LuauSolverV2) { + TypeCheckLimits limits; + TypeFunctionRuntime typeFunctionRuntime{ + NotNull{&iceReporter}, NotNull{&limits} + }; // TODO: maybe subtyping checks should not invoke user-defined type function runtime + unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit; - } - return unifier.canUnify(subTy, superTy).empty(); + Subtyping subtyping{ + builtinTypes, + NotNull{typeArena}, + NotNull{simplifier.get()}, + NotNull{&normalizer}, + NotNull{&typeFunctionRuntime}, + NotNull{&iceReporter} + }; + + return subtyping.isSubtype(subTy, superTy, scope).isSubtype; + } + else + { + Unifier unifier(NotNull{&normalizer}, scope, Location(), Variance::Covariant); + + // Cost of normalization can be too high for autocomplete response time requirements + unifier.normalize = false; + unifier.checkInhabited = false; + unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; + unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit; + + return unifier.canUnify(subTy, superTy).empty(); + } } } @@ -209,10 +258,10 @@ static TypeCorrectKind checkTypeCorrectKind( TypeId expectedType = follow(*typeAtPosition); - auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType](const FunctionType* ftv) + auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType, &module](const FunctionType* ftv) { if (std::optional firstRetTy = first(ftv->retTypes)) - return checkTypeMatch(*firstRetTy, expectedType, moduleScope, typeArena, builtinTypes); + return checkTypeMatch(module, *firstRetTy, expectedType, moduleScope, typeArena, builtinTypes); return false; }; @@ -235,7 +284,7 @@ static TypeCorrectKind checkTypeCorrectKind( } } - return checkTypeMatch(ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None; + return checkTypeMatch(module, ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None; } enum class PropIndexType @@ -286,7 +335,7 @@ static void autocompleteProps( // When called with '.', but declared with 'self', it is considered invalid if first argument is compatible if (std::optional firstArgTy = first(ftv->argTypes)) { - if (checkTypeMatch(rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes)) + if (checkTypeMatch(module, rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes)) return calledWithSelf; } @@ -1714,6 +1763,7 @@ AutocompleteResult autocomplete_( StringCompletionCallback callback ) { + LUAU_TIMETRACE_SCOPE("Luau::autocomplete_", "AutocompleteCore"); AstNode* node = ancestry.back(); AstExprConstantNil dummy{Location{}}; diff --git a/Analysis/src/Constraint.cpp b/Analysis/src/Constraint.cpp index 0fe97abc..e62a3f18 100644 --- a/Analysis/src/Constraint.cpp +++ b/Analysis/src/Constraint.cpp @@ -3,6 +3,8 @@ #include "Luau/Constraint.h" #include "Luau/VisitType.h" +LUAU_FASTFLAG(DebugLuauGreedyGeneralization) + namespace Luau { @@ -111,6 +113,11 @@ DenseHashSet Constraint::getMaybeMutatedFreeTypes() const { rci.traverse(fchc->argsPack); } + else if (auto fcc = get(*this); fcc && FFlag::DebugLuauGreedyGeneralization) + { + rci.traverse(fcc->fn); + rci.traverse(fcc->argsPack); + } else if (auto ptc = get(*this)) { rci.traverse(ptc->freeType); @@ -118,7 +125,8 @@ DenseHashSet Constraint::getMaybeMutatedFreeTypes() const else if (auto hpc = get(*this)) { rci.traverse(hpc->resultType); - // `HasPropConstraints` should not mutate `subjectType`. + if (FFlag::DebugLuauGreedyGeneralization) + rci.traverse(hpc->subjectType); } else if (auto hic = get(*this)) { diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 369b1170..962d11fa 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -32,11 +32,11 @@ LUAU_FASTINT(LuauCheckRecursionLimit) LUAU_FASTFLAG(DebugLuauLogSolverToJson) LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType) +LUAU_FASTFLAG(DebugLuauGreedyGeneralization) -LUAU_FASTFLAGVARIABLE(LuauNewSolverPrePopulateClasses) -LUAU_FASTFLAGVARIABLE(LuauNewSolverPopulateTableLocations) LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTypesOnScope) LUAU_FASTFLAGVARIABLE(LuauDeferBidirectionalInferenceForTableAssignment) +LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions) LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds) LUAU_FASTFLAGVARIABLE(LuauInferLocalTypesInMultipleAssignments) @@ -785,9 +785,6 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc } else if (auto classDeclaration = stat->as()) { - if (!FFlag::LuauNewSolverPrePopulateClasses) - continue; - if (scope->exportedTypeBindings.count(classDeclaration->name.value)) { auto it = classDefinitionLocations.find(classDeclaration->name.value); @@ -1384,6 +1381,28 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location); bool sigFullyDefined = !hasFreeType(sig.signature); + DefId def = dfg->getDef(function->name); + + if (FFlag::LuauUngeneralizedTypesForRecursiveFunctions) + { + if (AstExprLocal* localName = function->name->as()) + { + sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location}; + sig.bodyScope->lvalueTypes[def] = sig.signature; + sig.bodyScope->rvalueRefinements[def] = sig.signature; + } + else if (AstExprGlobal* globalName = function->name->as()) + { + sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location}; + sig.bodyScope->lvalueTypes[def] = sig.signature; + sig.bodyScope->rvalueRefinements[def] = sig.signature; + } + else if (AstExprIndexName* indexName = function->name->as()) + { + sig.bodyScope->rvalueRefinements[def] = sig.signature; + } + } + checkFunctionBody(sig.bodyScope, function->func); Checkpoint end = checkpoint(this); @@ -1417,7 +1436,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f ); } - DefId def = dfg->getDef(function->name); std::optional existingFunctionTy = follow(lookup(scope, function->name->location, def)); if (AstExprLocal* localName = function->name->as()) @@ -1691,7 +1709,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas { // If a class with the same name was already defined, we skip over auto bindingIt = scope->exportedTypeBindings.find(declaredClass->name.value); - if (FFlag::LuauNewSolverPrePopulateClasses && bindingIt == scope->exportedTypeBindings.end()) + if (bindingIt == scope->exportedTypeBindings.end()) return ControlFlow::None; std::optional superTy = std::make_optional(builtinTypes->classType); @@ -1708,10 +1726,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas // We don't have generic classes, so this assertion _should_ never be hit. LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0); - if (FFlag::LuauNewSolverPrePopulateClasses) - superTy = follow(lookupType->type); - else - superTy = lookupType->type; + superTy = follow(lookupType->type); if (!get(follow(*superTy))) { @@ -1734,14 +1749,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas ctv->metatable = metaTy; - - if (FFlag::LuauNewSolverPrePopulateClasses) - { - TypeId classBindTy = bindingIt->second.type; - emplaceType(asMutable(classBindTy), classTy); - } - else - scope->exportedTypeBindings[className] = TypeFun{{}, classTy}; + TypeId classBindTy = bindingIt->second.type; + emplaceType(asMutable(classBindTy), classTy); if (declaredClass->indexer) { @@ -2930,10 +2939,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, ttv->state = TableState::Unsealed; ttv->definitionModuleName = module->name; - if (FFlag::LuauNewSolverPopulateTableLocations) - { - ttv->definitionLocation = expr->location; - } + ttv->definitionLocation = expr->location; ttv->scope = scope.get(); interiorTypes.back().push_back(ty); @@ -3230,6 +3236,9 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu if (expectedType && get(*expectedType)) bindFreeType(*expectedType, actualFunctionType); + if (FFlag::DebugLuauGreedyGeneralization) + scopeToFunction[signatureScope.get()] = actualFunctionType; + return { /* signature */ actualFunctionType, /* signatureScope */ signatureScope, @@ -3392,11 +3401,8 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty, TypeId tableTy = arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed}); TableType* ttv = getMutable(tableTy); - if (FFlag::LuauNewSolverPopulateTableLocations) - { - ttv->definitionModuleName = module->name; - ttv->definitionLocation = tab->location; - } + ttv->definitionModuleName = module->name; + ttv->definitionLocation = tab->location; return tableTy; } diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index b3dc904d..73538532 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -27,17 +27,16 @@ #include #include +LUAU_FASTFLAGVARIABLE(DebugLuauAssertOnForcedConstraint) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies) LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings) LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations) -LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer) LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope) -LUAU_FASTFLAGVARIABLE(LuauAlwaysFillInFunctionCallDiscriminantTypes) LUAU_FASTFLAGVARIABLE(LuauTrackInteriorFreeTablesOnScope) LUAU_FASTFLAGVARIABLE(LuauPrecalculateMutatedFreeTypes2) +LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization) namespace Luau { @@ -328,6 +327,7 @@ ConstraintSolver::ConstraintSolver( NotNull typeFunctionRuntime, NotNull rootScope, std::vector> constraints, + NotNull> scopeToFunction, ModuleName moduleName, NotNull moduleResolver, std::vector requireCycles, @@ -341,6 +341,7 @@ ConstraintSolver::ConstraintSolver( , simplifier(simplifier) , typeFunctionRuntime(typeFunctionRuntime) , constraints(std::move(constraints)) + , scopeToFunction(scopeToFunction) , rootScope(rootScope) , currentModuleName(std::move(moduleName)) , dfg(dfg) @@ -362,6 +363,12 @@ ConstraintSolver::ConstraintSolver( { auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0); refCount += 1; + + if (FFlag::DebugLuauGreedyGeneralization) + { + auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, DenseHashSet{nullptr}); + it->second.insert(c.get()); + } } maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint); } @@ -453,6 +460,9 @@ void ConstraintSolver::run() snapshot = logger->prepareStepSnapshot(rootScope, c, force, unsolvedConstraints); } + if (FFlag::DebugLuauAssertOnForcedConstraint) + LUAU_ASSERT(!force); + bool success = tryDispatch(c, force); progress |= success; @@ -467,12 +477,21 @@ void ConstraintSolver::run() const auto maybeMutated = maybeMutatedFreeTypes.find(c); if (maybeMutated != maybeMutatedFreeTypes.end()) { + DenseHashSet seen{nullptr}; for (auto ty : maybeMutated->second) { // There is a high chance that this type has been rebound // across blocked types, rebound free types, pending // expansion types, etc, so we need to follow it. ty = follow(ty); + + if (FFlag::DebugLuauGreedyGeneralization) + { + if (seen.contains(ty)) + continue; + seen.insert(ty); + } + size_t& refCount = unresolvedConstraints[ty]; if (refCount > 0) refCount -= 1; @@ -484,6 +503,9 @@ void ConstraintSolver::run() // refcount to be 1 or 0. if (refCount <= 1) unblock(ty, Location{}); + + if (FFlag::DebugLuauGreedyGeneralization && refCount == 0) + generalizeOneType(ty); } } } @@ -600,16 +622,154 @@ bool ConstraintSolver::isDone() const return unsolvedConstraints.empty(); } -namespace +struct TypeSearcher : TypeVisitor { + enum struct Polarity: uint8_t + { + None = 0b00, + Positive = 0b01, + Negative = 0b10, + Mixed = 0b11, + }; -struct TypeAndLocation -{ - TypeId typeId; - Location location; + TypeId needle; + Polarity current = Polarity::Positive; + + Polarity result = Polarity::None; + + explicit TypeSearcher(TypeId needle) + : TypeSearcher(needle, Polarity::Positive) + {} + + explicit TypeSearcher(TypeId needle, Polarity initialPolarity) + : needle(needle) + , current(initialPolarity) + {} + + bool visit(TypeId ty) override + { + if (ty == needle) + result = Polarity(int(result) | int(current)); + + return true; + } + + void flip() + { + switch (current) + { + case Polarity::Positive: + current = Polarity::Negative; + break; + case Polarity::Negative: + current = Polarity::Positive; + break; + default: + break; + } + } + + bool visit(TypeId ty, const FunctionType& ft) override + { + flip(); + traverse(ft.argTypes); + + flip(); + traverse(ft.retTypes); + + return false; + } + + // bool visit(TypeId ty, const TableType& tt) override + // { + + // } + + bool visit(TypeId ty, const ClassType&) override + { + return false; + } }; -} // namespace +void ConstraintSolver::generalizeOneType(TypeId ty) +{ + ty = follow(ty); + const FreeType* freeTy = get(ty); + + std::string saveme = toString(ty, opts); + + // Some constraints (like prim) will also replace a free type with something + // concrete. If so, our work is already done. + if (!freeTy) + return; + + NotNull tyScope{freeTy->scope}; + + // TODO: If freeTy occurs within the enclosing function's type, we need to + // check to see whether this type should instead be generic. + + TypeId newBound = follow(freeTy->upperBound); + + TypeId* functionTyPtr = nullptr; + while (true) + { + functionTyPtr = scopeToFunction->find(tyScope); + if (functionTyPtr || !tyScope->parent) + break; + else if (tyScope->parent) + tyScope = NotNull{tyScope->parent.get()}; + else + break; + } + + if (ty == newBound) + ty = builtinTypes->unknownType; + + if (!functionTyPtr) + { + asMutable(ty)->reassign(Type{BoundType{follow(freeTy->upperBound)}}); + } + else + { + const TypeId functionTy = follow(*functionTyPtr); + FunctionType* const function = getMutable(functionTy); + LUAU_ASSERT(function); + + TypeSearcher ts{ty}; + ts.traverse(functionTy); + + const TypeId upperBound = follow(freeTy->upperBound); + const TypeId lowerBound = follow(freeTy->lowerBound); + + switch (ts.result) + { + case TypeSearcher::Polarity::None: + asMutable(ty)->reassign(Type{BoundType{upperBound}}); + break; + + case TypeSearcher::Polarity::Negative: + case TypeSearcher::Polarity::Mixed: + if (get(upperBound)) + { + asMutable(ty)->reassign(Type{GenericType{tyScope}}); + function->generics.emplace_back(ty); + } + else + asMutable(ty)->reassign(Type{BoundType{upperBound}}); + break; + + case TypeSearcher::Polarity::Positive: + if (get(lowerBound)) + { + asMutable(ty)->reassign(Type{GenericType{tyScope}}); + function->generics.emplace_back(ty); + } + else + asMutable(ty)->reassign(Type{BoundType{lowerBound}}); + break; + } + } +} void ConstraintSolver::bind(NotNull constraint, TypeId ty, TypeId boundTo) { @@ -1170,12 +1330,9 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul target = follow(instantiated); } - if (FFlag::LuauNewSolverPopulateTableLocations) - { - // This is a new type - redefine the location. - ttv->definitionLocation = constraint->location; - ttv->definitionModuleName = currentModuleName; - } + // This is a new type - redefine the location. + ttv->definitionLocation = constraint->location; + ttv->definitionModuleName = currentModuleName; ttv->instantiatedTypeParams = typeArguments; ttv->instantiatedTypePackParams = packArguments; @@ -1222,8 +1379,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull(asMutable(c.result), builtinTypes->anyTypePack); unblock(c.result, constraint->location); - if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes) - fillInDiscriminantTypes(constraint, c.discriminantTypes); + fillInDiscriminantTypes(constraint, c.discriminantTypes); return true; } @@ -1231,16 +1387,14 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull(fn)) { bind(constraint, c.result, builtinTypes->errorRecoveryTypePack()); - if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes) - fillInDiscriminantTypes(constraint, c.discriminantTypes); + fillInDiscriminantTypes(constraint, c.discriminantTypes); return true; } if (get(fn)) { bind(constraint, c.result, builtinTypes->neverTypePack); - if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes) - fillInDiscriminantTypes(constraint, c.discriminantTypes); + fillInDiscriminantTypes(constraint, c.discriminantTypes); return true; } @@ -1321,30 +1475,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull(constraint, c.result, constraint->scope); } - if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes) - { - fillInDiscriminantTypes(constraint, c.discriminantTypes); - } - else - { - // NOTE: This is the body of the `fillInDiscriminantTypes` helper. - for (std::optional ty : c.discriminantTypes) - { - if (!ty) - continue; - - // If the discriminant type has been transmuted, we need to unblock them. - if (!isBlocked(*ty)) - { - unblock(*ty, constraint->location); - continue; - } - - // We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored. - emplaceType(asMutable(follow(*ty)), builtinTypes->noRefineType); - } - } - + fillInDiscriminantTypes(constraint, c.discriminantTypes); OverloadResolver resolver{ builtinTypes, @@ -1912,7 +2043,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNulladdType(UnionType{{propTy, builtinTypes->nilType}}) : propTy + isIndex ? arena->addType(UnionType{{propTy, builtinTypes->nilType}}) : propTy ); unify(constraint, rhsType, propTy); return true; @@ -2010,8 +2141,7 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNulladdType(UnionType{{lhsTable->indexer->indexResultType, builtinTypes->nilType}}) - : lhsTable->indexer->indexResultType + arena->addType(UnionType{{lhsTable->indexer->indexResultType, builtinTypes->nilType}}) ); return true; } @@ -2064,8 +2194,7 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNulladdType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}}) - : lhsClass->indexer->indexResultType + arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}}) ); return true; } @@ -3113,6 +3242,24 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target) auto [targetRefs, _] = unresolvedConstraints.try_insert(target, 0); targetRefs += count; + + // Any constraint that might have mutated source may now mutate target + + if (FFlag::DebugLuauGreedyGeneralization) + { + auto it = mutatedFreeTypeToConstraint.find(source); + if (it != mutatedFreeTypeToConstraint.end()) + { + auto [it2, fresh] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet{nullptr}); + for (const Constraint* constraint : it->second) + { + it2->second.insert(constraint); + + auto [it3, fresh2] = maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, DenseHashSet{nullptr}); + it3->second.insert(target); + } + } + } } std::optional ConstraintSolver::generalizeFreeType(NotNull scope, TypeId type, bool avoidSealingTables) diff --git a/Analysis/src/EmbeddedBuiltinDefinitions.cpp b/Analysis/src/EmbeddedBuiltinDefinitions.cpp index bbcf4841..ff2f02c0 100644 --- a/Analysis/src/EmbeddedBuiltinDefinitions.cpp +++ b/Analysis/src/EmbeddedBuiltinDefinitions.cpp @@ -1,8 +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(LuauBufferBitMethods2) -LUAU_FASTFLAG(LuauVector2Constructor) LUAU_FASTFLAGVARIABLE(LuauDebugInfoDefn) namespace Luau @@ -239,37 +237,6 @@ declare utf8: { )BUILTIN_SRC"; -static const std::string kBuiltinDefinitionBufferSrc_DEPRECATED = R"BUILTIN_SRC( ---- Buffer API -declare buffer: { - create: @checked (size: number) -> buffer, - fromstring: @checked (str: string) -> buffer, - tostring: @checked (b: buffer) -> string, - len: @checked (b: buffer) -> number, - copy: @checked (target: buffer, targetOffset: number, source: buffer, sourceOffset: number?, count: number?) -> (), - fill: @checked (b: buffer, offset: number, value: number, count: number?) -> (), - readi8: @checked (b: buffer, offset: number) -> number, - readu8: @checked (b: buffer, offset: number) -> number, - readi16: @checked (b: buffer, offset: number) -> number, - readu16: @checked (b: buffer, offset: number) -> number, - readi32: @checked (b: buffer, offset: number) -> number, - readu32: @checked (b: buffer, offset: number) -> number, - readf32: @checked (b: buffer, offset: number) -> number, - readf64: @checked (b: buffer, offset: number) -> number, - writei8: @checked (b: buffer, offset: number, value: number) -> (), - writeu8: @checked (b: buffer, offset: number, value: number) -> (), - writei16: @checked (b: buffer, offset: number, value: number) -> (), - writeu16: @checked (b: buffer, offset: number, value: number) -> (), - writei32: @checked (b: buffer, offset: number, value: number) -> (), - writeu32: @checked (b: buffer, offset: number, value: number) -> (), - writef32: @checked (b: buffer, offset: number, value: number) -> (), - writef64: @checked (b: buffer, offset: number, value: number) -> (), - readstring: @checked (b: buffer, offset: number, count: number) -> string, - writestring: @checked (b: buffer, offset: number, value: string, count: number?) -> (), -} - -)BUILTIN_SRC"; - static const std::string kBuiltinDefinitionBufferSrc = R"BUILTIN_SRC( --- Buffer API declare buffer: { @@ -303,36 +270,6 @@ declare buffer: { )BUILTIN_SRC"; -static const std::string kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED = R"BUILTIN_SRC( - --- While vector would have been better represented as a built-in primitive type, type solver class handling covers most of the properties -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"; - static const std::string kBuiltinDefinitionVectorSrc = 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 @@ -374,13 +311,8 @@ std::string getBuiltinDefinitionSource() result += kBuiltinDefinitionTableSrc; result += FFlag::LuauDebugInfoDefn ? kBuiltinDefinitionDebugSrc : kBuiltinDefinitionDebugSrc_DEPRECATED; result += kBuiltinDefinitionUtf8Src; - - result += FFlag::LuauBufferBitMethods2 ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED; - - if (FFlag::LuauVector2Constructor) - result += kBuiltinDefinitionVectorSrc; - else - result += kBuiltinDefinitionVectorSrc_NoVector2Ctor_DEPRECATED; + result += kBuiltinDefinitionBufferSrc; + result += kBuiltinDefinitionVectorSrc; return result; } diff --git a/Analysis/src/FragmentAutocomplete.cpp b/Analysis/src/FragmentAutocomplete.cpp index 90ecc15c..47c0c1a1 100644 --- a/Analysis/src/FragmentAutocomplete.cpp +++ b/Analysis/src/FragmentAutocomplete.cpp @@ -26,7 +26,6 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit); LUAU_FASTINT(LuauTypeInferIterationLimit); LUAU_FASTINT(LuauTarjanChildLimit) -LUAU_FASTFLAG(LuauAllowFragmentParsing); LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete) LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteBugfixes) @@ -98,6 +97,7 @@ void cloneAndSquashScopes( Scope* destScope ) { + LUAU_TIMETRACE_SCOPE("Luau::cloneAndSquashScopes", "FragmentAutocomplete"); std::vector scopes; for (const Scope* current = staleScope; current; current = current->parent.get()) { @@ -364,6 +364,7 @@ std::optional parseFragment( ModulePtr cloneModule(CloneState& cloneState, const ModulePtr& source, std::unique_ptr alloc) { + LUAU_TIMETRACE_SCOPE("Luau::cloneModule", "FragmentAutocomplete"); freeze(source->internalTypes); freeze(source->interfaceTypes); ModulePtr incremental = std::make_shared(); @@ -445,6 +446,8 @@ FragmentTypeCheckResult typecheckFragment_( const FrontendOptions& opts ) { + LUAU_TIMETRACE_SCOPE("Luau::typecheckFragment_", "FragmentAutocomplete"); + freeze(stale->internalTypes); freeze(stale->interfaceTypes); CloneState cloneState{frontend.builtinTypes}; @@ -533,6 +536,7 @@ FragmentTypeCheckResult typecheckFragment_( NotNull{&typeFunctionRuntime}, NotNull(cg.rootScope), borrowConstraints(cg.constraints), + NotNull{&cg.scopeToFunction}, incrementalModule->name, NotNull{&resolver}, {}, @@ -573,6 +577,8 @@ std::pair typecheckFragment( std::optional fragmentEndPosition ) { + LUAU_TIMETRACE_SCOPE("Luau::typecheckFragment", "FragmentAutocomplete"); + LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str()); if (FFlag::LuauBetterReverseDependencyTracking) { @@ -621,6 +627,35 @@ std::pair typecheckFragment( return {FragmentTypeCheckStatus::Success, result}; } +FragmentAutocompleteStatusResult tryFragmentAutocomplete( + Frontend& frontend, + const ModuleName& moduleName, + Position cursorPosition, + FragmentContext context, + StringCompletionCallback stringCompletionCB +) +{ + // TODO: we should calculate fragmentEnd position here, by using context.newAstRoot and cursorPosition + try + { + Luau::FragmentAutocompleteResult fragmentAutocomplete = Luau::fragmentAutocomplete( + frontend, + context.newSrc, + moduleName, + cursorPosition, + context.opts, + std::move(stringCompletionCB), + context.DEPRECATED_fragmentEndPosition + ); + return {FragmentAutocompleteStatus::Success, std::move(fragmentAutocomplete)}; + } + catch (const Luau::InternalCompilerError& e) + { + if (FFlag::LogFragmentsFromAutocomplete) + logLuau(e.what()); + return {FragmentAutocompleteStatus::InternalIce, std::nullopt}; + } +} FragmentAutocompleteResult fragmentAutocomplete( Frontend& frontend, @@ -632,8 +667,9 @@ FragmentAutocompleteResult fragmentAutocomplete( std::optional fragmentEndPosition ) { - LUAU_ASSERT(FFlag::LuauAllowFragmentParsing); LUAU_ASSERT(FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete); + LUAU_TIMETRACE_SCOPE("Luau::fragmentAutocomplete", "FragmentAutocomplete"); + LUAU_TIMETRACE_ARGUMENT("name", moduleName.c_str()); const SourceModule* sourceModule = frontend.getSourceModule(moduleName); if (!sourceModule) diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 19740804..4bb801ae 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -1481,6 +1481,7 @@ ModulePtr check( NotNull{&typeFunctionRuntime}, NotNull(cg.rootScope), borrowConstraints(cg.constraints), + NotNull{&cg.scopeToFunction}, result->name, moduleResolver, requireCycles, diff --git a/Analysis/src/NonStrictTypeChecker.cpp b/Analysis/src/NonStrictTypeChecker.cpp index 453b552c..93a02c3f 100644 --- a/Analysis/src/NonStrictTypeChecker.cpp +++ b/Analysis/src/NonStrictTypeChecker.cpp @@ -19,7 +19,6 @@ #include #include -LUAU_FASTFLAGVARIABLE(LuauCountSelfCallsNonstrict) LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds) LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements) LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals) @@ -628,17 +627,6 @@ struct NonStrictTypeChecker NonStrictContext visit(AstExprCall* call) { - if (FFlag::LuauCountSelfCallsNonstrict) - return visitCall(call); - else - return visitCall_DEPRECATED(call); - } - - // rename this to `visit` when `FFlag::LuauCountSelfCallsNonstrict` is removed, and clean up above `visit`. - NonStrictContext visitCall(AstExprCall* call) - { - LUAU_ASSERT(FFlag::LuauCountSelfCallsNonstrict); - NonStrictContext fresh{}; TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func); if (!originalCallTy) @@ -747,109 +735,6 @@ struct NonStrictTypeChecker return fresh; } - // Remove with `FFlag::LuauCountSelfCallsNonstrict` clean up. - NonStrictContext visitCall_DEPRECATED(AstExprCall* call) - { - LUAU_ASSERT(!FFlag::LuauCountSelfCallsNonstrict); - - NonStrictContext fresh{}; - TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func); - if (!originalCallTy) - return fresh; - - TypeId fnTy = *originalCallTy; - if (auto fn = get(follow(fnTy))) - { - if (fn->isCheckedFunction) - { - // We know fn is a checked function, which means it looks like: - // (S1, ... SN) -> T & - // (~S1, unknown^N-1) -> error & - // (unknown, ~S2, unknown^N-2) -> error - // ... - // ... - // (unknown^N-1, ~S_N) -> error - std::vector argTypes; - argTypes.reserve(call->args.size); - // Pad out the arg types array with the types you would expect to see - TypePackIterator curr = begin(fn->argTypes); - TypePackIterator fin = end(fn->argTypes); - while (curr != fin) - { - argTypes.push_back(*curr); - ++curr; - } - if (auto argTail = curr.tail()) - { - if (const VariadicTypePack* vtp = get(follow(*argTail))) - { - while (argTypes.size() < call->args.size) - { - argTypes.push_back(vtp->ty); - } - } - } - - std::string functionName = getFunctionNameAsString(*call->func).value_or(""); - if (call->args.size > argTypes.size()) - { - // We are passing more arguments than we expect, so we should error - reportError(CheckedFunctionIncorrectArgs{functionName, argTypes.size(), call->args.size}, call->location); - return fresh; - } - - for (size_t i = 0; i < call->args.size; i++) - { - // For example, if the arg is "hi" - // The actual arg type is string - // The expected arg type is number - // The type of the argument in the overload is ~number - // We will compare arg and ~number - AstExpr* arg = call->args.data[i]; - TypeId expectedArgType = argTypes[i]; - std::shared_ptr norm = normalizer.normalize(expectedArgType); - DefId def = dfg->getDef(arg); - TypeId runTimeErrorTy; - // If we're dealing with any, negating any will cause all subtype tests to fail - // However, when someone calls this function, they're going to want to be able to pass it anything, - // for that reason, we manually inject never into the context so that the runtime test will always pass. - if (!norm) - reportError(NormalizationTooComplex{}, arg->location); - - if (norm && get(norm->tops)) - runTimeErrorTy = builtinTypes->neverType; - else - runTimeErrorTy = getOrCreateNegation(expectedArgType); - fresh.addContext(def, runTimeErrorTy); - } - - // Populate the context and now iterate through each of the arguments to the call to find out if we satisfy the types - for (size_t i = 0; i < call->args.size; i++) - { - AstExpr* arg = call->args.data[i]; - if (auto runTimeFailureType = willRunTimeError(arg, fresh)) - reportError(CheckedFunctionCallError{argTypes[i], *runTimeFailureType, functionName, i}, arg->location); - } - - if (call->args.size < argTypes.size()) - { - // We are passing fewer arguments than we expect - // so we need to ensure that the rest of the args are optional. - bool remainingArgsOptional = true; - for (size_t i = call->args.size; i < argTypes.size(); i++) - remainingArgsOptional = remainingArgsOptional && isOptional(argTypes[i]); - if (!remainingArgsOptional) - { - reportError(CheckedFunctionIncorrectArgs{functionName, argTypes.size(), call->args.size}, call->location); - return fresh; - } - } - } - } - - return fresh; - } - NonStrictContext visit(AstExprIndexName* indexName, ValueContext context) { if (FFlag::LuauNonStrictVisitorImprovements) diff --git a/Analysis/src/Transpiler.cpp b/Analysis/src/Transpiler.cpp index 218b269a..ab272587 100644 --- a/Analysis/src/Transpiler.cpp +++ b/Analysis/src/Transpiler.cpp @@ -12,7 +12,7 @@ LUAU_FASTFLAG(LuauStoreCSTData) LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon) -LUAU_FASTFLAG(LuauAstTypeGroup) +LUAU_FASTFLAG(LuauAstTypeGroup2) LUAU_FASTFLAG(LuauFixDoBlockEndLocation) namespace @@ -330,7 +330,7 @@ struct Printer_DEPRECATED else if (typeCount == 1) { bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is()); - if (FFlag::LuauAstTypeGroup ? shouldParenthesize : unconditionallyParenthesize) + if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize) writer.symbol("("); // Only variadic tail @@ -343,7 +343,7 @@ struct Printer_DEPRECATED visualizeTypeAnnotation(*list.types.data[0]); } - if (FFlag::LuauAstTypeGroup ? shouldParenthesize : unconditionallyParenthesize) + if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize) writer.symbol(")"); } else @@ -1349,7 +1349,7 @@ struct Printer else if (typeCount == 1) { bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is()); - if (FFlag::LuauAstTypeGroup ? shouldParenthesize : unconditionallyParenthesize) + if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize) writer.symbol("("); // Only variadic tail @@ -1362,7 +1362,7 @@ struct Printer visualizeTypeAnnotation(*list.types.data[0]); } - if (FFlag::LuauAstTypeGroup ? shouldParenthesize : unconditionallyParenthesize) + if (FFlag::LuauAstTypeGroup2 ? shouldParenthesize : unconditionallyParenthesize) writer.symbol(")"); } else @@ -2599,6 +2599,7 @@ struct Printer { writer.symbol("("); visualizeTypeAnnotation(*a->type); + advance(Position{a->location.end.line, a->location.end.column - 1}); writer.symbol(")"); } else if (const auto& a = typeAnnotation.as()) diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 0f578954..01db570a 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -29,7 +29,6 @@ LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAGVARIABLE(LuauTableKeysAreRValues) LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds) namespace Luau @@ -1848,16 +1847,8 @@ void TypeChecker2::visit(AstExprTable* expr) { for (const AstExprTable::Item& item : expr->items) { - if (FFlag::LuauTableKeysAreRValues) - { - if (item.key) - visit(item.key, ValueContext::RValue); - } - else - { - if (item.key) - visit(item.key, ValueContext::LValue); - } + if (item.key) + visit(item.key, ValueContext::RValue); visit(item.value, ValueContext::RValue); } } diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index a258f3ab..9b5f5ef7 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -51,6 +51,8 @@ LUAU_FASTFLAGVARIABLE(LuauMetatableTypeFunctions) LUAU_FASTFLAGVARIABLE(LuauClipNestedAndRecursiveUnion) LUAU_FASTFLAGVARIABLE(LuauDoNotGeneralizeInTypeFunctions) LUAU_FASTFLAGVARIABLE(LuauPreventReentrantTypeFunctionReduction) +LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil) +LUAU_FASTFLAGVARIABLE(LuauSkipNoRefineDuringRefinement) namespace Luau { @@ -2005,17 +2007,9 @@ TypeFunctionReductionResult refineTypeFunction( } else { - /* HACK: Refinements sometimes produce a type T & ~any under the assumption - * that ~any is the same as any. This is so so weird, but refinements needs - * some way to say "I may refine this, but I'm not sure." - * - * It does this by refining on a blocked type and deferring the decision - * until it is unblocked. - * - * Refinements also get negated, so we wind up with types like T & ~*blocked* - * - * We need to treat T & ~any as T in this case. - */ + if (FFlag::LuauSkipNoRefineDuringRefinement) + if (get(discriminant)) + return {target, {}}; if (auto nt = get(discriminant)) { if (get(follow(nt->ty))) @@ -2292,8 +2286,20 @@ TypeFunctionReductionResult intersectTypeFunction( continue; SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty); - if (!result.blockedTypes.empty()) - return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}}; + + if (FFlag::LuauIntersectNotNil) + { + for (TypeId blockedType : result.blockedTypes) + { + if (!get(blockedType)) + return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}}; + } + } + else + { + if (!result.blockedTypes.empty()) + return {std::nullopt, Reduction::MaybeOk, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}}; + } resultTy = result.result; } diff --git a/Analysis/src/TypeFunctionRuntime.cpp b/Analysis/src/TypeFunctionRuntime.cpp index 6ba5f261..fb33560e 100644 --- a/Analysis/src/TypeFunctionRuntime.cpp +++ b/Analysis/src/TypeFunctionRuntime.cpp @@ -18,6 +18,7 @@ LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit) LUAU_FASTFLAGVARIABLE(LuauTypeFunSingletonEquality) LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypeofReturnsType) LUAU_FASTFLAGVARIABLE(LuauTypeFunPrintFix) +LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents) namespace Luau { @@ -1110,7 +1111,7 @@ static int getFunctionGenerics(lua_State* L) // Luau: `self:parent() -> type` // Returns the parent of a class type -static int getClassParent(lua_State* L) +static int getClassParent_DEPRECATED(lua_State* L) { int argumentCount = lua_gettop(L); if (argumentCount != 1) @@ -1122,10 +1123,54 @@ static int getClassParent(lua_State* L) luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str()); // If the parent does not exist, we should return nil - if (!tfct->parent) + if (!tfct->parent_DEPRECATED) lua_pushnil(L); else - allocTypeUserData(L, (*tfct->parent)->type); + allocTypeUserData(L, (*tfct->parent_DEPRECATED)->type); + + return 1; +} + +// Luau: `self:readparent() -> type` +// Returns the read type of the class' parent +static int getReadParent(lua_State* L) +{ + int argumentCount = lua_gettop(L); + if (argumentCount != 1) + luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount); + + TypeFunctionTypeId self = getTypeUserData(L, 1); + auto tfct = get(self); + if (!tfct) + luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str()); + + // If the parent does not exist, we should return nil + if (!tfct->readParent) + lua_pushnil(L); + else + allocTypeUserData(L, (*tfct->readParent)->type); + + return 1; +} +// +// Luau: `self:writeparent() -> type` +// Returns the write type of the class' parent +static int getWriteParent(lua_State* L) +{ + int argumentCount = lua_gettop(L); + if (argumentCount != 1) + luaL_error(L, "type.parent: expected 1 arguments, but got %d", argumentCount); + + TypeFunctionTypeId self = getTypeUserData(L, 1); + auto tfct = get(self); + if (!tfct) + luaL_error(L, "type.parent: expected self to be a class, but got %s instead", getTag(L, self).c_str()); + + // If the parent does not exist, we should return nil + if (!tfct->writeParent) + lua_pushnil(L); + else + allocTypeUserData(L, (*tfct->writeParent)->type); return 1; } @@ -1553,7 +1598,7 @@ void registerTypeUserData(lua_State* L) {"components", getComponents}, // Class type methods - {"parent", getClassParent}, + {FFlag::LuauTypeFunReadWriteParents ? "readparent" : "parent", FFlag::LuauTypeFunReadWriteParents ? getReadParent : getClassParent_DEPRECATED}, // Function type methods (cont.) {"setgenerics", setFunctionGenerics}, @@ -1563,6 +1608,9 @@ void registerTypeUserData(lua_State* L) {"name", getGenericName}, {"ispack", getGenericIsPack}, + // move this under Class type methods when removing FFlagLuauTypeFunReadWriteParents + {FFlag::LuauTypeFunReadWriteParents ? "writeparent" : nullptr, FFlag::LuauTypeFunReadWriteParents ? getWriteParent : nullptr}, + {nullptr, nullptr} }; diff --git a/Analysis/src/TypeFunctionRuntimeBuilder.cpp b/Analysis/src/TypeFunctionRuntimeBuilder.cpp index fa2aab9e..8a8779b2 100644 --- a/Analysis/src/TypeFunctionRuntimeBuilder.cpp +++ b/Analysis/src/TypeFunctionRuntimeBuilder.cpp @@ -20,6 +20,7 @@ // currently, controls serialization, deserialization, and `type.copy` LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000); LUAU_FASTFLAG(LuauTypeFunFixHydratedClasses) +LUAU_FASTFLAG(LuauTypeFunReadWriteParents) namespace Luau { @@ -212,13 +213,13 @@ private: { // Since there aren't any new class types being created in type functions, we will deserialize by using a direct reference to the // original class - target = typeFunctionRuntime->typeArena.allocate(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, ty}); + target = typeFunctionRuntime->typeArena.allocate(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, ty}); } else { state->classesSerialized_DEPRECATED[c->name] = ty; target = typeFunctionRuntime->typeArena.allocate( - TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, /* classTy */ nullptr, c->name} + TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, /* classTy */ nullptr, c->name} ); } } @@ -441,7 +442,20 @@ private: c2->metatable = shallowSerialize(*c1->metatable); if (c1->parent) - c2->parent = shallowSerialize(*c1->parent); + { + TypeFunctionTypeId parent = shallowSerialize(*c1->parent); + + if (FFlag::LuauTypeFunReadWriteParents) + { + // we don't yet have read/write parents in the type inference engine. + c2->readParent = parent; + c2->writeParent = parent; + } + else + { + c2->parent_DEPRECATED = parent; + } + } } void serializeChildren(const GenericType* g1, TypeFunctionGenericType* g2) diff --git a/Analysis/src/TypePath.cpp b/Analysis/src/TypePath.cpp index 855ac303..baf7bb11 100644 --- a/Analysis/src/TypePath.cpp +++ b/Analysis/src/TypePath.cpp @@ -14,7 +14,7 @@ #include LUAU_FASTFLAG(LuauSolverV2); - +LUAU_FASTFLAGVARIABLE(LuauDisableNewSolverAssertsInMixedMode); // 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 // logic. @@ -156,14 +156,16 @@ Path PathBuilder::build() PathBuilder& PathBuilder::readProp(std::string name) { - LUAU_ASSERT(FFlag::LuauSolverV2); + if (!FFlag::LuauDisableNewSolverAssertsInMixedMode) + LUAU_ASSERT(FFlag::LuauSolverV2); components.push_back(Property{std::move(name), true}); return *this; } PathBuilder& PathBuilder::writeProp(std::string name) { - LUAU_ASSERT(FFlag::LuauSolverV2); + if (!FFlag::LuauDisableNewSolverAssertsInMixedMode) + LUAU_ASSERT(FFlag::LuauSolverV2); components.push_back(Property{std::move(name), false}); return *this; } diff --git a/Analysis/src/TypeUtils.cpp b/Analysis/src/TypeUtils.cpp index bb68503f..bf8cf533 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/Analysis/src/TypeUtils.cpp @@ -14,7 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete); LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope); LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds) - +LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode) namespace Luau { @@ -550,7 +550,10 @@ std::vector findBlockedArgTypesIn(AstExprCall* expr, NotNullparent.get()) { if (scope->interiorFreeTypes) diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index 926245ea..47b7cc41 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -33,38 +33,20 @@ struct PromoteTypeLevels final : TypeOnceVisitor const TypeArena* typeArena = nullptr; TypeLevel minLevel; - Scope* outerScope = nullptr; - bool useScopes; - - PromoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes) + PromoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel) : log(log) , typeArena(typeArena) , minLevel(minLevel) - , outerScope(outerScope) - , useScopes(useScopes) { } template void promote(TID ty, T* t) { - if (useScopes && !t) - return; - LUAU_ASSERT(t); - if (useScopes) - { - if (subsumesStrict(outerScope, t->scope)) - log.changeScope(ty, NotNull{outerScope}); - } - else - { - if (minLevel.subsumesStrict(t->level)) - { - log.changeLevel(ty, minLevel); - } - } + if (minLevel.subsumesStrict(t->level)) + log.changeLevel(ty, minLevel); } bool visit(TypeId ty) override @@ -141,23 +123,23 @@ struct PromoteTypeLevels final : TypeOnceVisitor } }; -static void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes, TypeId ty) +static void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, TypeId ty) { // Type levels of types from other modules are already global, so we don't need to promote anything inside if (ty->owningArena != typeArena) return; - PromoteTypeLevels ptl{log, typeArena, minLevel, outerScope, useScopes}; + PromoteTypeLevels ptl{log, typeArena, minLevel}; ptl.traverse(ty); } -void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, Scope* outerScope, bool useScopes, TypePackId tp) +void promoteTypeLevels(TxnLog& log, const TypeArena* typeArena, TypeLevel minLevel, TypePackId tp) { // Type levels of types from other modules are already global, so we don't need to promote anything inside if (tp->owningArena != typeArena) return; - PromoteTypeLevels ptl{log, typeArena, minLevel, outerScope, useScopes}; + PromoteTypeLevels ptl{log, typeArena, minLevel}; ptl.traverse(tp); } @@ -370,12 +352,9 @@ static std::optional> getTableMatchT } template -static bool subsumes(bool useScopes, TY_A* left, TY_B* right) +static bool subsumes(TY_A* left, TY_B* right) { - if (useScopes) - return subsumes(left->scope, right->scope); - else - return left->level.subsumes(right->level); + return left->level.subsumes(right->level); } TypeMismatch::Context Unifier::mismatchContext() @@ -464,7 +443,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool auto superFree = log.getMutable(superTy); auto subFree = log.getMutable(subTy); - if (superFree && subFree && subsumes(useNewSolver, superFree, subFree)) + if (superFree && subFree && subsumes(superFree, subFree)) { if (!occursCheck(subTy, superTy, /* reversed = */ false)) log.replace(subTy, BoundType(superTy)); @@ -475,7 +454,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool { if (!occursCheck(superTy, subTy, /* reversed = */ true)) { - if (subsumes(useNewSolver, superFree, subFree)) + if (subsumes(superFree, subFree)) { log.changeLevel(subTy, superFree->level); } @@ -489,7 +468,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool { // Unification can't change the level of a generic. auto subGeneric = log.getMutable(subTy); - if (subGeneric && !subsumes(useNewSolver, subGeneric, superFree)) + if (subGeneric && !subsumes(subGeneric, superFree)) { // TODO: a more informative error message? CLI-39912 reportError(location, GenericError{"Generic subtype escaping scope"}); @@ -498,7 +477,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool if (!occursCheck(superTy, subTy, /* reversed = */ true)) { - promoteTypeLevels(log, types, superFree->level, superFree->scope, useNewSolver, subTy); + promoteTypeLevels(log, types, superFree->level, subTy); Widen widen{types, builtinTypes}; log.replace(superTy, BoundType(widen(subTy))); @@ -515,7 +494,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool // Unification can't change the level of a generic. auto superGeneric = log.getMutable(superTy); - if (superGeneric && !subsumes(useNewSolver, superGeneric, subFree)) + if (superGeneric && !subsumes(superGeneric, subFree)) { // TODO: a more informative error message? CLI-39912 reportError(location, GenericError{"Generic supertype escaping scope"}); @@ -524,7 +503,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool if (!occursCheck(subTy, superTy, /* reversed = */ false)) { - promoteTypeLevels(log, types, subFree->level, subFree->scope, useNewSolver, superTy); + promoteTypeLevels(log, types, subFree->level, superTy); log.replace(subTy, BoundType(superTy)); } @@ -536,7 +515,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool auto superGeneric = log.getMutable(superTy); auto subGeneric = log.getMutable(subTy); - if (superGeneric && subGeneric && subsumes(useNewSolver, superGeneric, subGeneric)) + if (superGeneric && subGeneric && subsumes(superGeneric, subGeneric)) { if (!occursCheck(subTy, superTy, /* reversed = */ false)) log.replace(subTy, BoundType(superTy)); @@ -753,9 +732,6 @@ void Unifier::tryUnifyUnionWithType(TypeId subTy, const UnionType* subUnion, Typ std::unique_ptr innerState = makeChildUnifier(); innerState->tryUnify_(type, superTy); - if (useNewSolver) - logs.push_back(std::move(innerState->log)); - if (auto e = hasUnificationTooComplex(innerState->errors)) unificationTooComplex = e; else if (innerState->failure) @@ -870,13 +846,8 @@ void Unifier::tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTyp if (!innerState->failure) { found = true; - if (useNewSolver) - logs.push_back(std::move(innerState->log)); - else - { - log.concat(std::move(innerState->log)); - break; - } + log.concat(std::move(innerState->log)); + break; } else if (innerState->errors.empty()) { @@ -895,9 +866,6 @@ void Unifier::tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTyp } } - if (useNewSolver) - log.concatAsUnion(combineLogsIntoUnion(std::move(logs)), NotNull{types}); - if (unificationTooComplex) { reportError(*unificationTooComplex); @@ -975,16 +943,10 @@ void Unifier::tryUnifyTypeWithIntersection(TypeId subTy, TypeId superTy, const I firstFailedOption = {innerState->errors.front()}; } - if (useNewSolver) - logs.push_back(std::move(innerState->log)); - else - log.concat(std::move(innerState->log)); + log.concat(std::move(innerState->log)); failure |= innerState->failure; } - if (useNewSolver) - log.concat(combineLogsIntoIntersection(std::move(logs))); - if (unificationTooComplex) reportError(*unificationTooComplex); else if (firstFailedOption) @@ -1032,28 +994,6 @@ void Unifier::tryUnifyIntersectionWithType(TypeId subTy, const IntersectionType* } } - if (useNewSolver && normalize) - { - // Sometimes a negation type is inside one of the types, e.g. { p: number } & { p: ~number }. - NegationTypeFinder finder; - finder.traverse(subTy); - - if (finder.found) - { - // It is possible that A & B <: T even though A subNorm = normalizer->normalize(subTy); - std::shared_ptr superNorm = normalizer->normalize(superTy); - if (subNorm && superNorm) - tryUnifyNormalizedTypes(subTy, superTy, *subNorm, *superNorm, "none of the intersection parts are compatible"); - else - reportError(location, NormalizationTooComplex{}); - - return; - } - } - std::vector logs; for (size_t i = 0; i < uv->parts.size(); ++i) @@ -1070,7 +1010,7 @@ void Unifier::tryUnifyIntersectionWithType(TypeId subTy, const IntersectionType* { found = true; errorsSuppressed = innerState->failure; - if (useNewSolver || innerState->failure) + if (innerState->failure) logs.push_back(std::move(innerState->log)); else { @@ -1085,9 +1025,7 @@ void Unifier::tryUnifyIntersectionWithType(TypeId subTy, const IntersectionType* } } - if (useNewSolver) - log.concat(combineLogsIntoIntersection(std::move(logs))); - else if (errorsSuppressed) + if (errorsSuppressed) log.concat(std::move(logs.front())); if (unificationTooComplex) @@ -1201,24 +1139,6 @@ void Unifier::tryUnifyNormalizedTypes( } } - if (useNewSolver) - { - for (TypeId superTable : superNorm.tables) - { - std::unique_ptr innerState = makeChildUnifier(); - innerState->tryUnify(subClass, superTable); - - if (innerState->errors.empty()) - { - found = true; - log.concat(std::move(innerState->log)); - break; - } - else if (auto e = hasUnificationTooComplex(innerState->errors)) - return reportError(*e); - } - } - if (!found) { return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); @@ -1503,12 +1423,6 @@ struct WeirdIter } }; -void Unifier::enableNewSolver() -{ - useNewSolver = true; - log.useScopes = true; -} - ErrorVec Unifier::canUnify(TypeId subTy, TypeId superTy) { std::unique_ptr s = makeChildUnifier(); @@ -1588,8 +1502,6 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal if (!occursCheck(superTp, subTp, /* reversed = */ true)) { Widen widen{types, builtinTypes}; - if (useNewSolver) - promoteTypeLevels(log, types, superFree->level, superFree->scope, /*useScopes*/ true, subTp); log.replace(superTp, Unifiable::Bound(widen(subTp))); } } @@ -1597,8 +1509,6 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal { if (!occursCheck(subTp, superTp, /* reversed = */ false)) { - if (useNewSolver) - promoteTypeLevels(log, types, subFree->level, subFree->scope, /*useScopes*/ true, superTp); log.replace(subTp, Unifiable::Bound(superTp)); } } @@ -1688,74 +1598,28 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal // If both are at the end, we're done if (!superIter.good() && !subIter.good()) { - if (useNewSolver) + const bool lFreeTail = superTpv->tail && log.getMutable(log.follow(*superTpv->tail)) != nullptr; + const bool rFreeTail = subTpv->tail && log.getMutable(log.follow(*subTpv->tail)) != nullptr; + if (lFreeTail && rFreeTail) { - if (subIter.tail() && superIter.tail()) - tryUnify_(*subIter.tail(), *superIter.tail()); - else if (subIter.tail()) - { - const TypePackId subTail = log.follow(*subIter.tail()); - - if (log.get(subTail)) - tryUnify_(subTail, emptyTp); - else if (log.get(subTail)) - reportError(location, TypePackMismatch{subTail, emptyTp}); - else if (log.get(subTail) || log.get(subTail)) - { - // Nothing. This is ok. - } - else - { - ice("Unexpected subtype tail pack " + toString(subTail), location); - } - } - else if (superIter.tail()) - { - const TypePackId superTail = log.follow(*superIter.tail()); - - if (log.get(superTail)) - tryUnify_(emptyTp, superTail); - else if (log.get(superTail)) - reportError(location, TypePackMismatch{emptyTp, superTail}); - else if (log.get(superTail) || log.get(superTail)) - { - // Nothing. This is ok. - } - else - { - ice("Unexpected supertype tail pack " + toString(superTail), location); - } - } - else - { - // Nothing. This is ok. - } + tryUnify_(*subTpv->tail, *superTpv->tail); } - else + else if (lFreeTail) { - const bool lFreeTail = superTpv->tail && log.getMutable(log.follow(*superTpv->tail)) != nullptr; - const bool rFreeTail = subTpv->tail && log.getMutable(log.follow(*subTpv->tail)) != nullptr; - if (lFreeTail && rFreeTail) - { + tryUnify_(emptyTp, *superTpv->tail); + } + else if (rFreeTail) + { + tryUnify_(emptyTp, *subTpv->tail); + } + else if (subTpv->tail && superTpv->tail) + { + if (log.getMutable(superIter.packId)) + tryUnifyVariadics(subIter.packId, superIter.packId, false, int(subIter.index)); + else if (log.getMutable(subIter.packId)) + tryUnifyVariadics(superIter.packId, subIter.packId, true, int(superIter.index)); + else tryUnify_(*subTpv->tail, *superTpv->tail); - } - else if (lFreeTail) - { - tryUnify_(emptyTp, *superTpv->tail); - } - else if (rFreeTail) - { - tryUnify_(emptyTp, *subTpv->tail); - } - else if (subTpv->tail && superTpv->tail) - { - if (log.getMutable(superIter.packId)) - tryUnifyVariadics(subIter.packId, superIter.packId, false, int(subIter.index)); - else if (log.getMutable(subIter.packId)) - tryUnifyVariadics(superIter.packId, subIter.packId, true, int(superIter.index)); - else - tryUnify_(*subTpv->tail, *superTpv->tail); - } } break; @@ -2212,7 +2076,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, variance = Invariant; std::unique_ptr innerState = makeChildUnifier(); - if (useNewSolver || FFlag::LuauFixIndexerSubtypingOrdering) + if (FFlag::LuauFixIndexerSubtypingOrdering) innerState->tryUnify_(prop.type(), superTable->indexer->indexResultType); else { @@ -2497,49 +2361,8 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed) { case TableState::Free: { - if (useNewSolver) - { - std::unique_ptr innerState = makeChildUnifier(); - bool missingProperty = false; - - for (const auto& [propName, prop] : subTable->props) - { - if (std::optional mtPropTy = findTablePropertyRespectingMeta(superTy, propName)) - { - innerState->tryUnify(prop.type(), *mtPropTy); - } - else - { - reportError(mismatchError); - missingProperty = true; - break; - } - } - - if (const TableType* superTable = log.get(log.follow(superMetatable->table))) - { - // TODO: Unify indexers. - } - - if (auto e = hasUnificationTooComplex(innerState->errors)) - reportError(*e); - else if (!innerState->errors.empty()) - reportError(TypeError{ - location, - TypeMismatch{reversed ? subTy : superTy, reversed ? superTy : subTy, "", innerState->errors.front(), mismatchContext()} - }); - else if (!missingProperty) - { - log.concat(std::move(innerState->log)); - log.bindTable(subTy, superTy); - failure |= innerState->failure; - } - } - else - { - tryUnify_(subTy, superMetatable->table); - log.bindTable(subTy, superTy); - } + tryUnify_(subTy, superMetatable->table); + log.bindTable(subTy, superTy); break; } @@ -2865,18 +2688,9 @@ std::optional Unifier::findTablePropertyRespectingMeta(TypeId lhsType, N return Luau::findTablePropertyRespectingMeta(builtinTypes, errors, lhsType, name, location); } -TxnLog Unifier::combineLogsIntoIntersection(std::vector logs) -{ - LUAU_ASSERT(useNewSolver); - TxnLog result(useNewSolver); - for (TxnLog& log : logs) - result.concatAsIntersections(std::move(log), NotNull{types}); - return result; -} - TxnLog Unifier::combineLogsIntoUnion(std::vector logs) { - TxnLog result(useNewSolver); + TxnLog result; for (TxnLog& log : logs) result.concatAsUnion(std::move(log), NotNull{types}); return result; @@ -3021,9 +2835,6 @@ std::unique_ptr Unifier::makeChildUnifier() u->normalize = normalize; u->checkInhabited = checkInhabited; - if (useNewSolver) - u->enableNewSolver(); - return u; } diff --git a/Ast/include/Luau/ParseResult.h b/Ast/include/Luau/ParseResult.h index 1ad9c5e9..7803dc55 100644 --- a/Ast/include/Luau/ParseResult.h +++ b/Ast/include/Luau/ParseResult.h @@ -71,6 +71,19 @@ struct ParseResult CstNodeMap cstNodeMap{nullptr}; }; +struct ParseExprResult +{ + AstExpr* expr; + size_t lines = 0; + + std::vector hotcomments; + std::vector errors; + + std::vector commentLocations; + + CstNodeMap cstNodeMap{nullptr}; +}; + static constexpr const char* kParseNameError = "%error-id%"; } // namespace Luau diff --git a/Ast/include/Luau/Parser.h b/Ast/include/Luau/Parser.h index bd3cdcda..cfe7d08c 100644 --- a/Ast/include/Luau/Parser.h +++ b/Ast/include/Luau/Parser.h @@ -63,6 +63,14 @@ public: ParseOptions options = ParseOptions() ); + static ParseExprResult parseExpr( + const char* buffer, + std::size_t bufferSize, + AstNameTable& names, + Allocator& allocator, + ParseOptions options = ParseOptions() + ); + private: struct Name; struct Binding; diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 1165a38c..a7c81dd9 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(LuauAllowFragmentParsing) LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams) LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes) LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForClassNames) @@ -26,7 +25,7 @@ LUAU_FASTFLAGVARIABLE(LuauFixFunctionNameStartPosition) LUAU_FASTFLAGVARIABLE(LuauExtendStatEndPosWithSemicolon) LUAU_FASTFLAGVARIABLE(LuauStoreCSTData) LUAU_FASTFLAGVARIABLE(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType) -LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup) +LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup2) LUAU_FASTFLAGVARIABLE(ParserNoErrorLimit) LUAU_FASTFLAGVARIABLE(LuauFixDoBlockEndLocation) @@ -182,6 +181,28 @@ ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& n } } +ParseExprResult Parser::parseExpr(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options) +{ + LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser"); + + Parser p(buffer, bufferSize, names, allocator, options); + + try + { + AstExpr* expr = p.parseExpr(); + size_t lines = p.lexer.current().location.end.line + (bufferSize > 0 && buffer[bufferSize - 1] != '\n'); + + return ParseExprResult{expr, lines, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations), std::move(p.cstNodeMap)}; + } + catch (ParseError& err) + { + // when catching a fatal error, append it to the list of non-fatal errors and return + p.parseErrors.push_back(err); + + return ParseExprResult{nullptr, 0, {}, p.parseErrors, {}, std::move(p.cstNodeMap)}; + } +} + Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, const ParseOptions& options) : options(options) , lexer(buffer, bufferSize, names, options.parseFragment ? options.parseFragment->resumePosition : Position(0, 0)) @@ -197,18 +218,9 @@ Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Alloc functionStack.reserve(8); functionStack.push_back(top); - if (FFlag::LuauAllowFragmentParsing) - { - nameSelf = names.getOrAdd("self"); - nameNumber = names.getOrAdd("number"); - nameError = names.getOrAdd(kParseNameError); - } - else - { - nameSelf = names.addStatic("self"); - nameNumber = names.addStatic("number"); - nameError = names.addStatic(kParseNameError); - } + nameSelf = names.getOrAdd("self"); + nameNumber = names.getOrAdd("number"); + nameError = names.getOrAdd(kParseNameError); nameNil = names.getOrAdd("nil"); // nil is a reserved keyword matchRecoveryStopOnToken.assign(Lexeme::Type::Reserved_END, 0); @@ -231,13 +243,10 @@ Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Alloc scratchLocal.reserve(16); scratchBinding.reserve(16); - if (FFlag::LuauAllowFragmentParsing) + if (options.parseFragment) { - if (options.parseFragment) - { - localMap = options.parseFragment->localMap; - localStack = options.parseFragment->localStack; - } + localMap = options.parseFragment->localMap; + localStack = options.parseFragment->localStack; } } @@ -1723,7 +1732,7 @@ std::pair Parser::parseReturnType() if (lexer.current().type != Lexeme::SkinnyArrow && resultNames.empty()) { // If it turns out that it's just '(A)', it's possible that there are unions/intersections to follow, so fold over it. - if (FFlag::LuauAstTypeGroup) + if (FFlag::LuauAstTypeGroup2) { if (result.size() == 1 && varargAnnotation == nullptr) { @@ -2034,6 +2043,7 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray if (lexer.current().type != ')') varargAnnotation = parseTypeList(params, names); + Location closeArgsLocation = lexer.current().location; expectMatchAndConsume(')', parameterStart, true); matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--; @@ -2052,8 +2062,8 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray return {{}, allocator.alloc(begin.location, AstTypeList{paramTypes, nullptr})}; else { - if (FFlag::LuauAstTypeGroup) - return {allocator.alloc(Location(parameterStart.location, params[0]->location), params[0]), {}}; + if (FFlag::LuauAstTypeGroup2) + return {allocator.alloc(Location(parameterStart.location, closeArgsLocation), params[0]), {}}; else return {params[0], {}}; } @@ -3562,7 +3572,7 @@ AstArray Parser::parseTypeParams(Position* openingPosition, TempV // the next lexeme is one that follows a type // (&, |, ?), then assume that this was actually a // parenthesized type. - if (FFlag::LuauAstTypeGroup) + if (FFlag::LuauAstTypeGroup2) { auto parenthesizedType = explicitTypePack->typeList.types.data[0]; parameters.push_back( diff --git a/CLI/src/Repl.cpp b/CLI/src/Repl.cpp index 2dec1d8c..3e3ae182 100644 --- a/CLI/src/Repl.cpp +++ b/CLI/src/Repl.cpp @@ -791,8 +791,6 @@ int replMain(int argc, char** argv) { Luau::assertHandler() = assertionHandler; - setLuauFlagsDefault(); - #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); #endif diff --git a/CLI/src/ReplEntry.cpp b/CLI/src/ReplEntry.cpp index 7e5f9e06..a69e4a37 100644 --- a/CLI/src/ReplEntry.cpp +++ b/CLI/src/ReplEntry.cpp @@ -1,7 +1,10 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/Repl.h" +#include "Luau/Flags.h" int main(int argc, char** argv) { + setLuauFlagsDefault(); + return replMain(argc, argv); } diff --git a/CodeGen/src/CodeGen.cpp b/CodeGen/src/CodeGen.cpp index a518165f..01e87d3d 100644 --- a/CodeGen/src/CodeGen.cpp +++ b/CodeGen/src/CodeGen.cpp @@ -44,7 +44,6 @@ LUAU_FASTFLAGVARIABLE(DebugCodegenNoOpt) LUAU_FASTFLAGVARIABLE(DebugCodegenOptSize) LUAU_FASTFLAGVARIABLE(DebugCodegenSkipNumbering) -LUAU_FASTFLAGVARIABLE(CodegenWiderLoweringStats) // Per-module IR instruction count limit LUAU_FASTINTVARIABLE(CodegenHeuristicsInstructionLimit, 1'048'576) // 1 M diff --git a/CodeGen/src/CodeGenLower.h b/CodeGen/src/CodeGenLower.h index 406fe5c9..c2117a12 100644 --- a/CodeGen/src/CodeGenLower.h +++ b/CodeGen/src/CodeGenLower.h @@ -25,7 +25,6 @@ LUAU_FASTFLAG(DebugCodegenNoOpt) LUAU_FASTFLAG(DebugCodegenOptSize) LUAU_FASTFLAG(DebugCodegenSkipNumbering) -LUAU_FASTFLAG(CodegenWiderLoweringStats) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_FASTINT(CodegenHeuristicsBlockLimit) LUAU_FASTINT(CodegenHeuristicsBlockInstructionLimit) @@ -300,8 +299,7 @@ inline bool lowerFunction( CodeGenCompilationResult& codeGenCompilationResult ) { - if (FFlag::CodegenWiderLoweringStats) - ir.function.stats = stats; + ir.function.stats = stats; killUnusedBlocks(ir.function); diff --git a/CodeGen/src/IrLoweringA64.cpp b/CodeGen/src/IrLoweringA64.cpp index 1eece87f..086b91ed 100644 --- a/CodeGen/src/IrLoweringA64.cpp +++ b/CodeGen/src/IrLoweringA64.cpp @@ -13,7 +13,6 @@ #include "lgc.h" LUAU_FASTFLAG(LuauVectorLibNativeDot) -LUAU_FASTFLAG(LuauCodeGenLerp) namespace Luau { @@ -706,7 +705,6 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) } case IrCmd::SELECT_NUM: { - LUAU_ASSERT(FFlag::LuauCodeGenLerp); inst.regA64 = regs.allocReuse(KindA64::d, index, {inst.a, inst.b, inst.c, inst.d}); RegisterA64 temp1 = tempDouble(inst.a); diff --git a/CodeGen/src/IrLoweringX64.cpp b/CodeGen/src/IrLoweringX64.cpp index 0f99959f..373f4f59 100644 --- a/CodeGen/src/IrLoweringX64.cpp +++ b/CodeGen/src/IrLoweringX64.cpp @@ -17,7 +17,6 @@ #include "lgc.h" LUAU_FASTFLAG(LuauVectorLibNativeDot) -LUAU_FASTFLAG(LuauCodeGenLerp) namespace Luau { @@ -625,7 +624,6 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) } case IrCmd::SELECT_NUM: { - LUAU_ASSERT(FFlag::LuauCodeGenLerp); inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.c, inst.d}); // can't reuse b if a is a memory operand ScopedRegX64 tmp{regs, SizeX64::xmmword}; diff --git a/CodeGen/src/IrTranslateBuiltins.cpp b/CodeGen/src/IrTranslateBuiltins.cpp index a5fa3ad0..ec72b692 100644 --- a/CodeGen/src/IrTranslateBuiltins.cpp +++ b/CodeGen/src/IrTranslateBuiltins.cpp @@ -13,9 +13,7 @@ static const int kMinMaxUnrolledParams = 5; static const int kBit32BinaryOpUnrolledParams = 5; -LUAU_FASTFLAGVARIABLE(LuauVectorLibNativeCodegen); LUAU_FASTFLAGVARIABLE(LuauVectorLibNativeDot); -LUAU_FASTFLAGVARIABLE(LuauCodeGenLerp); namespace Luau { @@ -297,8 +295,6 @@ static BuiltinImplResult translateBuiltinMathLerp( int pcpos ) { - LUAU_ASSERT(FFlag::LuauCodeGenLerp); - if (nparams < 3 || nresults > 1) return {BuiltinImplType::None, -1}; @@ -936,8 +932,6 @@ static BuiltinImplResult translateBuiltinVectorMagnitude( int pcpos ) { - LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen); - IrOp arg1 = build.vmReg(arg); if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant) @@ -985,8 +979,6 @@ static BuiltinImplResult translateBuiltinVectorNormalize( int pcpos ) { - LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen); - IrOp arg1 = build.vmReg(arg); if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant) @@ -1037,8 +1029,6 @@ static BuiltinImplResult translateBuiltinVectorNormalize( static BuiltinImplResult translateBuiltinVectorCross(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos) { - LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen); - IrOp arg1 = build.vmReg(arg); if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant) @@ -1076,8 +1066,6 @@ static BuiltinImplResult translateBuiltinVectorCross(IrBuilder& build, int npara static BuiltinImplResult translateBuiltinVectorDot(IrBuilder& build, int nparams, int ra, int arg, IrOp args, IrOp arg3, int nresults, int pcpos) { - LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen); - IrOp arg1 = build.vmReg(arg); if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant) @@ -1130,8 +1118,6 @@ static BuiltinImplResult translateBuiltinVectorMap1( int pcpos ) { - LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen); - IrOp arg1 = build.vmReg(arg); if (nparams != 1 || nresults > 1 || arg1.kind == IrOpKind::Constant) @@ -1165,8 +1151,6 @@ static BuiltinImplResult translateBuiltinVectorClamp( int pcpos ) { - LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen); - IrOp arg1 = build.vmReg(arg); if (nparams != 3 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant || arg3.kind == IrOpKind::Constant) @@ -1231,8 +1215,6 @@ static BuiltinImplResult translateBuiltinVectorMap2( int pcpos ) { - LUAU_ASSERT(FFlag::LuauVectorLibNativeCodegen); - IrOp arg1 = build.vmReg(arg); if (nparams != 2 || nresults > 1 || arg1.kind == IrOpKind::Constant || args.kind == IrOpKind::Constant) @@ -1273,8 +1255,6 @@ BuiltinImplResult translateBuiltin( int pcpos ) { - BuiltinImplResult noneResult = {BuiltinImplType::None, -1}; - // Builtins are not allowed to handle variadic arguments if (nparams == LUA_MULTRET) return {BuiltinImplType::None, -1}; @@ -1396,36 +1376,29 @@ BuiltinImplResult translateBuiltin( case LBF_BUFFER_WRITEF64: return translateBuiltinBufferWrite(build, nparams, ra, arg, args, arg3, nresults, pcpos, IrCmd::BUFFER_WRITEF64, 8, IrCmd::NOP); case LBF_VECTOR_MAGNITUDE: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMagnitude(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult; + return translateBuiltinVectorMagnitude(build, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_NORMALIZE: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorNormalize(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult; + return translateBuiltinVectorNormalize(build, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_CROSS: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorCross(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult; + return translateBuiltinVectorCross(build, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_DOT: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorDot(build, nparams, ra, arg, args, arg3, nresults, pcpos) : noneResult; + return translateBuiltinVectorDot(build, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_FLOOR: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::FLOOR_NUM, nparams, ra, arg, args, arg3, nresults, pcpos) - : noneResult; + return translateBuiltinVectorMap1(build, IrCmd::FLOOR_NUM, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_CEIL: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::CEIL_NUM, nparams, ra, arg, args, arg3, nresults, pcpos) - : noneResult; + return translateBuiltinVectorMap1(build, IrCmd::CEIL_NUM, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_ABS: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::ABS_NUM, nparams, ra, arg, args, arg3, nresults, pcpos) - : noneResult; + return translateBuiltinVectorMap1(build, IrCmd::ABS_NUM, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_SIGN: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap1(build, IrCmd::SIGN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos) - : noneResult; + return translateBuiltinVectorMap1(build, IrCmd::SIGN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_CLAMP: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorClamp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos) - : noneResult; + return translateBuiltinVectorClamp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos); case LBF_VECTOR_MIN: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap2(build, IrCmd::MIN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos) - : noneResult; + return translateBuiltinVectorMap2(build, IrCmd::MIN_NUM, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_VECTOR_MAX: - return FFlag::LuauVectorLibNativeCodegen ? translateBuiltinVectorMap2(build, IrCmd::MAX_NUM, nparams, ra, arg, args, arg3, nresults, pcpos) - : noneResult; + return translateBuiltinVectorMap2(build, IrCmd::MAX_NUM, nparams, ra, arg, args, arg3, nresults, pcpos); case LBF_MATH_LERP: - return FFlag::LuauCodeGenLerp ? translateBuiltinMathLerp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos) : noneResult; + return translateBuiltinMathLerp(build, nparams, ra, arg, args, arg3, nresults, fallback, pcpos); default: return {BuiltinImplType::None, -1}; } diff --git a/CodeGen/src/IrUtils.cpp b/CodeGen/src/IrUtils.cpp index 02d19e49..54902435 100644 --- a/CodeGen/src/IrUtils.cpp +++ b/CodeGen/src/IrUtils.cpp @@ -17,7 +17,6 @@ #include LUAU_FASTFLAG(LuauVectorLibNativeDot); -LUAU_FASTFLAG(LuauCodeGenLerp); namespace Luau { @@ -663,7 +662,6 @@ void foldConstants(IrBuilder& build, IrFunction& function, IrBlock& block, uint3 } break; case IrCmd::SELECT_NUM: - LUAU_ASSERT(FFlag::LuauCodeGenLerp); if (inst.c.kind == IrOpKind::Constant && inst.d.kind == IrOpKind::Constant) { double c = function.doubleOp(inst.c); diff --git a/CodeGen/src/OptimizeConstProp.cpp b/CodeGen/src/OptimizeConstProp.cpp index e2fefbed..8cdd1dc8 100644 --- a/CodeGen/src/OptimizeConstProp.cpp +++ b/CodeGen/src/OptimizeConstProp.cpp @@ -22,7 +22,6 @@ LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64) LUAU_FASTINTVARIABLE(LuauCodeGenLiveSlotReuseLimit, 8) LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks) LUAU_FASTFLAG(LuauVectorLibNativeDot) -LUAU_FASTFLAGVARIABLE(LuauCodeGenLimitLiveSlotReuse) namespace Luau { @@ -200,11 +199,7 @@ struct ConstPropState // Same goes for table array elements as well void invalidateHeapTableData() { - if (FFlag::LuauCodeGenLimitLiveSlotReuse) - getSlotNodeCache.clear(); - else - getSlotNodeCache_DEPRECATED.clear(); - + getSlotNodeCache.clear(); checkSlotMatchCache.clear(); getArrAddrCache.clear(); @@ -428,8 +423,6 @@ struct ConstPropState // Note that this pressure is approximate, as some values that might have been live at one point could have been marked dead later int getMaxInternalOverlap(std::vector& set, size_t slot) { - CODEGEN_ASSERT(FFlag::LuauCodeGenLimitLiveSlotReuse); - // Start with one live range for the slot we want to reuse int curr = 1; @@ -487,9 +480,7 @@ struct ConstPropState regs[i] = RegisterInfo(); maxReg = 0; - - if (FFlag::LuauCodeGenLimitLiveSlotReuse) - instPos = 0u; + instPos = 0u; inSafeEnv = false; checkedGc = false; @@ -526,7 +517,6 @@ struct ConstPropState // Heap changes might affect table state std::vector getSlotNodeCache; // Additionally, pcpos argument might be different - std::vector getSlotNodeCache_DEPRECATED; // Additionally, pcpos argument might be different std::vector checkSlotMatchCache; // Additionally, fallback block argument might be different std::vector getArrAddrCache; @@ -651,8 +641,7 @@ static void handleBuiltinEffects(ConstPropState& state, LuauBuiltinFunction bfid static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& function, IrBlock& block, IrInst& inst, uint32_t index) { - if (FFlag::LuauCodeGenLimitLiveSlotReuse) - state.instPos++; + state.instPos++; switch (inst.cmd) { @@ -1260,49 +1249,30 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& state.getArrAddrCache.push_back(index); break; case IrCmd::GET_SLOT_NODE_ADDR: - if (FFlag::LuauCodeGenLimitLiveSlotReuse) + for (size_t i = 0; i < state.getSlotNodeCache.size(); i++) { - for (size_t i = 0; i < state.getSlotNodeCache.size(); i++) + auto&& [prevIdx, num, lastNum] = state.getSlotNodeCache[i]; + + const IrInst& prev = function.instructions[prevIdx]; + + if (prev.a == inst.a && prev.c == inst.c) { - auto&& [prevIdx, num, lastNum] = state.getSlotNodeCache[i]; + // Check if this reuse will increase the overall register pressure over the limit + int limit = FInt::LuauCodeGenLiveSlotReuseLimit; - const IrInst& prev = function.instructions[prevIdx]; + if (int(state.getSlotNodeCache.size()) > limit && state.getMaxInternalOverlap(state.getSlotNodeCache, i) > limit) + return; - if (prev.a == inst.a && prev.c == inst.c) - { - // Check if this reuse will increase the overall register pressure over the limit - int limit = FInt::LuauCodeGenLiveSlotReuseLimit; + // Update live range of the value from the optimization standpoint + lastNum = state.instPos; - if (int(state.getSlotNodeCache.size()) > limit && state.getMaxInternalOverlap(state.getSlotNodeCache, i) > limit) - return; - - // Update live range of the value from the optimization standpoint - lastNum = state.instPos; - - substitute(function, inst, IrOp{IrOpKind::Inst, prevIdx}); - return; // Break out from both the loop and the switch - } + substitute(function, inst, IrOp{IrOpKind::Inst, prevIdx}); + return; // Break out from both the loop and the switch } - - if (int(state.getSlotNodeCache.size()) < FInt::LuauCodeGenReuseSlotLimit) - state.getSlotNodeCache.push_back({index, state.instPos, state.instPos}); } - else - { - for (uint32_t prevIdx : state.getSlotNodeCache_DEPRECATED) - { - const IrInst& prev = function.instructions[prevIdx]; - if (prev.a == inst.a && prev.c == inst.c) - { - substitute(function, inst, IrOp{IrOpKind::Inst, prevIdx}); - return; // Break out from both the loop and the switch - } - } - - if (int(state.getSlotNodeCache_DEPRECATED.size()) < FInt::LuauCodeGenReuseSlotLimit) - state.getSlotNodeCache_DEPRECATED.push_back(index); - } + if (int(state.getSlotNodeCache.size()) < FInt::LuauCodeGenReuseSlotLimit) + state.getSlotNodeCache.push_back({index, state.instPos, state.instPos}); break; case IrCmd::GET_HASH_NODE_ADDR: case IrCmd::GET_CLOSURE_UPVAL_ADDR: diff --git a/Compiler/src/BuiltinFolding.cpp b/Compiler/src/BuiltinFolding.cpp index d6aeb3dd..28b812f5 100644 --- a/Compiler/src/BuiltinFolding.cpp +++ b/Compiler/src/BuiltinFolding.cpp @@ -5,9 +5,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauVector2Constants) -LUAU_FASTFLAG(LuauCompileMathLerp) - namespace Luau { namespace Compile @@ -476,7 +473,7 @@ Constant foldBuiltin(int bfid, const Constant* args, size_t count) case LBF_VECTOR: if (count >= 2 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number) { - if (count == 2 && FFlag::LuauVector2Constants) + if (count == 2) return cvector(args[0].valueNumber, args[1].valueNumber, 0.0, 0.0); else if (count == 3 && args[2].type == Constant::Type_Number) return cvector(args[0].valueNumber, args[1].valueNumber, args[2].valueNumber, 0.0); @@ -486,8 +483,7 @@ Constant foldBuiltin(int bfid, const Constant* args, size_t count) break; case LBF_MATH_LERP: - if (FFlag::LuauCompileMathLerp && count == 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number && - args[2].type == Constant::Type_Number) + if (count == 3 && args[0].type == Constant::Type_Number && args[1].type == Constant::Type_Number && args[2].type == Constant::Type_Number) { double a = args[0].valueNumber; double b = args[1].valueNumber; diff --git a/Compiler/src/Builtins.cpp b/Compiler/src/Builtins.cpp index 64d4d3f2..bc342bd3 100644 --- a/Compiler/src/Builtins.cpp +++ b/Compiler/src/Builtins.cpp @@ -7,8 +7,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauCompileMathLerp) - namespace Luau { namespace Compile @@ -139,7 +137,7 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op return LBF_MATH_SIGN; if (builtin.method == "round") return LBF_MATH_ROUND; - if (FFlag::LuauCompileMathLerp && builtin.method == "lerp") + if (builtin.method == "lerp") return LBF_MATH_LERP; } @@ -556,7 +554,6 @@ BuiltinInfo getBuiltinInfo(int bfid) return {-1, 1}; // variadic case LBF_MATH_LERP: - LUAU_ASSERT(FFlag::LuauCompileMathLerp); return {3, 1, BuiltinInfo::Flag_NoneSafe}; } diff --git a/VM/src/lbuflib.cpp b/VM/src/lbuflib.cpp index 17ca8b0b..ec14eb27 100644 --- a/VM/src/lbuflib.cpp +++ b/VM/src/lbuflib.cpp @@ -10,8 +10,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauBufferBitMethods2) - // while C API returns 'size_t' for binary compatibility in case of future extensions, // in the current implementation, length and offset are limited to 31 bits // because offset is limited to an integer, a single 64bit comparison can be used and will not overflow @@ -330,34 +328,6 @@ static int buffer_writebits(lua_State* L) return 0; } -static const luaL_Reg bufferlib_DEPRECATED[] = { - {"create", buffer_create}, - {"fromstring", buffer_fromstring}, - {"tostring", buffer_tostring}, - {"readi8", buffer_readinteger}, - {"readu8", buffer_readinteger}, - {"readi16", buffer_readinteger}, - {"readu16", buffer_readinteger}, - {"readi32", buffer_readinteger}, - {"readu32", buffer_readinteger}, - {"readf32", buffer_readfp}, - {"readf64", buffer_readfp}, - {"writei8", buffer_writeinteger}, - {"writeu8", buffer_writeinteger}, - {"writei16", buffer_writeinteger}, - {"writeu16", buffer_writeinteger}, - {"writei32", buffer_writeinteger}, - {"writeu32", buffer_writeinteger}, - {"writef32", buffer_writefp}, - {"writef64", buffer_writefp}, - {"readstring", buffer_readstring}, - {"writestring", buffer_writestring}, - {"len", buffer_len}, - {"copy", buffer_copy}, - {"fill", buffer_fill}, - {NULL, NULL}, -}; - static const luaL_Reg bufferlib[] = { {"create", buffer_create}, {"fromstring", buffer_fromstring}, @@ -390,7 +360,7 @@ static const luaL_Reg bufferlib[] = { int luaopen_buffer(lua_State* L) { - luaL_register(L, LUA_BUFFERLIBNAME, FFlag::LuauBufferBitMethods2 ? bufferlib : bufferlib_DEPRECATED); + luaL_register(L, LUA_BUFFERLIBNAME, bufferlib); return 1; } diff --git a/VM/src/lbuiltins.cpp b/VM/src/lbuiltins.cpp index 74702fe5..3fc687e1 100644 --- a/VM/src/lbuiltins.cpp +++ b/VM/src/lbuiltins.cpp @@ -25,8 +25,6 @@ #endif #endif -LUAU_FASTFLAG(LuauVector2Constructor) - // luauF functions implement FASTCALL instruction that performs a direct execution of some builtin functions from the VM // The rule of thumb is that FASTCALL functions can not call user code, yield, fail, or reallocate stack. // If types of the arguments mismatch, luauF_* needs to return -1 and the execution will fall back to the usual call path @@ -1057,60 +1055,33 @@ static int luauF_tunpack(lua_State* L, StkId res, TValue* arg0, int nresults, St static int luauF_vector(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams) { - if (FFlag::LuauVector2Constructor) + if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args)) { - if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args)) - { - float x = (float)nvalue(arg0); - float y = (float)nvalue(args); - float z = 0.0f; + float x = (float)nvalue(arg0); + float y = (float)nvalue(args); + float z = 0.0f; - if (nparams >= 3) - { - if (!ttisnumber(args + 1)) - return -1; - z = (float)nvalue(args + 1); - } + if (nparams >= 3) + { + if (!ttisnumber(args + 1)) + return -1; + z = (float)nvalue(args + 1); + } #if LUA_VECTOR_SIZE == 4 - float w = 0.0f; - if (nparams >= 4) - { - if (!ttisnumber(args + 2)) - return -1; - w = (float)nvalue(args + 2); - } - setvvalue(res, x, y, z, w); -#else - setvvalue(res, x, y, z, 0.0f); -#endif - - return 1; - } - } - else - { - if (nparams >= 3 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args) && ttisnumber(args + 1)) + float w = 0.0f; + if (nparams >= 4) { - double x = nvalue(arg0); - double y = nvalue(args); - double z = nvalue(args + 1); - -#if LUA_VECTOR_SIZE == 4 - double w = 0.0; - if (nparams >= 4) - { - if (!ttisnumber(args + 2)) - return -1; - w = nvalue(args + 2); - } - setvvalue(res, float(x), float(y), float(z), float(w)); + if (!ttisnumber(args + 2)) + return -1; + w = (float)nvalue(args + 2); + } + setvvalue(res, x, y, z, w); #else - setvvalue(res, float(x), float(y), float(z), 0.0f); + setvvalue(res, x, y, z, 0.0f); #endif - return 1; - } + return 1; } return -1; diff --git a/VM/src/lmathlib.cpp b/VM/src/lmathlib.cpp index 9bd21607..546725ca 100644 --- a/VM/src/lmathlib.cpp +++ b/VM/src/lmathlib.cpp @@ -7,8 +7,6 @@ #include #include -LUAU_FASTFLAGVARIABLE(LuauMathLerp) - #undef PI #define PI (3.14159265358979323846) #define RADIANS_PER_DEGREE (PI / 180.0) @@ -463,6 +461,7 @@ static const luaL_Reg mathlib[] = { {"sign", math_sign}, {"round", math_round}, {"map", math_map}, + {"lerp", math_lerp}, {NULL, NULL}, }; @@ -483,11 +482,5 @@ int luaopen_math(lua_State* L) lua_pushnumber(L, HUGE_VAL); lua_setfield(L, -2, "huge"); - if (FFlag::LuauMathLerp) - { - lua_pushcfunction(L, math_lerp, "lerp"); - lua_setfield(L, -2, "lerp"); - } - return 1; } diff --git a/VM/src/lveclib.cpp b/VM/src/lveclib.cpp index c08087bd..f24dc8b6 100644 --- a/VM/src/lveclib.cpp +++ b/VM/src/lveclib.cpp @@ -6,8 +6,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauVector2Constructor) - static int vector_create(lua_State* L) { // checking argument count to avoid accepting 'nil' as a valid value @@ -15,7 +13,7 @@ static int vector_create(lua_State* L) double x = luaL_checknumber(L, 1); double y = luaL_checknumber(L, 2); - double z = FFlag::LuauVector2Constructor ? (count >= 3 ? luaL_checknumber(L, 3) : 0.0) : luaL_checknumber(L, 3); + double z = count >= 3 ? luaL_checknumber(L, 3) : 0.0; #if LUA_VECTOR_SIZE == 4 double w = count >= 4 ? luaL_checknumber(L, 4) : 0.0; diff --git a/tests/AnyTypeSummary.test.cpp b/tests/AnyTypeSummary.test.cpp index a91ce5b3..471c4bb1 100644 --- a/tests/AnyTypeSummary.test.cpp +++ b/tests/AnyTypeSummary.test.cpp @@ -19,10 +19,10 @@ LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(StudioReportLuauAny2) LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope) -LUAU_FASTFLAG(LuauAlwaysFillInFunctionCallDiscriminantTypes) LUAU_FASTFLAG(LuauStoreCSTData) -LUAU_FASTFLAG(LuauAstTypeGroup) +LUAU_FASTFLAG(LuauAstTypeGroup2) LUAU_FASTFLAG(LuauDeferBidirectionalInferenceForTableAssignment) +LUAU_FASTFLAG(LuauSkipNoRefineDuringRefinement) struct ATSFixture : BuiltinsFixture @@ -76,7 +76,7 @@ export type t8 = t0 &((true | any)->('')) LUAU_ASSERT(module->ats.typeInfo.size() == 1); LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); - if (FFlag::LuauStoreCSTData && FFlag::LuauAstTypeGroup) + if (FFlag::LuauStoreCSTData && FFlag::LuauAstTypeGroup2) { LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8 = t0& (( true | any)->(''))"); } @@ -84,7 +84,7 @@ export type t8 = t0 &((true | any)->('')) { LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8 = t0 &(( true | any)->(''))"); } - else if (FFlag::LuauAstTypeGroup) + else if (FFlag::LuauAstTypeGroup2) { LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8 = t0& ((true | any)->(''))"); } @@ -429,7 +429,6 @@ TEST_CASE_FIXTURE(ATSFixture, "CannotExtendTable") ScopedFastFlag sff[] = { {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, - {FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes, true}, }; fileResolver.source["game/Gui/Modules/A"] = R"( @@ -449,7 +448,14 @@ end ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - LUAU_ASSERT(module->ats.typeInfo.size() == 0); + if (FFlag::LuauSkipNoRefineDuringRefinement) + { + REQUIRE_EQ(module->ats.typeInfo.size(), 1); + CHECK_EQ(module->ats.typeInfo[0].code, Pattern::Assign); + CHECK_EQ(module->ats.typeInfo[0].node, "descendant.CollisionGroup = CAR_COLLISION_GROUP"); + } + else + LUAU_ASSERT(module->ats.typeInfo.size() == 0); } TEST_CASE_FIXTURE(ATSFixture, "unknown_symbol") @@ -522,7 +528,6 @@ TEST_CASE_FIXTURE(ATSFixture, "racing_collision_2") ScopedFastFlag sff[] = { {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, - {FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes, true}, }; fileResolver.source["game/Gui/Modules/A"] = R"( @@ -590,7 +595,10 @@ initialize() ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - LUAU_ASSERT(module->ats.typeInfo.size() == 11); + if (FFlag::LuauSkipNoRefineDuringRefinement) + CHECK_EQ(module->ats.typeInfo.size(), 12); + else + LUAU_ASSERT(module->ats.typeInfo.size() == 11); LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); if (FFlag::LuauStoreCSTData) { diff --git a/tests/AstJsonEncoder.test.cpp b/tests/AstJsonEncoder.test.cpp index 75b7e7bd..7dff66d7 100644 --- a/tests/AstJsonEncoder.test.cpp +++ b/tests/AstJsonEncoder.test.cpp @@ -11,7 +11,7 @@ using namespace Luau; -LUAU_FASTFLAG(LuauAstTypeGroup) +LUAU_FASTFLAG(LuauAstTypeGroup2) struct JsonEncoderFixture { @@ -473,10 +473,10 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation") { AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())"); - if (FFlag::LuauAstTypeGroup) + if (FFlag::LuauAstTypeGroup2) { std::string_view expected = - R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeGroup","location":"0,9 - 0,36","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}},{"type":"AstTypeGroup","location":"0,40 - 0,55","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}}]},"exported":false})"; + R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}}]},"exported":false})"; CHECK(toJson(statement) == expected); } else diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index af04fb77..56ba1a4f 100644 --- a/tests/Compiler.test.cpp +++ b/tests/Compiler.test.cpp @@ -23,7 +23,6 @@ LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost) LUAU_FASTINT(LuauCompileLoopUnrollThreshold) LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost) LUAU_FASTINT(LuauRecursionLimit) -LUAU_FASTFLAG(LuauVector2Constants) using namespace Luau; @@ -5093,8 +5092,6 @@ L0: RETURN R3 -1 TEST_CASE("VectorConstants") { - ScopedFastFlag luauVector2Constants{FFlag::LuauVector2Constants, true}; - CHECK_EQ("\n" + compileFunction("return vector.create(1, 2)", 0, 2, 0), R"( LOADK R0 K0 [1, 2, 0] RETURN R0 1 diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index f5da6f15..073fcf35 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -32,15 +32,10 @@ void luaC_fullgc(lua_State* L); void luaC_validate(lua_State* L); LUAU_FASTFLAG(LuauLibWhereErrorAutoreserve) -LUAU_FASTFLAG(LuauMathLerp) LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_DYNAMIC_FASTFLAG(LuauStackLimit) -LUAU_FASTFLAG(LuauVectorLibNativeCodegen) LUAU_FASTFLAG(LuauVectorLibNativeDot) -LUAU_FASTFLAG(LuauVector2Constructor) -LUAU_FASTFLAG(LuauBufferBitMethods2) -LUAU_FASTFLAG(LuauCodeGenLimitLiveSlotReuse) LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC) static lua_CompileOptions defaultOptions() @@ -655,15 +650,11 @@ TEST_CASE("Basic") TEST_CASE("Buffers") { - ScopedFastFlag luauBufferBitMethods{FFlag::LuauBufferBitMethods2, true}; - runConformance("buffers.luau"); } TEST_CASE("Math") { - ScopedFastFlag LuauMathLerp{FFlag::LuauMathLerp, true}; - runConformance("math.luau"); } @@ -896,9 +887,7 @@ TEST_CASE("Vector") TEST_CASE("VectorLibrary") { - ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true}; ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true}; - ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true}; lua_CompileOptions copts = defaultOptions(); @@ -989,9 +978,6 @@ static void populateRTTI(lua_State* L, Luau::TypeId type) TEST_CASE("Types") { - ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true}; - ScopedFastFlag luauMathLerp{FFlag::LuauMathLerp, true}; - runConformance( "types.luau", [](lua_State* L) @@ -2622,8 +2608,6 @@ TEST_CASE("SafeEnv") TEST_CASE("Native") { - ScopedFastFlag luauCodeGenLimitLiveSlotReuse{FFlag::LuauCodeGenLimitLiveSlotReuse, true}; - // This tests requires code to run natively, otherwise all 'is_native' checks will fail if (!codegen || !luau_codegen_supported()) return; diff --git a/tests/ConstraintGeneratorFixture.cpp b/tests/ConstraintGeneratorFixture.cpp index 90a8b507..e10e60d4 100644 --- a/tests/ConstraintGeneratorFixture.cpp +++ b/tests/ConstraintGeneratorFixture.cpp @@ -53,6 +53,7 @@ void ConstraintGeneratorFixture::solve(const std::string& code) NotNull{&typeFunctionRuntime}, NotNull{rootScope}, constraints, + NotNull{&cg->scopeToFunction}, "MainModule", NotNull(&moduleResolver), {}, diff --git a/tests/Fixture.cpp b/tests/Fixture.cpp index 3a8d2cfc..dcb228a3 100644 --- a/tests/Fixture.cpp +++ b/tests/Fixture.cpp @@ -25,7 +25,6 @@ static const char* mainModuleName = "MainModule"; LUAU_FASTFLAG(LuauSolverV2); -LUAU_FASTFLAG(LuauVector2Constructor) LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile) LUAU_FASTFLAGVARIABLE(DebugLuauForceAllNewSolverTests); @@ -593,8 +592,6 @@ LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool BuiltinsFixture::BuiltinsFixture(bool prepareAutocomplete) : Fixture(prepareAutocomplete) { - ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true}; - Luau::unfreeze(frontend.globals.globalTypes); Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); diff --git a/tests/FragmentAutocomplete.test.cpp b/tests/FragmentAutocomplete.test.cpp index 8805b7e6..9f7a5261 100644 --- a/tests/FragmentAutocomplete.test.cpp +++ b/tests/FragmentAutocomplete.test.cpp @@ -10,6 +10,7 @@ #include "Luau/Frontend.h" #include "Luau/AutocompleteTypes.h" #include "Luau/Type.h" +#include "ScopedFlags.h" #include #include @@ -21,7 +22,6 @@ using namespace Luau; -LUAU_FASTFLAG(LuauAllowFragmentParsing); LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete) LUAU_FASTFLAG(LuauSymbolEquality); LUAU_FASTFLAG(LuauStoreSolverTypeOnModule); @@ -35,6 +35,7 @@ LUAU_FASTFLAG(LuauMixedModeDefFinderTraversesTypeOf) LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds) LUAU_FASTFLAG(LuauBetterReverseDependencyTracking) +LUAU_FASTFLAG(LuauAutocompleteUsesModuleForTypeCompatibility) static std::optional nullCallback(std::string tag, std::optional ptr, std::optional contents) { @@ -64,8 +65,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType { static_assert(std::is_base_of_v, "BaseType must be a descendant of Fixture"); - ScopedFastFlag sffs[7] = { - {FFlag::LuauAllowFragmentParsing, true}, + ScopedFastFlag sffs[6] = { {FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true}, {FFlag::LuauStoreSolverTypeOnModule, true}, {FFlag::LuauSymbolEquality, true}, @@ -859,17 +859,16 @@ return module)"; { ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + ScopedFastFlag sff2{FFlag::LuauCloneIncrementalModule, true}; + ScopedFastFlag sff3{FFlag::LuauFreeTypesMustHaveBounds, true}; checkAndExamine(source, "module", "{ }"); - // [TODO] CLI-140762 we shouldn't mutate stale module in autocompleteFragment - // early return since the following checking will fail, which it shouldn't! - // fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }"); - // fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }"); + fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }"); + fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }"); } { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; checkAndExamine(source, "module", "{ }"); - // [TODO] CLI-140762 we shouldn't mutate stale module in autocompleteFragment - // early return since the following checking will fail, which it shouldn't! + // [TODO] CLI-140762 Fragment autocomplete still doesn't return correct result when LuauSolverV2 is on return; fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }"); fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }"); @@ -2092,5 +2091,15 @@ return module autocompleteFragmentInBothSolvers(source, updated, Position{1, 18}, [](FragmentAutocompleteResult& result) {}); } +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "ice_caused_by_mixed_mode_use") +{ + ScopedFastFlag sff{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true}; + const std::string source = "--[[\n\tPackage link auto-generated by Rotriever\n]]\nlocal PackageIndex = script.Parent._Index\n\nlocal Package = " + "require(PackageIndex[\"ReactOtter\"][\"ReactOtter\"])\n\nexport type Goal = Package.Goal\nexport type SpringOptions " + "= Package.SpringOptions\n\n\nreturn Pa"; + autocompleteFragmentInBothSolvers(source, source, Position{11,9}, [](auto& _){ + + }); +} TEST_SUITE_END(); diff --git a/tests/NonStrictTypeChecker.test.cpp b/tests/NonStrictTypeChecker.test.cpp index c21b64a3..61ebecf3 100644 --- a/tests/NonStrictTypeChecker.test.cpp +++ b/tests/NonStrictTypeChecker.test.cpp @@ -15,8 +15,6 @@ #include "doctest.h" #include -LUAU_FASTFLAG(LuauCountSelfCallsNonstrict) -LUAU_FASTFLAG(LuauVector2Constructor) LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals) LUAU_FASTFLAG(LuauNonStrictVisitorImprovements) @@ -619,9 +617,6 @@ buffer.readi8(b, 0) TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls") { - ScopedFastFlag luauCountSelfCallsNonstrict{FFlag::LuauCountSelfCallsNonstrict, true}; - ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true}; - Luau::unfreeze(frontend.globals.globalTypes); Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index c3478815..de986cd4 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -22,7 +22,7 @@ LUAU_FASTFLAG(LuauErrorRecoveryForClassNames) LUAU_FASTFLAG(LuauFixFunctionNameStartPosition) LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon) LUAU_FASTFLAG(LuauPreserveUnionIntersectionNodeForLeadingTokenSingleType) -LUAU_FASTFLAG(LuauAstTypeGroup) +LUAU_FASTFLAG(LuauAstTypeGroup2) LUAU_FASTFLAG(LuauFixDoBlockEndLocation) namespace @@ -372,7 +372,7 @@ TEST_CASE_FIXTURE(Fixture, "return_type_is_an_intersection_type_if_led_with_one_ AstTypeIntersection* returnAnnotation = annotation->returnTypes.types.data[0]->as(); REQUIRE(returnAnnotation != nullptr); - if (FFlag::LuauAstTypeGroup) + if (FFlag::LuauAstTypeGroup2) CHECK(returnAnnotation->types.data[0]->as()); else CHECK(returnAnnotation->types.data[0]->as()); @@ -2451,7 +2451,7 @@ TEST_CASE_FIXTURE(Fixture, "leading_union_intersection_with_single_type_preserve TEST_CASE_FIXTURE(Fixture, "parse_simple_ast_type_group") { - ScopedFastFlag _{FFlag::LuauAstTypeGroup, true}; + ScopedFastFlag _{FFlag::LuauAstTypeGroup2, true}; AstStatBlock* stat = parse(R"( type Foo = (string) @@ -2469,7 +2469,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_simple_ast_type_group") TEST_CASE_FIXTURE(Fixture, "parse_nested_ast_type_group") { - ScopedFastFlag _{FFlag::LuauAstTypeGroup, true}; + ScopedFastFlag _{FFlag::LuauAstTypeGroup2, true}; AstStatBlock* stat = parse(R"( type Foo = ((string)) @@ -2490,7 +2490,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_nested_ast_type_group") TEST_CASE_FIXTURE(Fixture, "parse_return_type_ast_type_group") { - ScopedFastFlag _{FFlag::LuauAstTypeGroup, true}; + ScopedFastFlag _{FFlag::LuauAstTypeGroup2, true}; AstStatBlock* stat = parse(R"( type Foo = () -> (string) @@ -3813,7 +3813,7 @@ TEST_CASE_FIXTURE(Fixture, "grouped_function_type") auto unionTy = paramTy.type->as(); LUAU_ASSERT(unionTy); CHECK_EQ(unionTy->types.size, 2); - if (FFlag::LuauAstTypeGroup) + if (FFlag::LuauAstTypeGroup2) { auto groupTy = unionTy->types.data[0]->as(); // (() -> ()) REQUIRE(groupTy); diff --git a/tests/Transpiler.test.cpp b/tests/Transpiler.test.cpp index caf1ba1c..f179876c 100644 --- a/tests/Transpiler.test.cpp +++ b/tests/Transpiler.test.cpp @@ -14,7 +14,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauStoreCSTData) LUAU_FASTFLAG(LuauExtendStatEndPosWithSemicolon) -LUAU_FASTFLAG(LuauAstTypeGroup); +LUAU_FASTFLAG(LuauAstTypeGroup2); LUAU_FASTFLAG(LexerFixInterpStringStart) TEST_SUITE_BEGIN("TranspilerTests"); @@ -1175,7 +1175,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3") { std::string code = "local a: nil | (string & number)"; - if (FFlag::LuauAstTypeGroup) + if (FFlag::LuauAstTypeGroup2) CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code); else CHECK_EQ("local a: ( string & number)?", transpile(code, {}, true).code); @@ -1732,4 +1732,24 @@ TEST_CASE("transpile_type_table_preserve_property_definition_style") CHECK_EQ(code, transpile(code, {}, true).code); } +TEST_CASE("transpile_types_preserve_parentheses_style") +{ + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData, true}, + {FFlag::LuauAstTypeGroup2, true}, + }; + + std::string code = R"( type Foo = number )"; + CHECK_EQ(code, transpile(code, {}, true).code); + + code = R"( type Foo = (number) )"; + CHECK_EQ(code, transpile(code, {}, true).code); + + code = R"( type Foo = ((number)) )"; + CHECK_EQ(code, transpile(code, {}, true).code); + + code = R"( type Foo = ( (number) ) )"; + CHECK_EQ(code, transpile(code, {}, true).code); +} + TEST_SUITE_END(); diff --git a/tests/TxnLog.test.cpp b/tests/TxnLog.test.cpp index b4b18353..29f7c0f3 100644 --- a/tests/TxnLog.test.cpp +++ b/tests/TxnLog.test.cpp @@ -16,8 +16,8 @@ LUAU_FASTFLAG(LuauSolverV2) struct TxnLogFixture { - TxnLog log{/*useScopes*/ true}; - TxnLog log2{/*useScopes*/ true}; + TxnLog log; + TxnLog log2; TypeArena arena; BuiltinTypes builtinTypes; @@ -33,39 +33,6 @@ struct TxnLogFixture TEST_SUITE_BEGIN("TxnLog"); -TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_greater_scope") -{ - ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - - log.replace(c, BoundType{a}); - log2.replace(a, BoundType{c}); - - CHECK(nullptr != log.pending(c)); - - log.concatAsUnion(std::move(log2), NotNull{&arena}); - - // 'a has greater scope than 'c, so we expect the incoming binding of 'a to - // be discarded. - - CHECK(nullptr == log.pending(a)); - - const PendingType* pt = log.pending(c); - REQUIRE(pt != nullptr); - - CHECK(!pt->dead); - const BoundType* bt = get_if(&pt->pending.ty); - - CHECK(a == bt->boundTo); - - log.commit(); - - REQUIRE(get(a)); - - const BoundType* bound = get(c); - REQUIRE(bound); - CHECK(a == bound->boundTo); -} - TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_lesser_scope") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index f54330e3..12c9ece2 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -12,6 +12,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauTypeFunSingletonEquality) LUAU_FASTFLAG(LuauUserTypeFunTypeofReturnsType) +LUAU_FASTFLAG(LuauTypeFunReadWriteParents) LUAU_FASTFLAG(LuauTypeFunPrintFix) TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests"); @@ -1960,4 +1961,25 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_print_tab_char_fix") CHECK_EQ("1\t2", toString(result.errors[0])); } +TEST_CASE_FIXTURE(ClassFixture, "udtf_class_parent_ops") +{ + ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; + ScopedFastFlag readWriteParents{FFlag::LuauTypeFunReadWriteParents, true}; + + CheckResult result = check(R"( + type function readparentof(arg) + return arg:readparent() + end + + type function writeparentof(arg) + return arg:writeparent() + end + + local function ok1(idx: readparentof): BaseClass return idx end + local function ok2(idx: writeparentof): BaseClass return idx end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.definitions.test.cpp b/tests/TypeInfer.definitions.test.cpp index 84cfb97b..90593891 100644 --- a/tests/TypeInfer.definitions.test.cpp +++ b/tests/TypeInfer.definitions.test.cpp @@ -9,7 +9,6 @@ using namespace Luau; -LUAU_FASTFLAG(LuauNewSolverPrePopulateClasses) LUAU_FASTFLAG(LuauClipNestedAndRecursiveUnion) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauPreventReentrantTypeFunctionReduction) @@ -497,7 +496,6 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_indexer") TEST_CASE_FIXTURE(Fixture, "class_definitions_reference_other_classes") { - ScopedFastFlag _{FFlag::LuauNewSolverPrePopulateClasses, true}; loadDefinition(R"( declare class Channel Messages: { Message } diff --git a/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index b8d80743..f13524fb 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/tests/TypeInfer.functions.test.cpp @@ -16,11 +16,14 @@ using namespace Luau; +LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) + LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauSubtypingFixTailPack) +LUAU_FASTFLAG(LuauUngeneralizedTypesForRecursiveFunctions) TEST_SUITE_BEGIN("TypeInferFunctions"); @@ -3041,4 +3044,48 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "coroutine_wrap_result_call") // New solver still reports an error in this case, but the main goal of the test is to not crash } +TEST_CASE_FIXTURE(Fixture, "recursive_function_calls_should_not_use_the_generalized_type") +{ + ScopedFastFlag crashOnForce{FFlag::DebugLuauAssertOnForcedConstraint, true}; + ScopedFastFlag sff{FFlag::LuauUngeneralizedTypesForRecursiveFunctions, true}; + + CheckResult result = check(R"( + --!strict + + function random() + return true -- chosen by fair coin toss + end + + local f + f = 5 + function f() + if random() then f() end + end + )"); + + if (FFlag::LuauSolverV2) + LUAU_REQUIRE_NO_ERRORS(result); + else + LUAU_REQUIRE_ERRORS(result); // errors without typestate, obviously +} + +TEST_CASE_FIXTURE(Fixture, "recursive_function_calls_should_not_use_the_generalized_type_2") +{ + ScopedFastFlag crashOnForce{FFlag::DebugLuauAssertOnForcedConstraint, true}; + + CheckResult result = check(R"( + --!strict + + function random() + return true -- chosen by fair coin toss + end + + local function f() + if random() then f() end + end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.modules.test.cpp b/tests/TypeInfer.modules.test.cpp index e5fdbdd3..ce4490fa 100644 --- a/tests/TypeInfer.modules.test.cpp +++ b/tests/TypeInfer.modules.test.cpp @@ -12,7 +12,6 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations) using namespace Luau; @@ -464,13 +463,10 @@ local b: B.T = a if (FFlag::LuauSolverV2) { - if (FFlag::LuauNewSolverPopulateTableLocations) - CHECK( - toString(result.errors.at(0)) == - "Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; at [read \"x\"], number is not exactly string" - ); - else - CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string"); + CHECK( + toString(result.errors.at(0)) == + "Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; at [read \"x\"], number is not exactly string" + ); } else { @@ -513,13 +509,10 @@ local b: B.T = a if (FFlag::LuauSolverV2) { - if (FFlag::LuauNewSolverPopulateTableLocations) - CHECK( - toString(result.errors.at(0)) == - "Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; at [read \"x\"], number is not exactly string" - ); - else - CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string"); + CHECK( + toString(result.errors.at(0)) == + "Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; at [read \"x\"], number is not exactly string" + ); } else { diff --git a/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index ee7d713d..5cafedbf 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/tests/TypeInfer.provisional.test.cpp @@ -557,7 +557,7 @@ TEST_CASE_FIXTURE(Fixture, "dcr_can_partially_dispatch_a_constraint") TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; TypeArena arena; TypeId nilType = builtinTypes->nilType; @@ -575,9 +575,6 @@ TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together") Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant}; - if (FFlag::LuauSolverV2) - u.enableNewSolver(); - u.tryUnify(option1, option2); CHECK(!u.failure); @@ -987,7 +984,7 @@ TEST_CASE_FIXTURE(Fixture, "floating_generics_should_not_be_allowed") TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; TypeArena arena; TypeId nilType = builtinTypes->nilType; @@ -1005,9 +1002,6 @@ TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together") Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant}; - if (FFlag::LuauSolverV2) - u.enableNewSolver(); - u.tryUnify(option1, option2); CHECK(!u.failure); diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 27aeb625..fa62fbea 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -10,6 +10,8 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauGeneralizationRemoveRecursiveUpperBound2) +LUAU_FASTFLAG(LuauIntersectNotNil) +LUAU_FASTFLAG(LuauSkipNoRefineDuringRefinement) using namespace Luau; @@ -2459,4 +2461,69 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi CHECK_EQ("(nil & string)?", toString(requireTypeAtPosition({4, 24}))); } +TEST_CASE_FIXTURE(BuiltinsFixture, "nonnil_refinement_on_generic") +{ + ScopedFastFlag sff{FFlag::LuauIntersectNotNil, true}; + + CheckResult result = check(R"( + local function printOptional(item: T?, printer: (T) -> string): string + if item ~= nil then + return printer(item) + else + return "" + end + end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + if (FFlag::LuauSolverV2) + CHECK_EQ("T & ~nil", toString(requireTypeAtPosition({3, 31}))); + else + CHECK_EQ("T", toString(requireTypeAtPosition({3, 31}))); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "truthy_refinement_on_generic") +{ + ScopedFastFlag sff{FFlag::LuauIntersectNotNil, true}; + + CheckResult result = check(R"( + local function printOptional(item: T?, printer: (T) -> string): string + if item then + return printer(item) + else + return "" + end + end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + if (FFlag::LuauSolverV2) + CHECK_EQ("T & ~(false?)", toString(requireTypeAtPosition({3, 31}))); + else + CHECK_EQ("T", toString(requireTypeAtPosition({3, 31}))); +} + +TEST_CASE_FIXTURE(Fixture, "truthy_call_of_function_with_table_value_as_argument_should_not_refine_value_as_never") +{ + ScopedFastFlag sff{FFlag::LuauSkipNoRefineDuringRefinement, true}; + + CheckResult result = check(R"( + type Item = {} + + local function predicate(value: Item): boolean + return true + end + + local function checkValue(value: Item) + if predicate(value) then + local _ = value + end + end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + CHECK_EQ("Item", toString(requireTypeAtPosition({8, 27}))); + CHECK_EQ("Item", toString(requireTypeAtPosition({9, 28}))); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index 3b0380d7..4b7ce57c 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -18,8 +18,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering) -LUAU_FASTFLAG(LuauTableKeysAreRValues) -LUAU_FASTFLAG(LuauAllowNilAssignmentToIndexer) LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope) LUAU_FASTFLAG(LuauTrackInteriorFreeTablesOnScope) LUAU_FASTFLAG(LuauDontInPlaceMutateTableType) @@ -1932,7 +1930,7 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_on_massive_table_is_cut_short") TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr") { - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}}; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; LUAU_REQUIRE_NO_ERRORS(check(R"( local function f(): { [string]: number } @@ -1991,9 +1989,6 @@ TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr") TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_on_generic_map") { - - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}}; - LUAU_REQUIRE_NO_ERRORS(check(R"( type MyMap = { [K]: V } function set(m: MyMap, k: K, v: V) @@ -2010,8 +2005,7 @@ TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_on_generic_map") TEST_CASE_FIXTURE(Fixture, "key_setting_inference_given_nil_upper_bound") { - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}}; - + ScopedFastFlag _{FFlag::LuauSolverV2, true}; LUAU_REQUIRE_NO_ERRORS(check(R"( local function setkey_object(t: { [string]: number }, v) t.foo = v @@ -2942,16 +2936,12 @@ TEST_CASE_FIXTURE(Fixture, "table_length") TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_indexer") { - // CLI-100076 - Assigning a table key to `nil` in the presence of an indexer should always be permitted - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - - CheckResult result = check("local a = {} a[0] = 7 a[0] = nil"); - LUAU_REQUIRE_ERROR_COUNT(0, result); + LUAU_REQUIRE_NO_ERRORS(check("local a = {} a[0] = 7 a[0] = nil")); } TEST_CASE_FIXTURE(Fixture, "wrong_assign_does_hit_indexer") { - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}}; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local a = {} @@ -5055,7 +5045,6 @@ TEST_CASE_FIXTURE(Fixture, "function_check_constraint_too_eager") TEST_CASE_FIXTURE(BuiltinsFixture, "read_only_property_reads") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag sff{FFlag::LuauTableKeysAreRValues, true}; // none of the `t.id` accesses here should error auto result = check(R"( diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index 9bb22798..217391b8 100644 --- a/tests/TypeInfer.test.cpp +++ b/tests/TypeInfer.test.cpp @@ -23,7 +23,7 @@ LUAU_FASTINT(LuauCheckRecursionLimit) LUAU_FASTINT(LuauNormalizeCacheLimit) LUAU_FASTINT(LuauRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit) -LUAU_FASTFLAG(LuauAstTypeGroup) +LUAU_FASTFLAG(LuauAstTypeGroup2) LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals) LUAU_FASTFLAG(LuauInferLocalTypesInMultipleAssignments) @@ -1201,10 +1201,13 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer") if (FFlag::LuauSolverV2) { CHECK(3 == result.errors.size()); - CHECK(Location{{2, 22}, {2, 41}} == result.errors[0].location); + if (FFlag::LuauAstTypeGroup2) + CHECK(Location{{2, 22}, {2, 42}} == result.errors[0].location); + else + CHECK(Location{{2, 22}, {2, 41}} == result.errors[0].location); CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location); - if (FFlag::LuauAstTypeGroup) - CHECK(Location{{3, 22}, {3, 40}} == result.errors[2].location); + if (FFlag::LuauAstTypeGroup2) + CHECK(Location{{3, 22}, {3, 41}} == result.errors[2].location); else CHECK(Location{{3, 23}, {3, 40}} == result.errors[2].location); CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0])); diff --git a/tests/TypeInfer.tryUnify.test.cpp b/tests/TypeInfer.tryUnify.test.cpp index 48f5d3ea..66bf034e 100644 --- a/tests/TypeInfer.tryUnify.test.cpp +++ b/tests/TypeInfer.tryUnify.test.cpp @@ -341,43 +341,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "txnlog_preserves_pack_owner") CHECK_EQ(a->owningArena, &arena); } -TEST_CASE_FIXTURE(TryUnifyFixture, "metatables_unify_against_shape_of_free_table") -{ - TableType::Props freeProps{ - {"foo", {builtinTypes->numberType}}, - }; - - TypeId free = arena.addType(TableType{freeProps, std::nullopt, TypeLevel{}, TableState::Free}); - - TableType::Props indexProps{ - {"foo", {builtinTypes->stringType}}, - }; - - TypeId index = arena.addType(TableType{indexProps, std::nullopt, TypeLevel{}, TableState::Sealed}); - - TableType::Props mtProps{ - {"__index", {index}}, - }; - - TypeId mt = arena.addType(TableType{mtProps, std::nullopt, TypeLevel{}, TableState::Sealed}); - - TypeId target = arena.addType(TableType{TableState::Unsealed, TypeLevel{}}); - TypeId metatable = arena.addType(MetatableType{target, mt}); - - state.enableNewSolver(); - state.tryUnify(metatable, free); - state.log.commit(); - - REQUIRE_EQ(state.errors.size(), 1); - const std::string expected = R"(Type - '{ @metatable {| __index: {| foo: string |} |}, { } }' -could not be converted into - '{- foo: number -}' -caused by: - Type 'number' could not be converted into 'string')"; - CHECK_EQ(expected, toString(state.errors[0])); -} - TEST_CASE_FIXTURE(TryUnifyFixture, "fuzz_tail_unification_issue") { TypePackVar variadicAny{VariadicTypePack{builtinTypes->anyType}}; @@ -403,101 +366,6 @@ local l0:(any)&(typeof(_)),l0:(any)|(any) = _,_ LUAU_REQUIRE_ERRORS(result); } -static TypeId createTheType(TypeArena& arena, NotNull builtinTypes, Scope* scope, TypeId freeTy) -{ - /* - ({| - render: ( - (('a) -> ()) | {| current: 'a |} - ) -> nil - |}) -> () - */ - TypePackId emptyPack = arena.addTypePack({}); - - return arena.addType(FunctionType{ - arena.addTypePack({arena.addType(TableType{ - TableType::Props{ - {{"render", - Property(arena.addType(FunctionType{ - arena.addTypePack({arena.addType(UnionType{ - {arena.addType(FunctionType{arena.addTypePack({freeTy}), emptyPack}), - arena.addType(TableType{TableType::Props{{"current", {freeTy}}}, std::nullopt, TypeLevel{}, scope, TableState::Sealed})} - })}), - arena.addTypePack({builtinTypes->nilType}) - }))}} - }, - std::nullopt, - TypeLevel{}, - scope, - TableState::Sealed - })}), - emptyPack - }); -}; - -// See CLI-71190 -TEST_CASE_FIXTURE(TryUnifyFixture, "unifying_two_unions_under_dcr_does_not_create_a_BoundType_cycle") -{ - const std::shared_ptr scope = globalScope; - const std::shared_ptr nestedScope = std::make_shared(scope); - - const TypeId outerType = arena.freshType(builtinTypes, scope.get()); - const TypeId outerType2 = arena.freshType(builtinTypes, scope.get()); - - const TypeId innerType = arena.freshType(builtinTypes, nestedScope.get()); - - state.enableNewSolver(); - - SUBCASE("equal_scopes") - { - TypeId one = createTheType(arena, builtinTypes, scope.get(), outerType); - TypeId two = createTheType(arena, builtinTypes, scope.get(), outerType2); - - state.tryUnify(one, two); - state.log.commit(); - - ToStringOptions opts; - - CHECK(follow(outerType) == follow(outerType2)); - } - - SUBCASE("outer_scope_is_subtype") - { - TypeId one = createTheType(arena, builtinTypes, scope.get(), outerType); - TypeId two = createTheType(arena, builtinTypes, scope.get(), innerType); - - state.tryUnify(one, two); - state.log.commit(); - - ToStringOptions opts; - - CHECK(follow(outerType) == follow(innerType)); - - // The scope of outerType exceeds that of innerType. The latter should be bound to the former. - const BoundType* bt = get_if(&innerType->ty); - REQUIRE(bt); - CHECK(bt->boundTo == outerType); - } - - SUBCASE("outer_scope_is_supertype") - { - TypeId one = createTheType(arena, builtinTypes, scope.get(), innerType); - TypeId two = createTheType(arena, builtinTypes, scope.get(), outerType); - - state.tryUnify(one, two); - state.log.commit(); - - ToStringOptions opts; - - CHECK(follow(outerType) == follow(innerType)); - - // The scope of outerType exceeds that of innerType. The latter should be bound to the former. - const BoundType* bt = get_if(&innerType->ty); - REQUIRE(bt); - CHECK(bt->boundTo == outerType); - } -} - TEST_CASE_FIXTURE(BuiltinsFixture, "table_unification_full_restart_recursion") { ScopedFastFlag luauUnifierRecursionOnRestart{FFlag::LuauUnifierRecursionOnRestart, true};