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};