diff --git a/Analysis/include/Luau/ConstraintGenerator.h b/Analysis/include/Luau/ConstraintGenerator.h index 10b962f6..e98adf7c 100644 --- a/Analysis/include/Luau/ConstraintGenerator.h +++ b/Analysis/include/Luau/ConstraintGenerator.h @@ -175,6 +175,10 @@ private: std::vector unionsToSimplify; + // Used to keep track of when we are inside a large table and should + // opt *not* to do type inference for singletons. + size_t largeTableDepth = 0; + /** * Fabricates a new free type belonging to a given scope. * @param scope the scope the free type belongs to. diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h index 0804f296..91550579 100644 --- a/Analysis/include/Luau/ConstraintSolver.h +++ b/Analysis/include/Luau/ConstraintSolver.h @@ -225,7 +225,7 @@ public: bool tryDispatch(const IterableConstraint& c, NotNull constraint, bool force); bool tryDispatch(const NameConstraint& c, NotNull constraint); bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull constraint); - bool tryDispatch(const FunctionCallConstraint& c, NotNull constraint); + bool tryDispatch(const FunctionCallConstraint& c, NotNull constraint, bool force); bool tryDispatch(const TableCheckConstraint& c, NotNull constraint); bool tryDispatch(const FunctionCheckConstraint& c, NotNull constraint); bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull constraint); diff --git a/Analysis/include/Luau/Frontend.h b/Analysis/include/Luau/Frontend.h index 54c027d5..377cbc1b 100644 --- a/Analysis/include/Luau/Frontend.h +++ b/Analysis/include/Luau/Frontend.h @@ -166,6 +166,7 @@ struct Frontend // Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking void parse(const ModuleName& name); + void parseModules(const std::vector& name); // Parse and typecheck module graph CheckResult check(const ModuleName& name, std::optional optionOverride = {}); // new shininess diff --git a/Analysis/include/Luau/OverloadResolution.h b/Analysis/include/Luau/OverloadResolution.h index d85d769e..7337a868 100644 --- a/Analysis/include/Luau/OverloadResolution.h +++ b/Analysis/include/Luau/OverloadResolution.h @@ -63,7 +63,7 @@ struct OverloadResolver InsertionOrderedMap> resolution; - std::pair selectOverload(TypeId ty, TypePackId args); + std::pair selectOverload(TypeId ty, TypePackId args, bool useFreeTypeBounds); void resolve(TypeId fnTy, const TypePack* args, AstExpr* selfExpr, const std::vector* argExprs); private: diff --git a/Analysis/include/Luau/Subtyping.h b/Analysis/include/Luau/Subtyping.h index fbb0452e..f65a516e 100644 --- a/Analysis/include/Luau/Subtyping.h +++ b/Analysis/include/Luau/Subtyping.h @@ -72,6 +72,11 @@ struct SubtypingResult /// isSubtype is false, depending on the input types. SubtypingReasonings reasoning{kEmptyReasoning}; + // If this subtype result required testing free types, we might be making + // assumptions about what the free type eventually resolves to. If so, + // those assumptions are recorded here. + std::vector assumedConstraints; + SubtypingResult& andAlso(const SubtypingResult& other); SubtypingResult& orElse(const SubtypingResult& other); SubtypingResult& withBothComponent(TypePath::Component component); diff --git a/Analysis/src/AutocompleteCore.cpp b/Analysis/src/AutocompleteCore.cpp index 2c678123..c6ff4b09 100644 --- a/Analysis/src/AutocompleteCore.cpp +++ b/Analysis/src/AutocompleteCore.cpp @@ -24,7 +24,6 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTINT(LuauTypeInferIterationLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames) -LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility) LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) @@ -163,78 +162,39 @@ static bool checkTypeMatch( UnifierSharedState unifierState(&iceReporter); SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes); Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}}; - if (FFlag::LuauAutocompleteUsesModuleForTypeCompatibility) + if (module.checkedInNewSolver) { - if (module.checkedInNewSolver) - { - TypeCheckLimits limits; - TypeFunctionRuntime typeFunctionRuntime{ - NotNull{&iceReporter}, NotNull{&limits} - }; // TODO: maybe subtyping checks should not invoke user-defined type function runtime + 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; - } - 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(); - } + return subtyping.isSubtype(subTy, superTy, scope).isSubtype; } else { - if (FFlag::LuauSolverV2) - { - TypeCheckLimits limits; - TypeFunctionRuntime typeFunctionRuntime{ - NotNull{&iceReporter}, NotNull{&limits} - }; // TODO: maybe subtyping checks should not invoke user-defined type function runtime + Unifier unifier(NotNull{&normalizer}, scope, Location(), Variance::Covariant); - unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; - unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit; + // Cost of normalization can be too high for autocomplete response time requirements + unifier.normalize = false; + unifier.checkInhabited = false; - Subtyping subtyping{ - builtinTypes, - NotNull{typeArena}, - NotNull{simplifier.get()}, - NotNull{&normalizer}, - NotNull{&typeFunctionRuntime}, - NotNull{&iceReporter} - }; + unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; + unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit; - 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(); - } + return unifier.canUnify(subTy, superTy).empty(); } } diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index e6a02677..f3b2b329 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -30,7 +30,7 @@ */ LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3) LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2) LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition) @@ -310,8 +310,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC TypeArena& arena = globals.globalTypes; NotNull builtinTypes = globals.builtinTypes; - Scope* globalScope = nullptr; // NotNull when removing FFlag::LuauNonReentrantGeneralization3 - if (FFlag::LuauNonReentrantGeneralization3) + Scope* globalScope = nullptr; // NotNull when removing FFlag::LuauEagerGeneralization + if (FFlag::LuauEagerGeneralization) globalScope = globals.globalScope.get(); if (FFlag::LuauSolverV2) diff --git a/Analysis/src/Clone.cpp b/Analysis/src/Clone.cpp index 1257a4b2..4a1f6a4d 100644 --- a/Analysis/src/Clone.cpp +++ b/Analysis/src/Clone.cpp @@ -12,8 +12,6 @@ LUAU_FASTFLAG(LuauSolverV2) // For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit. LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000) -LUAU_FASTFLAGVARIABLE(LuauClonedTableAndFunctionTypesMustHaveScopes) -LUAU_FASTFLAGVARIABLE(LuauDoNotClonePersistentBindings) namespace Luau { @@ -514,10 +512,7 @@ public: free->scope = replacementForNullScope; } else if (auto tt = getMutable(target)) - { - if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes) - tt->scope = replacementForNullScope; - } + tt->scope = replacementForNullScope; (*types)[ty] = target; queue.emplace_back(target); @@ -733,7 +728,7 @@ Binding cloneIncremental(const Binding& binding, TypeArena& dest, CloneState& cl b.deprecatedSuggestion = binding.deprecatedSuggestion; b.documentationSymbol = binding.documentationSymbol; b.location = binding.location; - b.typeId = FFlag::LuauDoNotClonePersistentBindings && binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId); + b.typeId = binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId); return b; } diff --git a/Analysis/src/Constraint.cpp b/Analysis/src/Constraint.cpp index b84e476b..ff19fef9 100644 --- a/Analysis/src/Constraint.cpp +++ b/Analysis/src/Constraint.cpp @@ -3,7 +3,7 @@ #include "Luau/Constraint.h" #include "Luau/VisitType.h" -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) namespace Luau { @@ -17,8 +17,8 @@ Constraint::Constraint(NotNull scope, const Location& location, Constrain struct ReferenceCountInitializer : TypeOnceVisitor { - DenseHashSet* result; + bool traverseIntoTypeFunctions = true; explicit ReferenceCountInitializer(DenseHashSet* result) : result(result) @@ -51,7 +51,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor bool visit(TypeId, const TypeFunctionInstanceType&) override { - return FFlag::DebugLuauGreedyGeneralization; + return FFlag::LuauEagerGeneralization && traverseIntoTypeFunctions; } }; @@ -104,10 +104,12 @@ DenseHashSet Constraint::getMaybeMutatedFreeTypes() const { rci.traverse(fchc->argsPack); } - else if (auto fcc = get(*this); fcc && FFlag::DebugLuauGreedyGeneralization) + else if (auto fcc = get(*this); fcc && FFlag::LuauEagerGeneralization) { + rci.traverseIntoTypeFunctions = false; rci.traverse(fcc->fn); rci.traverse(fcc->argsPack); + rci.traverseIntoTypeFunctions = true; } else if (auto ptc = get(*this)) { @@ -116,12 +118,12 @@ DenseHashSet Constraint::getMaybeMutatedFreeTypes() const else if (auto hpc = get(*this)) { rci.traverse(hpc->resultType); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) rci.traverse(hpc->subjectType); } else if (auto hic = get(*this)) { - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) rci.traverse(hic->subjectType); rci.traverse(hic->resultType); // `HasIndexerConstraint` should not mutate `indexType`. diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 6d3ec1c1..228bc6c0 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -33,8 +33,8 @@ LUAU_FASTINT(LuauCheckRecursionLimit) LUAU_FASTFLAG(DebugLuauLogSolverToJson) LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations) @@ -42,12 +42,15 @@ LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation) LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions) +LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation) LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) +LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables) +LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500) namespace Luau { @@ -251,7 +254,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) rootScope->location = block->location; module->astScopes[block] = NotNull{scope.get()}; - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.emplace_back(); @@ -287,7 +290,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) } ); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -306,7 +309,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) } ); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -344,13 +347,13 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat // We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block); // Pre - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.emplace_back(); visitBlockWithoutChildScope(resumeScope, block); // Post - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -380,12 +383,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity) { - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity); interiorFreeTypes.back().types.push_back(ft); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) freeTypes.insert(ft); return ft; @@ -402,7 +405,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po { FreeTypePack f{scope.get(), polarity}; TypePackId result = arena->addTypePack(TypePackVar{std::move(f)}); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.back().typePacks.push_back(result); return result; } @@ -1393,7 +1396,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location); sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->name->location}; - bool sigFullyDefined = FFlag::DebugLuauGreedyGeneralization ? false : !hasFreeType(sig.signature); + bool sigFullyDefined = FFlag::LuauEagerGeneralization ? false : !hasFreeType(sig.signature); if (sigFullyDefined) emplaceType(asMutable(functionType), sig.signature); @@ -1453,7 +1456,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f Checkpoint start = checkpoint(this); FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location); - bool sigFullyDefined = FFlag::DebugLuauGreedyGeneralization ? false : !hasFreeType(sig.signature); + bool sigFullyDefined = FFlag::LuauEagerGeneralization ? false : !hasFreeType(sig.signature); DefId def = dfg->getDef(function->name); @@ -1767,7 +1770,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio // Place this function as a child of the non-type function scope scope->children.push_back(NotNull{sig.signatureScope.get()}); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.push_back(std::vector{}); @@ -1785,7 +1788,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio } ); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -1794,7 +1797,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back()); getMutable(generalizedTy)->setOwner(gc); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -2359,12 +2362,8 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall* this, [checkConstraint, callConstraint](const ConstraintPtr& constraint) { - if (!(FFlag::DebugLuauGreedyGeneralization && get(*constraint))) - { - constraint->dependencies.emplace_back(checkConstraint); - - callConstraint->dependencies.emplace_back(constraint.get()); - } + constraint->dependencies.emplace_back(checkConstraint); + callConstraint->dependencies.emplace_back(constraint.get()); } ); @@ -2457,8 +2456,17 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin if (forceSingleton) return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})}; + // Consider a table like: + // + // local DICTIONARY = { "aback", "abacus", "abandon", --[[ so on and so forth ]] } + // + // The intent is (probably) not for this to be an array-like table with a massive + // union for the value, but instead a `{ string }`. + if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && largeTableDepth > 0) + return Inference{builtinTypes->stringType}; + TypeId freeTy = nullptr; - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { freeTy = freshType(scope, Polarity::Positive); FreeType* ft = getMutable(freeTy); @@ -2484,8 +2492,22 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool* if (forceSingleton) return Inference{singletonType}; + // Consider a table like: + // + // local FLAGS = { + // Foo = true, + // Bar = false, + // Baz = true, + // -- so on and so forth + // } + // + // The intent is (probably) not for this to be a table where each element + // is potentially `true` or `false` as a singleton, but just `boolean`. + if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && largeTableDepth > 0) + return Inference{builtinTypes->booleanType}; + TypeId freeTy = nullptr; - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { freeTy = freshType(scope, Polarity::Positive); FreeType* ft = getMutable(freeTy); @@ -2646,7 +2668,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun Checkpoint startCheckpoint = checkpoint(this); FunctionSignature sig = checkFunctionSignature(scope, func, expectedType); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.push_back(std::vector{}); @@ -2664,7 +2686,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun } ); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -3168,7 +3190,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, ttv->definitionLocation = expr->location; ttv->scope = scope.get(); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit)) + largeTableDepth++; + + if (FFlag::LuauEagerGeneralization) interiorFreeTypes.back().types.push_back(ty); else DEPRECATED_interiorTypes.back().push_back(ty); @@ -3230,7 +3255,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, { indexKey = *indexKeyLowerBound.begin(); } - else + else { indexKey = arena->addType(UnionType{std::vector(indexKeyLowerBound.begin(), indexKeyLowerBound.end())}); unionsToSimplify.push_back(indexKey); @@ -3276,6 +3301,9 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, ); } + if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit)) + largeTableDepth--; + return Inference{ty}; } @@ -3425,7 +3453,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu LUAU_ASSERT(nullptr != varargPack); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { // Some of the types in argTypes will eventually be generics, and some // will not. The ones that are not generic will be pruned when @@ -3490,7 +3518,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu if (expectedType && get(*expectedType)) bindFreeType(*expectedType, actualFunctionType); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) scopeToFunction[signatureScope.get()] = actualFunctionType; return { @@ -3625,8 +3653,11 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty, p.readTy = propTy; break; case AstTableAccess::Write: - reportError(*prop.accessLocation, GenericError{"write keyword is illegal here"}); - p.readTy = propTy; + if (!FFlag::LuauEnableWriteOnlyProperties) + { + reportError(*prop.accessLocation, GenericError{"write keyword is illegal here"}); + p.readTy = propTy; + } p.writeTy = propTy; break; default: diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index c7c3e437..3fc68dfe 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -33,12 +33,12 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings) LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock) -LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauDeprecatedAttribute) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall) LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions) -LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion) +LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2) LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult) LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations) @@ -418,7 +418,7 @@ void ConstraintSolver::run() } // Free types that have no constraints at all can be generalized right away. - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { for (TypeId ty : constraintSet.freeTypes) { @@ -479,7 +479,7 @@ void ConstraintSolver::run() // expansion types, etc, so we need to follow it. ty = follow(ty); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { if (seen.contains(ty)) continue; @@ -498,7 +498,7 @@ void ConstraintSolver::run() if (refCount <= 1) unblock(ty, Location{}); - if (FFlag::DebugLuauGreedyGeneralization && refCount == 0) + if (FFlag::LuauEagerGeneralization && refCount == 0) generalizeOneType(ty); } } @@ -676,7 +676,7 @@ void ConstraintSolver::initFreeTypeTracking() auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0); refCount += 1; - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr); it->second.insert(c.get()); @@ -691,7 +691,7 @@ void ConstraintSolver::initFreeTypeTracking() } // Also check flag integrity while we're here - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { LUAU_ASSERT(FFlag::LuauSubtypeGenericsAndNegations); LUAU_ASSERT(FFlag::LuauNoMoreInjectiveTypeFunctions); @@ -739,7 +739,7 @@ void ConstraintSolver::bind(NotNull constraint, TypeId ty, Typ constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed ); // FIXME? Is this the right polarity? - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) trackInteriorFreeType(constraint->scope, ty); return; @@ -806,7 +806,7 @@ bool ConstraintSolver::tryDispatch(NotNull constraint, bool fo else if (auto taec = get(*constraint)) success = tryDispatch(*taec, constraint); else if (auto fcc = get(*constraint)) - success = tryDispatch(*fcc, constraint); + success = tryDispatch(*fcc, constraint, force); else if (auto fcc = get(*constraint)) success = tryDispatch(*fcc, constraint); else if (auto tcc = get(*constraint)) @@ -900,7 +900,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNullscope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access) { - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { ty = follow(ty); if (auto freeTy = get(ty)) @@ -922,7 +922,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNullscope->interiorFreeTypePacks) { @@ -942,7 +942,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull(cTarget)); - shiftReferences(cTarget, result); - bind(constraint, cTarget, result); + // We do this check here to ensure that we don't bind an alias to itself + if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2 && occursCheck(cTarget, result)) + { + reportError(OccursCheckFailed{}, constraint->location); + bind(constraint, cTarget, builtinTypes->errorRecoveryType()); + } + else + { + shiftReferences(cTarget, result); + bind(constraint, cTarget, result); + } }; std::optional tf = (petv->prefix) ? constraint->scope->lookupImportedType(petv->prefix->value, petv->name.value) @@ -1239,19 +1248,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul // deterministic. if (TypeId* cached = instantiatedAliases.find(signature)) { - // However, we might now be revealing a malformed mutually recursive - // alias. `instantiatedAliases` can change from underneath us in a - // way that can cause a cached type id to bind to itself if we don't - // do this check. - if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion && occursCheck(follow(c.target), *cached)) - { - reportError(OccursCheckFailed{}, constraint->location); - bindResult(errorRecoveryType()); - } - else - { - bindResult(*cached); - } + bindResult(*cached); return true; } @@ -1385,13 +1382,13 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull constra } } -bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull constraint) +bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull constraint, bool force) { TypeId fn = follow(c.fn); TypePackId argsPack = follow(c.argsPack); TypePackId result = follow(c.result); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { if (isBlocked(fn)) return block(c.fn, constraint); @@ -1521,7 +1518,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNulllocation }; - auto [status, overload] = resolver.selectOverload(fn, argsPack); + auto [status, overload] = resolver.selectOverload(fn, argsPack, /*useFreeTypeBounds*/ force); TypeId overloadToUse = fn; if (status == OverloadResolver::Analysis::Ok) overloadToUse = overload; @@ -1531,7 +1528,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNullscope, freeTy); @@ -1945,7 +1942,7 @@ bool ConstraintSolver::tryDispatchHasIndexer( TypeId upperBound = arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed}); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { TypeId sr = follow(simplifyIntersection(constraint->scope, constraint->location, ft->upperBound, upperBound)); @@ -1976,7 +1973,7 @@ bool ConstraintSolver::tryDispatchHasIndexer( FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed}; emplace(constraint, resultType, freeResult); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) trackInteriorFreeType(constraint->scope, resultType); tt->indexer = TableIndexer{indexType, resultType}; @@ -2166,7 +2163,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNullupperBound); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue); if (!blocked.empty()) @@ -3067,7 +3064,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( { const TypeId upperBound = follow(ft->upperBound); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { if (get(upperBound) || get(upperBound)) { @@ -3535,7 +3532,7 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target) // Any constraint that might have mutated source may now mutate target - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { auto it = mutatedFreeTypeToConstraint.find(source); if (it != mutatedFreeTypeToConstraint.end()) diff --git a/Analysis/src/DataFlowGraph.cpp b/Analysis/src/DataFlowGraph.cpp index 51476821..0bdfdb19 100644 --- a/Analysis/src/DataFlowGraph.cpp +++ b/Analysis/src/DataFlowGraph.cpp @@ -18,7 +18,6 @@ LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow) -LUAU_FASTFLAGVARIABLE(LuauDfgMatchCGScopes) LUAU_FASTFLAGVARIABLE(LuauDfgAllowUpdatesInLoops) namespace Luau @@ -1240,18 +1239,9 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprUnary* u) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b) { visitExpr(b->left); - if (FFlag::LuauDfgMatchCGScopes) - { - PushScope _{scopeStack, makeChildScope()}; - visitExpr(b->right); - return {defArena->freshCell(Symbol{}, b->location), nullptr}; - } - else - { - visitExpr(b->right); - return {defArena->freshCell(Symbol{}, b->location), nullptr}; - } + visitExpr(b->right); + return {defArena->freshCell(Symbol{}, b->location), nullptr}; } DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t) @@ -1264,29 +1254,9 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIfElse* i) { - if (FFlag::LuauDfgMatchCGScopes) - { - // In the constraint generator, the condition, consequence, and - // alternative all have distinct scopes. - { - PushScope _{scopeStack, makeChildScope()}; - visitExpr(i->condition); - } - { - PushScope _{scopeStack, makeChildScope()}; - visitExpr(i->trueExpr); - } - { - PushScope _{scopeStack, makeChildScope()}; - visitExpr(i->falseExpr); - } - } - else - { - visitExpr(i->condition); - visitExpr(i->trueExpr); - visitExpr(i->falseExpr); - } + visitExpr(i->condition); + visitExpr(i->trueExpr); + visitExpr(i->falseExpr); return {defArena->freshCell(Symbol{}, i->location), nullptr}; } diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index 6a18370a..2cf968a8 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -18,7 +18,9 @@ #include LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) + +LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive) static std::string wrongNumberOfArgsString( size_t expectedCount, @@ -446,6 +448,12 @@ struct ErrorConverter return err; } + if (FFlag::LuauBetterCannotCallFunctionPrimitive) + { + if (auto primitiveTy = get(follow(e.ty)); primitiveTy && primitiveTy->type == PrimitiveType::Function) + return "The type " + toString(e.ty) + " is not precise enough for us to determine the appropriate result type of this call."; + } + return "Cannot call a value of type " + toString(e.ty); } std::string operator()(const Luau::ExtraInformation& e) const @@ -655,7 +663,7 @@ struct ErrorConverter } // binary operators - const auto binaryOps = FFlag::DebugLuauGreedyGeneralization ? kBinaryOps : DEPRECATED_kBinaryOps; + const auto binaryOps = FFlag::LuauEagerGeneralization ? kBinaryOps : DEPRECATED_kBinaryOps; if (auto binaryString = binaryOps.find(tfit->function->name); binaryString != binaryOps.end()) { std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types "; @@ -710,7 +718,7 @@ struct ErrorConverter "'"; } - if ((FFlag::DebugLuauGreedyGeneralization ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name)) + if ((FFlag::LuauEagerGeneralization ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name)) { return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" + "This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues"; diff --git a/Analysis/src/FragmentAutocomplete.cpp b/Analysis/src/FragmentAutocomplete.cpp index 3b606964..5e8a9598 100644 --- a/Analysis/src/FragmentAutocomplete.cpp +++ b/Analysis/src/FragmentAutocomplete.cpp @@ -29,10 +29,6 @@ LUAU_FASTINT(LuauTypeInferIterationLimit); LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete) -LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection) -LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes) -LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes) -LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings) LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection) LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection) LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak) @@ -1261,11 +1257,8 @@ FragmentAutocompleteStatusResult tryFragmentAutocomplete( StringCompletionCallback stringCompletionCB ) { - if (FFlag::LuauBetterCursorInCommentDetection) - { - if (isWithinComment(context.freshParse.commentLocations, cursorPosition)) - return {FragmentAutocompleteStatus::Success, std::nullopt}; - } + if (isWithinComment(context.freshParse.commentLocations, cursorPosition)) + return {FragmentAutocompleteStatus::Success, std::nullopt}; // TODO: we should calculate fragmentEnd position here, by using context.newAstRoot and cursorPosition try { diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 285403e8..2b06f536 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -39,12 +39,13 @@ LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(LuauInferInNoCheckMode) LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile) LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes) LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode) LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode) +LUAU_FASTFLAGVARIABLE(LuauNewSolverTypecheckCatchTimeouts) namespace Luau { @@ -408,6 +409,38 @@ void Frontend::parse(const ModuleName& name) parseGraph(buildQueue, name, false); } +void Frontend::parseModules(const std::vector& names) +{ + LUAU_TIMETRACE_SCOPE("Frontend::parseModules", "Frontend"); + + DenseHashSet seen{{}}; + + for (const ModuleName& name : names) + { + if (seen.contains(name)) + continue; + + if (auto it = sourceNodes.find(name); it != sourceNodes.end() && !it->second->hasDirtySourceModule()) + { + seen.insert(name); + continue; + } + + std::vector queue; + parseGraph( + queue, + name, + false, + [&seen](const ModuleName& name) + { + return seen.contains(name); + } + ); + + seen.insert(name); + } +} + CheckResult Frontend::check(const ModuleName& name, std::optional optionOverride) { LUAU_TIMETRACE_SCOPE("Frontend::check", "Frontend"); @@ -1403,13 +1436,13 @@ ModulePtr check( requireCycles }; - // FIXME: Delete this flag when clipping FFlag::DebugLuauGreedyGeneralization. + // FIXME: Delete this flag when clipping FFlag::LuauEagerGeneralization. // // This optional<> only exists so that we can run one constructor when the flag // is set, and another when it is unset. std::optional cs; - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { ConstraintSet constraintSet = cg.run(sourceModule.root); result->errors = std::move(constraintSet.errors); @@ -1495,6 +1528,52 @@ ModulePtr check( for (auto& [name, tf] : result->exportedTypeBindings) tf.type = builtinTypes->errorRecoveryType(); } + else if (FFlag::LuauNewSolverTypecheckCatchTimeouts) + { + try + { + switch (mode) + { + case Mode::Nonstrict: + Luau::checkNonStrict( + builtinTypes, + NotNull{simplifier.get()}, + NotNull{&typeFunctionRuntime}, + iceHandler, + NotNull{&unifierState}, + NotNull{&dfg}, + NotNull{&limits}, + sourceModule, + result.get() + ); + break; + case Mode::Definition: + // fallthrough intentional + case Mode::Strict: + Luau::check( + builtinTypes, + NotNull{simplifier.get()}, + NotNull{&typeFunctionRuntime}, + NotNull{&unifierState}, + NotNull{&limits}, + logger.get(), + sourceModule, + result.get() + ); + break; + case Mode::NoCheck: + break; + }; + } + catch (const TimeLimitError&) + { + result->timeout = true; + } + catch (const UserCancelError&) + { + result->cancelled = true; + } + } else { switch (mode) diff --git a/Analysis/src/Generalization.cpp b/Analysis/src/Generalization.cpp index 41da29cf..9405a333 100644 --- a/Analysis/src/Generalization.cpp +++ b/Analysis/src/Generalization.cpp @@ -14,9 +14,9 @@ #include "Luau/Substitution.h" #include "Luau/VisitType.h" -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) -LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization3) +LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization) namespace Luau { @@ -469,7 +469,7 @@ struct FreeTypeSearcher : TypeVisitor bool visit(TypeId ty, const FreeType& ft) override { - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { if (!subsumes(scope, ft.scope)) return true; @@ -520,7 +520,7 @@ struct FreeTypeSearcher : TypeVisitor if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope)) { - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) unsealedTables.insert(ty); else { @@ -544,22 +544,57 @@ struct FreeTypeSearcher : TypeVisitor for (const auto& [_name, prop] : tt.props) { - if (prop.isReadOnly()) - traverse(*prop.readTy); + if (FFlag::LuauEnableWriteOnlyProperties) + { + if (prop.isReadOnly()) + { + traverse(*prop.readTy); + } + else if (prop.isWriteOnly()) + { + Polarity p = polarity; + polarity = Polarity::Negative; + traverse(*prop.writeTy); + polarity = p; + } + else if (prop.isShared()) + { + Polarity p = polarity; + polarity = Polarity::Mixed; + traverse(prop.type()); + polarity = p; + } + else + { + LUAU_ASSERT(prop.isReadWrite() && !prop.isShared()); + + traverse(*prop.readTy); + Polarity p = polarity; + polarity = Polarity::Negative; + traverse(*prop.writeTy); + polarity = p; + } + + } else { - LUAU_ASSERT(prop.isShared()); + if (prop.isReadOnly()) + traverse(*prop.readTy); + else + { + LUAU_ASSERT(prop.isShared()); - Polarity p = polarity; - polarity = Polarity::Mixed; - traverse(prop.type()); - polarity = p; + Polarity p = polarity; + polarity = Polarity::Mixed; + traverse(prop.type()); + polarity = p; + } } } if (tt.indexer) { - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { // {[K]: V} is equivalent to three functions: get, set, and iterate // @@ -617,7 +652,7 @@ struct FreeTypeSearcher : TypeVisitor if (!subsumes(scope, ftp.scope)) return true; - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { GeneralizationParams& params = typePacks[tp]; ++params.useCount; @@ -1241,7 +1276,7 @@ GeneralizationResult generalizeType( if (!hasLowerBound && !hasUpperBound) { - if (!isWithinFunction || (!FFlag::DebugLuauGreedyGeneralization && (params.polarity != Polarity::Mixed && params.useCount == 1))) + if (!isWithinFunction || (!FFlag::LuauEagerGeneralization && (params.polarity != Polarity::Mixed && params.useCount == 1))) emplaceType(asMutable(freeTy), builtinTypes->unknownType); else { @@ -1273,7 +1308,7 @@ GeneralizationResult generalizeType( if (follow(lb) != freeTy) emplaceType(asMutable(freeTy), lb); - else if (!isWithinFunction || (!FFlag::DebugLuauGreedyGeneralization && params.useCount == 1)) + else if (!isWithinFunction || (!FFlag::LuauEagerGeneralization && params.useCount == 1)) emplaceType(asMutable(freeTy), builtinTypes->unknownType); else { @@ -1390,7 +1425,7 @@ std::optional generalize( FreeTypeSearcher fts{scope, cachedTypes}; fts.traverse(ty); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { FunctionType* functionTy = getMutable(ty); auto pushGeneric = [&](TypeId t) @@ -1521,15 +1556,51 @@ struct GenericCounter : TypeVisitor for (const auto& [_name, prop] : tt.props) { - if (prop.isReadOnly()) - traverse(*prop.readTy); + if (FFlag::LuauEnableWriteOnlyProperties) + { + if (prop.isReadOnly()) + { + traverse(*prop.readTy); + } + else if (prop.isWriteOnly()) + { + Polarity p = polarity; + polarity = Polarity::Negative; + traverse(*prop.writeTy); + polarity = p; + } + else if (prop.isShared()) + { + Polarity p = polarity; + polarity = Polarity::Mixed; + traverse(prop.type()); + polarity = p; + } + else + { + LUAU_ASSERT(prop.isReadWrite() && !prop.isShared()); + + traverse(*prop.readTy); + Polarity p = polarity; + polarity = Polarity::Negative; + traverse(*prop.writeTy); + polarity = p; + } + + } else { - LUAU_ASSERT(prop.isShared()); + if (prop.isReadOnly()) + traverse(*prop.readTy); + else + { + LUAU_ASSERT(prop.isShared()); - polarity = Polarity::Mixed; - traverse(prop.type()); - polarity = previous; + Polarity p = polarity; + polarity = Polarity::Mixed; + traverse(prop.type()); + polarity = p; + } } } @@ -1582,7 +1653,7 @@ void pruneUnnecessaryGenerics( TypeId ty ) { - if (!FFlag::DebugLuauGreedyGeneralization) + if (!FFlag::LuauEagerGeneralization) return; ty = follow(ty); diff --git a/Analysis/src/InferPolarity.cpp b/Analysis/src/InferPolarity.cpp index 6319a214..806afb27 100644 --- a/Analysis/src/InferPolarity.cpp +++ b/Analysis/src/InferPolarity.cpp @@ -5,7 +5,7 @@ #include "Luau/Scope.h" #include "Luau/VisitType.h" -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization) namespace Luau { @@ -133,7 +133,7 @@ struct InferPolarity : TypeVisitor template static void inferGenericPolarities_(NotNull arena, NotNull scope, TID ty) { - if (!FFlag::LuauNonReentrantGeneralization3) + if (!FFlag::LuauEagerGeneralization) return; InferPolarity infer{arena, scope}; diff --git a/Analysis/src/OverloadResolution.cpp b/Analysis/src/OverloadResolution.cpp index 0443ba6c..00d417b2 100644 --- a/Analysis/src/OverloadResolution.cpp +++ b/Analysis/src/OverloadResolution.cpp @@ -40,7 +40,7 @@ OverloadResolver::OverloadResolver( { } -std::pair OverloadResolver::selectOverload(TypeId ty, TypePackId argsPack) +std::pair OverloadResolver::selectOverload(TypeId ty, TypePackId argsPack, bool useFreeTypeBounds) { auto tryOne = [&](TypeId f) { @@ -51,6 +51,9 @@ std::pair OverloadResolver::selectOverload(T SubtypingResult r = subtyping.isSubtype(argsPack, ftv->argTypes, scope); subtyping.variance = variance; + if (!useFreeTypeBounds && !r.assumedConstraints.empty()) + return false; + if (r.isSubtype) return true; } @@ -461,7 +464,7 @@ static std::optional selectOverload( { auto resolver = std::make_unique(builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location); - auto [status, overload] = resolver->selectOverload(fn, argsPack); + auto [status, overload] = resolver->selectOverload(fn, argsPack, /*useFreeTypeBounds*/ false); if (status == OverloadResolver::Analysis::Ok) return overload; diff --git a/Analysis/src/Subtyping.cpp b/Analysis/src/Subtyping.cpp index 45d1b7fe..5cf7fbc2 100644 --- a/Analysis/src/Subtyping.cpp +++ b/Analysis/src/Subtyping.cpp @@ -21,6 +21,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity) LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100) LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) +LUAU_FASTFLAG(LuauEagerGeneralization) namespace Luau { @@ -680,6 +681,44 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub result.isSubtype = ok; result.isCacheable = false; } + else if (auto pair = get2(subTy, superTy); FFlag::LuauEagerGeneralization && pair) + { + // Any two free types are potentially subtypes of one another because + // both of them could be narrowed to never. + result = {true}; + result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy}); + } + else if (auto superFree = get(superTy); superFree && FFlag::LuauEagerGeneralization) + { + // Given SubTy <: (LB <: SuperTy <: UB) + // + // If SubTy <: UB, then it is possible that SubTy <: SuperTy. + // If SubTy upperBound, scope); + + if (result.isSubtype) + result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy}); + } + else if (auto subFree = get(subTy); subFree && FFlag::LuauEagerGeneralization) + { + // Given (LB <: SubTy <: UB) <: SuperTy + // + // If UB <: SuperTy, then it is certainly the case that SubTy <: SuperTy. + // If SuperTy <: UB and LB <: SuperTy, then it is possible that UB will later be narrowed such that SubTy <: SuperTy. + // If LB lowerBound, superTy, scope).isSubtype) + { + result = {true}; + result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy}); + } + else + result = {false}; + } else if (auto p = get2(subTy, superTy)) result = isCovariantWith(env, p.first->ty, p.second->ty, scope).withBothComponent(TypePath::TypeField::Negated); else if (auto subNegation = get(subTy)) @@ -1411,8 +1450,24 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl { SubtypingResult result{true}; - if (subTable->props.empty() && !subTable->indexer && superTable->indexer) - return {false}; + if (FFlag::LuauEagerGeneralization) + { + if (subTable->props.empty() && !subTable->indexer && subTable->state == TableState::Sealed && superTable->indexer) + { + // While it is certainly the case that {} props.empty() && !subTable->indexer && superTable->indexer) + return {false}; + } for (const auto& [name, superProp] : superTable->props) { @@ -1451,6 +1506,12 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl { if (subTable->indexer) result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope)); + else if (FFlag::LuauEagerGeneralization && subTable->state != TableState::Sealed) + { + // As above, we assume that {| |} <: {T} because the unsealed table + // on the left will eventually gain the necessary indexer. + return {true}; + } else return {false}; } diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index 6d192896..031d0b7c 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -42,6 +42,7 @@ LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit) */ LUAU_FASTINTVARIABLE(DebugLuauVerboseTypeNames, 0) LUAU_FASTFLAGVARIABLE(DebugLuauToStringNoLexicalSort) +LUAU_FASTFLAGVARIABLE(LuauFixEmptyTypePackStringification) namespace Luau { @@ -479,6 +480,9 @@ struct TypeStringifier bool wrap = !singleTp && get(follow(tp)); + if (FFlag::LuauFixEmptyTypePackStringification) + wrap &= !isEmpty(tp); + if (wrap) state.emit("("); @@ -687,14 +691,18 @@ struct TypeStringifier state.emit("("); - if (state.opts.functionTypeArguments) + if (FFlag::LuauFixEmptyTypePackStringification && isEmpty(ftv.argTypes)) + { + // if we've got an empty argument pack, we're done. + } + else if (state.opts.functionTypeArguments) stringify(ftv.argTypes, ftv.argNames); else stringify(ftv.argTypes); state.emit(") -> "); - bool plural = true; + bool plural = FFlag::LuauFixEmptyTypePackStringification ? !isEmpty(ftv.retTypes) : true; auto retBegin = begin(ftv.retTypes); auto retEnd = end(ftv.retTypes); @@ -1235,6 +1243,16 @@ struct TypePackStringifier return; } + if (FFlag::LuauFixEmptyTypePackStringification) + { + if (tp.head.empty() && (!tp.tail || isEmpty(*tp.tail))) + { + state.emit("()"); + state.unsee(&tp); + return; + } + } + bool first = true; for (const auto& typeId : tp.head) @@ -1782,17 +1800,34 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp state.emit("): "); - size_t retSize = size(ftv.retTypes); - bool hasTail = !finite(ftv.retTypes); - bool wrap = get(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize != 1); + if (FFlag::LuauFixEmptyTypePackStringification) + { + size_t retSize = size(ftv.retTypes); + bool hasTail = !finite(ftv.retTypes); + bool wrap = get(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize > 1); - if (wrap) - state.emit("("); + if (wrap) + state.emit("("); - tvs.stringify(ftv.retTypes); + tvs.stringify(ftv.retTypes); - if (wrap) - state.emit(")"); + if (wrap) + state.emit(")"); + } + else + { + size_t retSize = size(ftv.retTypes); + bool hasTail = !finite(ftv.retTypes); + bool wrap = get(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize != 1); + + if (wrap) + state.emit("("); + + tvs.stringify(ftv.retTypes); + + if (wrap) + state.emit(")"); + } return result.name; } diff --git a/Analysis/src/Transpiler.cpp b/Analysis/src/Transpiler.cpp index d9489c9d..27f15b12 100644 --- a/Analysis/src/Transpiler.cpp +++ b/Analysis/src/Transpiler.cpp @@ -10,6 +10,7 @@ #include #include +LUAU_FASTFLAG(LuauStoreCSTData2) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions) @@ -164,7 +165,17 @@ struct StringWriter : Writer void symbol(std::string_view s) override { - write(s); + if (FFlag::LuauStoreCSTData2) + { + write(s); + } + else + { + if (isDigit(lastChar) && s[0] == '.') + space(); + + write(s); + } } void literal(std::string_view s) override @@ -244,7 +255,7 @@ public: first = !first; else { - if (commaPosition) + if (FFlag::LuauStoreCSTData2 && commaPosition) { writer.advance(*commaPosition); commaPosition++; @@ -296,6 +307,1024 @@ private: size_t idx = 0; }; +struct Printer_DEPRECATED +{ + explicit Printer_DEPRECATED(Writer& writer) + : writer(writer) + { + } + + bool writeTypes = false; + Writer& writer; + + void visualize(const AstLocal& local) + { + advance(local.location.begin); + + writer.identifier(local.name.value); + if (writeTypes && local.annotation) + { + writer.symbol(":"); + visualizeTypeAnnotation(*local.annotation); + } + } + + void visualizeTypePackAnnotation(const AstTypePack& annotation, bool forVarArg, bool unconditionallyParenthesize = true) + { + advance(annotation.location.begin); + if (const AstTypePackVariadic* variadicTp = annotation.as()) + { + if (!forVarArg) + writer.symbol("..."); + + visualizeTypeAnnotation(*variadicTp->variadicType); + } + else if (const AstTypePackGeneric* genericTp = annotation.as()) + { + writer.symbol(genericTp->genericName.value); + writer.symbol("..."); + } + else if (const AstTypePackExplicit* explicitTp = annotation.as()) + { + LUAU_ASSERT(!forVarArg); + visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize); + } + else + { + LUAU_ASSERT(!"Unknown TypePackAnnotation kind"); + } + } + + void visualizeTypeList(const AstTypeList& list, bool unconditionallyParenthesize) + { + size_t typeCount = list.types.size + (list.tailType != nullptr ? 1 : 0); + if (typeCount == 0) + { + writer.symbol("("); + writer.symbol(")"); + } + else if (typeCount == 1) + { + bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is()); + if (shouldParenthesize) + writer.symbol("("); + + // Only variadic tail + if (list.types.size == 0) + { + visualizeTypePackAnnotation(*list.tailType, false); + } + else + { + visualizeTypeAnnotation(*list.types.data[0]); + } + + if (shouldParenthesize) + writer.symbol(")"); + } + else + { + writer.symbol("("); + + bool first = true; + for (const auto& el : list.types) + { + if (first) + first = false; + else + writer.symbol(","); + + visualizeTypeAnnotation(*el); + } + + if (list.tailType) + { + writer.symbol(","); + visualizeTypePackAnnotation(*list.tailType, false); + } + + writer.symbol(")"); + } + } + + bool isIntegerish(double d) + { + if (d <= std::numeric_limits::max() && d >= std::numeric_limits::min()) + return double(int(d)) == d && !(d == 0.0 && signbit(d)); + else + return false; + } + + void visualize(AstExpr& expr) + { + advance(expr.location.begin); + + if (const auto& a = expr.as()) + { + writer.symbol("("); + visualize(*a->expr); + writer.symbol(")"); + } + else if (expr.is()) + { + writer.keyword("nil"); + } + else if (const auto& a = expr.as()) + { + if (a->value) + writer.keyword("true"); + else + writer.keyword("false"); + } + else if (const auto& a = expr.as()) + { + if (isinf(a->value)) + { + if (a->value > 0) + writer.literal("1e500"); + else + writer.literal("-1e500"); + } + else if (isnan(a->value)) + writer.literal("0/0"); + else + { + if (isIntegerish(a->value)) + writer.literal(std::to_string(int(a->value))); + else + { + char buffer[100]; + size_t len = snprintf(buffer, sizeof(buffer), "%.17g", a->value); + writer.literal(std::string_view{buffer, len}); + } + } + } + else if (const auto& a = expr.as()) + { + writer.string(std::string_view(a->value.data, a->value.size)); + } + else if (const auto& a = expr.as()) + { + writer.identifier(a->local->name.value); + } + else if (const auto& a = expr.as()) + { + writer.identifier(a->name.value); + } + else if (expr.is()) + { + writer.symbol("..."); + } + else if (const auto& a = expr.as()) + { + visualize(*a->func); + writer.symbol("("); + + bool first = true; + for (const auto& arg : a->args) + { + if (first) + first = false; + else + writer.symbol(","); + + visualize(*arg); + } + + writer.symbol(")"); + } + else if (const auto& a = expr.as()) + { + visualize(*a->expr); + writer.symbol(std::string(1, a->op)); + writer.write(a->index.value); + } + else if (const auto& a = expr.as()) + { + visualize(*a->expr); + writer.symbol("["); + visualize(*a->index); + writer.symbol("]"); + } + else if (const auto& a = expr.as()) + { + writer.keyword("function"); + visualizeFunctionBody(*a); + } + else if (const auto& a = expr.as()) + { + writer.symbol("{"); + + bool first = true; + + for (const auto& item : a->items) + { + if (first) + first = false; + else + writer.symbol(","); + + switch (item.kind) + { + case AstExprTable::Item::List: + break; + + case AstExprTable::Item::Record: + { + const auto& value = item.key->as()->value; + advance(item.key->location.begin); + writer.identifier(std::string_view(value.data, value.size)); + writer.maybeSpace(item.value->location.begin, 1); + writer.symbol("="); + } + break; + + case AstExprTable::Item::General: + { + writer.symbol("["); + visualize(*item.key); + writer.symbol("]"); + writer.maybeSpace(item.value->location.begin, 1); + writer.symbol("="); + } + break; + + default: + LUAU_ASSERT(!"Unknown table item kind"); + } + + advance(item.value->location.begin); + visualize(*item.value); + } + + // Decrement endPos column so that we advance to before the closing `}` brace before writing, rather than after it + Position endPos = expr.location.end; + if (endPos.column > 0) + --endPos.column; + + advance(endPos); + + writer.symbol("}"); + advance(expr.location.end); + } + else if (const auto& a = expr.as()) + { + switch (a->op) + { + case AstExprUnary::Not: + writer.keyword("not"); + break; + case AstExprUnary::Minus: + writer.symbol("-"); + break; + case AstExprUnary::Len: + writer.symbol("#"); + break; + } + visualize(*a->expr); + } + else if (const auto& a = expr.as()) + { + visualize(*a->left); + + switch (a->op) + { + case AstExprBinary::Add: + case AstExprBinary::Sub: + case AstExprBinary::Mul: + case AstExprBinary::Div: + case AstExprBinary::FloorDiv: + case AstExprBinary::Mod: + case AstExprBinary::Pow: + case AstExprBinary::CompareLt: + case AstExprBinary::CompareGt: + writer.maybeSpace(a->right->location.begin, 2); + writer.symbol(toString(a->op)); + break; + case AstExprBinary::Concat: + case AstExprBinary::CompareNe: + case AstExprBinary::CompareEq: + case AstExprBinary::CompareLe: + case AstExprBinary::CompareGe: + case AstExprBinary::Or: + writer.maybeSpace(a->right->location.begin, 3); + writer.keyword(toString(a->op)); + break; + case AstExprBinary::And: + writer.maybeSpace(a->right->location.begin, 4); + writer.keyword(toString(a->op)); + break; + default: + LUAU_ASSERT(!"Unknown Op"); + } + + visualize(*a->right); + } + else if (const auto& a = expr.as()) + { + visualize(*a->expr); + + if (writeTypes) + { + writer.maybeSpace(a->annotation->location.begin, 2); + writer.symbol("::"); + visualizeTypeAnnotation(*a->annotation); + } + } + else if (const auto& a = expr.as()) + { + writer.keyword("if"); + visualize(*a->condition); + writer.keyword("then"); + visualize(*a->trueExpr); + writer.keyword("else"); + visualize(*a->falseExpr); + } + else if (const auto& a = expr.as()) + { + writer.symbol("`"); + + size_t index = 0; + + for (const auto& string : a->strings) + { + writer.write(escape(std::string_view(string.data, string.size), /* escapeForInterpString = */ true)); + + if (index < a->expressions.size) + { + writer.symbol("{"); + visualize(*a->expressions.data[index]); + writer.symbol("}"); + } + + index++; + } + + writer.symbol("`"); + } + else if (const auto& a = expr.as()) + { + writer.symbol("(error-expr"); + + for (size_t i = 0; i < a->expressions.size; i++) + { + writer.symbol(i == 0 ? ": " : ", "); + visualize(*a->expressions.data[i]); + } + + writer.symbol(")"); + } + else + { + LUAU_ASSERT(!"Unknown AstExpr"); + } + } + + void writeEnd(const Location& loc) + { + Position endPos = loc.end; + if (endPos.column >= 3) + endPos.column -= 3; + advance(endPos); + writer.keyword("end"); + } + + void advance(const Position& newPos) + { + writer.advance(newPos); + } + + void visualize(AstStat& program) + { + advance(program.location.begin); + + if (const auto& block = program.as()) + { + writer.keyword("do"); + for (const auto& s : block->body) + visualize(*s); + writeEnd(program.location); + } + else if (const auto& a = program.as()) + { + writer.keyword("if"); + visualizeElseIf(*a); + } + else if (const auto& a = program.as()) + { + writer.keyword("while"); + visualize(*a->condition); + writer.keyword("do"); + visualizeBlock(*a->body); + writeEnd(program.location); + } + else if (const auto& a = program.as()) + { + writer.keyword("repeat"); + visualizeBlock(*a->body); + if (a->condition->location.begin.column > 5) + writer.advance(Position{a->condition->location.begin.line, a->condition->location.begin.column - 6}); + writer.keyword("until"); + visualize(*a->condition); + } + else if (program.is()) + writer.keyword("break"); + else if (program.is()) + writer.keyword("continue"); + else if (const auto& a = program.as()) + { + writer.keyword("return"); + + bool first = true; + for (const auto& expr : a->list) + { + if (first) + first = false; + else + writer.symbol(","); + visualize(*expr); + } + } + else if (const auto& a = program.as()) + { + visualize(*a->expr); + } + else if (const auto& a = program.as()) + { + writer.keyword("local"); + + bool first = true; + for (const auto& local : a->vars) + { + if (first) + first = false; + else + writer.write(","); + + visualize(*local); + } + + first = true; + for (const auto& value : a->values) + { + if (first) + { + first = false; + writer.maybeSpace(value->location.begin, 2); + writer.symbol("="); + } + else + writer.symbol(","); + + visualize(*value); + } + } + else if (const auto& a = program.as()) + { + writer.keyword("for"); + + visualize(*a->var); + writer.symbol("="); + visualize(*a->from); + writer.symbol(","); + visualize(*a->to); + if (a->step) + { + writer.symbol(","); + visualize(*a->step); + } + writer.keyword("do"); + visualizeBlock(*a->body); + + writeEnd(program.location); + } + else if (const auto& a = program.as()) + { + writer.keyword("for"); + + bool first = true; + for (const auto& var : a->vars) + { + if (first) + first = false; + else + writer.symbol(","); + + visualize(*var); + } + + writer.keyword("in"); + + first = true; + for (const auto& val : a->values) + { + if (first) + first = false; + else + writer.symbol(","); + + visualize(*val); + } + + writer.keyword("do"); + + visualizeBlock(*a->body); + + writeEnd(program.location); + } + else if (const auto& a = program.as()) + { + bool first = true; + for (const auto& var : a->vars) + { + if (first) + first = false; + else + writer.symbol(","); + visualize(*var); + } + + first = true; + for (const auto& value : a->values) + { + if (first) + { + writer.maybeSpace(value->location.begin, 1); + writer.symbol("="); + first = false; + } + else + writer.symbol(","); + + visualize(*value); + } + } + else if (const auto& a = program.as()) + { + visualize(*a->var); + + switch (a->op) + { + case AstExprBinary::Add: + writer.maybeSpace(a->value->location.begin, 2); + writer.symbol("+="); + break; + case AstExprBinary::Sub: + writer.maybeSpace(a->value->location.begin, 2); + writer.symbol("-="); + break; + case AstExprBinary::Mul: + writer.maybeSpace(a->value->location.begin, 2); + writer.symbol("*="); + break; + case AstExprBinary::Div: + writer.maybeSpace(a->value->location.begin, 2); + writer.symbol("/="); + break; + case AstExprBinary::FloorDiv: + writer.maybeSpace(a->value->location.begin, 2); + writer.symbol("//="); + break; + case AstExprBinary::Mod: + writer.maybeSpace(a->value->location.begin, 2); + writer.symbol("%="); + break; + case AstExprBinary::Pow: + writer.maybeSpace(a->value->location.begin, 2); + writer.symbol("^="); + break; + case AstExprBinary::Concat: + writer.maybeSpace(a->value->location.begin, 3); + writer.symbol("..="); + break; + default: + LUAU_ASSERT(!"Unexpected compound assignment op"); + } + + visualize(*a->value); + } + else if (const auto& a = program.as()) + { + writer.keyword("function"); + visualize(*a->name); + visualizeFunctionBody(*a->func); + } + else if (const auto& a = program.as()) + { + writer.keyword("local function"); + advance(a->name->location.begin); + writer.identifier(a->name->name.value); + visualizeFunctionBody(*a->func); + } + else if (const auto& a = program.as()) + { + if (writeTypes) + { + if (a->exported) + writer.keyword("export"); + + writer.keyword("type"); + writer.identifier(a->name.value); + if (a->generics.size > 0 || a->genericPacks.size > 0) + { + writer.symbol("<"); + CommaSeparatorInserter comma(writer); + + for (auto o : a->generics) + { + comma(); + + writer.advance(o->location.begin); + writer.identifier(o->name.value); + + if (o->defaultValue) + { + writer.maybeSpace(o->defaultValue->location.begin, 2); + writer.symbol("="); + visualizeTypeAnnotation(*o->defaultValue); + } + } + + for (auto o : a->genericPacks) + { + comma(); + + writer.advance(o->location.begin); + writer.identifier(o->name.value); + writer.symbol("..."); + + if (o->defaultValue) + { + writer.maybeSpace(o->defaultValue->location.begin, 2); + writer.symbol("="); + visualizeTypePackAnnotation(*o->defaultValue, false); + } + } + + writer.symbol(">"); + } + writer.maybeSpace(a->type->location.begin, 2); + writer.symbol("="); + visualizeTypeAnnotation(*a->type); + } + } + else if (const auto& t = program.as()) + { + if (writeTypes) + { + writer.keyword("type function"); + writer.identifier(t->name.value); + visualizeFunctionBody(*t->body); + } + } + else if (const auto& a = program.as()) + { + writer.symbol("(error-stat"); + + for (size_t i = 0; i < a->expressions.size; i++) + { + writer.symbol(i == 0 ? ": " : ", "); + visualize(*a->expressions.data[i]); + } + + for (size_t i = 0; i < a->statements.size; i++) + { + writer.symbol(i == 0 && a->expressions.size == 0 ? ": " : ", "); + visualize(*a->statements.data[i]); + } + + writer.symbol(")"); + } + else + { + LUAU_ASSERT(!"Unknown AstStat"); + } + + if (program.hasSemicolon) + writer.symbol(";"); + } + + void visualizeFunctionBody(AstExprFunction& func) + { + if (func.generics.size > 0 || func.genericPacks.size > 0) + { + CommaSeparatorInserter comma(writer); + writer.symbol("<"); + for (const auto& o : func.generics) + { + comma(); + + writer.advance(o->location.begin); + writer.identifier(o->name.value); + } + for (const auto& o : func.genericPacks) + { + comma(); + + writer.advance(o->location.begin); + writer.identifier(o->name.value); + writer.symbol("..."); + } + writer.symbol(">"); + } + + writer.symbol("("); + CommaSeparatorInserter comma(writer); + + for (size_t i = 0; i < func.args.size; ++i) + { + AstLocal* local = func.args.data[i]; + + comma(); + + advance(local->location.begin); + writer.identifier(local->name.value); + if (writeTypes && local->annotation) + { + writer.symbol(":"); + visualizeTypeAnnotation(*local->annotation); + } + } + + if (func.vararg) + { + comma(); + advance(func.varargLocation.begin); + writer.symbol("..."); + + if (func.varargAnnotation) + { + writer.symbol(":"); + visualizeTypePackAnnotation(*func.varargAnnotation, true); + } + } + + writer.symbol(")"); + + if (writeTypes && (FFlag::LuauStoreReturnTypesAsPackOnAst ? func.returnAnnotation != nullptr : func.returnAnnotation_DEPRECATED.has_value())) + { + writer.symbol(":"); + writer.space(); + + if (FFlag::LuauStoreReturnTypesAsPackOnAst) + visualizeTypePackAnnotation(*func.returnAnnotation, false, false); + else + visualizeTypeList(*func.returnAnnotation_DEPRECATED, false); + } + + visualizeBlock(*func.body); + writeEnd(func.location); + } + + void visualizeBlock(AstStatBlock& block) + { + for (const auto& s : block.body) + visualize(*s); + writer.advance(block.location.end); + } + + void visualizeBlock(AstStat& stat) + { + if (AstStatBlock* block = stat.as()) + visualizeBlock(*block); + else + LUAU_ASSERT(!"visualizeBlock was expecting an AstStatBlock"); + } + + void visualizeElseIf(AstStatIf& elseif) + { + visualize(*elseif.condition); + writer.keyword("then"); + visualizeBlock(*elseif.thenbody); + + if (elseif.elsebody == nullptr) + { + writeEnd(elseif.location); + } + else if (auto elseifelseif = elseif.elsebody->as()) + { + writer.keyword("elseif"); + visualizeElseIf(*elseifelseif); + } + else + { + writer.keyword("else"); + + visualizeBlock(*elseif.elsebody); + writeEnd(elseif.location); + } + } + + void visualizeTypeAnnotation(const AstType& typeAnnotation) + { + advance(typeAnnotation.location.begin); + if (const auto& a = typeAnnotation.as()) + { + if (a->prefix) + { + writer.write(a->prefix->value); + writer.symbol("."); + } + + writer.write(a->name.value); + if (a->parameters.size > 0 || a->hasParameterList) + { + CommaSeparatorInserter comma(writer); + writer.symbol("<"); + for (auto o : a->parameters) + { + comma(); + + if (o.type) + visualizeTypeAnnotation(*o.type); + else + visualizeTypePackAnnotation(*o.typePack, false); + } + + writer.symbol(">"); + } + } + else if (const auto& a = typeAnnotation.as()) + { + if (a->generics.size > 0 || a->genericPacks.size > 0) + { + CommaSeparatorInserter comma(writer); + writer.symbol("<"); + for (const auto& o : a->generics) + { + comma(); + + writer.advance(o->location.begin); + writer.identifier(o->name.value); + } + for (const auto& o : a->genericPacks) + { + comma(); + + writer.advance(o->location.begin); + writer.identifier(o->name.value); + writer.symbol("..."); + } + writer.symbol(">"); + } + + { + visualizeTypeList(a->argTypes, true); + } + + writer.symbol("->"); + if (FFlag::LuauStoreReturnTypesAsPackOnAst) + visualizeTypePackAnnotation(*a->returnTypes, false); + else + visualizeTypeList(a->returnTypes_DEPRECATED, true); + } + else if (const auto& a = typeAnnotation.as()) + { + AstTypeReference* indexType = a->indexer ? a->indexer->indexType->as() : nullptr; + + if (a->props.size == 0 && indexType && indexType->name == "number") + { + writer.symbol("{"); + visualizeTypeAnnotation(*a->indexer->resultType); + writer.symbol("}"); + } + else + { + CommaSeparatorInserter comma(writer); + + writer.symbol("{"); + + for (std::size_t i = 0; i < a->props.size; ++i) + { + comma(); + advance(a->props.data[i].location.begin); + writer.identifier(a->props.data[i].name.value); + if (a->props.data[i].type) + { + writer.symbol(":"); + visualizeTypeAnnotation(*a->props.data[i].type); + } + } + if (a->indexer) + { + comma(); + writer.symbol("["); + visualizeTypeAnnotation(*a->indexer->indexType); + writer.symbol("]"); + writer.symbol(":"); + visualizeTypeAnnotation(*a->indexer->resultType); + } + writer.symbol("}"); + } + } + else if (auto a = typeAnnotation.as()) + { + writer.keyword("typeof"); + writer.symbol("("); + visualize(*a->expr); + writer.symbol(")"); + } + else if (const auto& a = typeAnnotation.as()) + { + if (a->types.size == 2) + { + AstType* l = a->types.data[0]; + AstType* r = a->types.data[1]; + + auto lta = l->as(); + if (lta && lta->name == "nil" && !r->is()) + std::swap(l, r); + + // it's still possible that we had a (T | U) or (T | nil) and not (nil | T) + auto rta = r->as(); + if (rta && rta->name == "nil") + { + bool wrap = l->as() || l->as(); + + if (wrap) + writer.symbol("("); + + visualizeTypeAnnotation(*l); + + if (wrap) + writer.symbol(")"); + + writer.symbol("?"); + return; + } + } + + for (size_t i = 0; i < a->types.size; ++i) + { + if (a->types.data[i]->is()) + { + writer.symbol("?"); + continue; + } + + if (i > 0) + { + writer.maybeSpace(a->types.data[i]->location.begin, 2); + writer.symbol("|"); + } + + bool wrap = a->types.data[i]->as() || a->types.data[i]->as(); + + if (wrap) + writer.symbol("("); + + visualizeTypeAnnotation(*a->types.data[i]); + + if (wrap) + writer.symbol(")"); + } + } + else if (const auto& a = typeAnnotation.as()) + { + for (size_t i = 0; i < a->types.size; ++i) + { + if (i > 0) + { + writer.maybeSpace(a->types.data[i]->location.begin, 2); + writer.symbol("&"); + } + + bool wrap = a->types.data[i]->as() || a->types.data[i]->as(); + + if (wrap) + writer.symbol("("); + + visualizeTypeAnnotation(*a->types.data[i]); + + if (wrap) + writer.symbol(")"); + } + } + else if (const auto& a = typeAnnotation.as()) + { + writer.symbol("("); + visualizeTypeAnnotation(*a->type); + writer.symbol(")"); + } + else if (const auto& a = typeAnnotation.as()) + { + writer.keyword(a->value ? "true" : "false"); + } + else if (const auto& a = typeAnnotation.as()) + { + writer.string(std::string_view(a->value.data, a->value.size)); + } + else if (typeAnnotation.is()) + { + writer.symbol("%error-type%"); + } + else + { + LUAU_ASSERT(!"Unknown AstType"); + } + } +}; + struct Printer { explicit Printer(Writer& writer, CstNodeMap cstNodeMap) @@ -1262,7 +2291,8 @@ struct Printer if (program.hasSemicolon) { - advanceBefore(program.location.end, 1); + if (FFlag::LuauStoreCSTData2) + advanceBefore(program.location.end, 1); writer.symbol(";"); } } @@ -1849,15 +2879,30 @@ std::string toString(AstNode* node) StringWriter writer; writer.pos = node->location.begin; - Printer printer(writer, CstNodeMap{nullptr}); - printer.writeTypes = true; + if (FFlag::LuauStoreCSTData2) + { + Printer printer(writer, CstNodeMap{nullptr}); + printer.writeTypes = true; - if (auto statNode = node->asStat()) - printer.visualize(*statNode); - else if (auto exprNode = node->asExpr()) - printer.visualize(*exprNode); - else if (auto typeNode = node->asType()) - printer.visualizeTypeAnnotation(*typeNode); + if (auto statNode = node->asStat()) + printer.visualize(*statNode); + else if (auto exprNode = node->asExpr()) + printer.visualize(*exprNode); + else if (auto typeNode = node->asType()) + printer.visualizeTypeAnnotation(*typeNode); + } + else + { + Printer_DEPRECATED printer(writer); + printer.writeTypes = true; + + if (auto statNode = node->asStat()) + printer.visualize(*statNode); + else if (auto exprNode = node->asExpr()) + printer.visualize(*exprNode); + else if (auto typeNode = node->asType()) + printer.visualizeTypeAnnotation(*typeNode); + } return writer.str(); } @@ -1870,21 +2915,38 @@ void dump(AstNode* node) std::string transpile(AstStatBlock& block, const CstNodeMap& cstNodeMap) { StringWriter writer; - Printer(writer, cstNodeMap).visualizeBlock(block); + if (FFlag::LuauStoreCSTData2) + { + Printer(writer, cstNodeMap).visualizeBlock(block); + } + else + { + Printer_DEPRECATED(writer).visualizeBlock(block); + } return writer.str(); } std::string transpileWithTypes(AstStatBlock& block, const CstNodeMap& cstNodeMap) { StringWriter writer; - Printer printer(writer, cstNodeMap); - printer.writeTypes = true; - printer.visualizeBlock(block); + if (FFlag::LuauStoreCSTData2) + { + Printer printer(writer, cstNodeMap); + printer.writeTypes = true; + printer.visualizeBlock(block); + } + else + { + Printer_DEPRECATED printer(writer); + printer.writeTypes = true; + printer.visualizeBlock(block); + } return writer.str(); } std::string transpileWithTypes(AstStatBlock& block) { + // TODO: remove this interface? return transpileWithTypes(block, CstNodeMap{nullptr}); } diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index 88ab4e53..dbe0f89b 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -27,7 +27,6 @@ LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500) LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauInstantiateInSubtyping) -LUAU_FASTFLAGVARIABLE(LuauFreeTypesMustHaveBounds) namespace Luau { diff --git a/Analysis/src/TypeArena.cpp b/Analysis/src/TypeArena.cpp index 5a46dcb7..d4e6604f 100644 --- a/Analysis/src/TypeArena.cpp +++ b/Analysis/src/TypeArena.cpp @@ -3,7 +3,6 @@ #include "Luau/TypeArena.h" LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena); -LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds) namespace Luau { diff --git a/Analysis/src/TypeAttach.cpp b/Analysis/src/TypeAttach.cpp index bc605be1..176bdb17 100644 --- a/Analysis/src/TypeAttach.cpp +++ b/Analysis/src/TypeAttach.cpp @@ -13,6 +13,7 @@ #include +LUAU_FASTFLAG(LuauStoreCSTData2) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) static char* allocateString(Luau::Allocator& allocator, std::string_view contents) @@ -307,7 +308,8 @@ public: std::optional* arg = &argNames.data[i++]; if (el) - new (arg) std::optional(AstArgumentName(AstName(el->name.c_str()), Location())); + new (arg) + std::optional(AstArgumentName(AstName(el->name.c_str()), FFlag::LuauStoreCSTData2 ? Location() : el->location)); else new (arg) std::optional(); } diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 0dfed541..3a2092f1 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -33,6 +33,7 @@ LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats) LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) +LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors) LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases) LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck) @@ -2984,10 +2985,22 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT Set > missingKeys{{}}; for (const auto& [name, prop] : expectedTableType->props) { - LUAU_ASSERT(!prop.isWriteOnly()); - auto readTy = *prop.readTy; - if (!isOptional(readTy)) - missingKeys.insert(name); + if (FFlag::LuauEnableWriteOnlyProperties) + { + if (prop.readTy) + { + if (!isOptional(*prop.readTy)) + missingKeys.insert(name); + } + } + else + { + LUAU_ASSERT(!prop.isWriteOnly()); + + auto readTy = *prop.readTy; + if (!isOptional(readTy)) + missingKeys.insert(name); + } } bool isArrayLike = false; @@ -3023,11 +3036,25 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT } else { - // TODO: What do we do for write only props? - LUAU_ASSERT(expectedIt->second.readTy); - // Some property is in the expected type: we can test against the specific type. - module->astExpectedTypes[item.value] = *expectedIt->second.readTy; - isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy); + if (FFlag::LuauEnableWriteOnlyProperties) + { + // If the type has a read type, then we have an expected type for it, otherwise, we actually don't + // care what's assigned to it because the only allowed behavior is writing to that property. + + if (expectedIt->second.readTy) + { + module->astExpectedTypes[item.value] = *expectedIt->second.readTy; + isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy); + } + } + else + { + // TODO: What do we do for write only props? + LUAU_ASSERT(expectedIt->second.readTy); + // Some property is in the expected type: we can test against the specific type. + module->astExpectedTypes[item.value] = *expectedIt->second.readTy; + isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy); + } } } else if (item.kind == AstExprTable::Item::List) diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index a55a22f7..8ca4e7d5 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -47,8 +47,8 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0 LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1); LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) @@ -283,7 +283,7 @@ struct TypeFunctionReducer } else if (is(ty)) { - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) return SkipTestResult::Generic; else return SkipTestResult::Irreducible; @@ -305,7 +305,7 @@ struct TypeFunctionReducer } else if (is(ty)) { - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) return SkipTestResult::Generic; else return SkipTestResult::Irreducible; @@ -1101,7 +1101,7 @@ TypeFunctionReductionResult unmTypeFunction( if (isPending(operandTy, ctx->solver)) return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}}; - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) operandTy = follow(operandTy); std::shared_ptr normTy = ctx->normalizer->normalize(operandTy); @@ -1698,7 +1698,7 @@ TypeFunctionReductionResult orTypeFunction( return {rhsTy, Reduction::MaybeOk, {}, {}}; // check to see if both operand types are resolved enough, and wait to reduce if not - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { if (is(lhsTy)) return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; @@ -1745,7 +1745,7 @@ static TypeFunctionReductionResult comparisonTypeFunction( if (lhsTy == instance || rhsTy == instance) return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}}; - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { if (is(lhsTy)) return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; @@ -2119,7 +2119,7 @@ TypeFunctionReductionResult refineTypeFunction( for (size_t i = 1; i < typeParams.size(); i++) discriminantTypes.push_back(follow(typeParams.at(i))); - const bool targetIsPending = FFlag::DebugLuauGreedyGeneralization + const bool targetIsPending = FFlag::LuauEagerGeneralization ? is(targetTy) : isPending(targetTy, ctx->solver); @@ -2206,7 +2206,7 @@ TypeFunctionReductionResult refineTypeFunction( if (is(target) || isSimpleDiscriminant(discriminant)) { SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { // Simplification considers free and generic types to be // 'blocking', but that's not suitable for refine<>. @@ -3335,7 +3335,7 @@ BuiltinTypeFunctions::BuiltinTypeFunctions() , ltFunc{"lt", ltTypeFunction} , leFunc{"le", leTypeFunction} , eqFunc{"eq", eqTypeFunction} - , refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::DebugLuauGreedyGeneralization} + , refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization} , singletonFunc{"singleton", singletonTypeFunction} , unionFunc{"union", unionTypeFunction} , intersectFunc{"intersect", intersectTypeFunction} diff --git a/Analysis/src/TypePath.cpp b/Analysis/src/TypePath.cpp index 74611857..95b8da05 100644 --- a/Analysis/src/TypePath.cpp +++ b/Analysis/src/TypePath.cpp @@ -14,7 +14,6 @@ #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 @@ -157,16 +156,12 @@ Path PathBuilder::build() PathBuilder& PathBuilder::readProp(std::string name) { - if (!FFlag::LuauDisableNewSolverAssertsInMixedMode) - LUAU_ASSERT(FFlag::LuauSolverV2); components.push_back(Property{std::move(name), true}); return *this; } PathBuilder& PathBuilder::writeProp(std::string name) { - 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 f9c165f8..4f38c358 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/Analysis/src/TypeUtils.cpp @@ -12,8 +12,7 @@ #include LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) -LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs) namespace Luau @@ -307,7 +306,7 @@ TypePack extendTypePack( TypePack newPack; newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) trackInteriorFreeTypePack(ftp->scope, *newPack.tail); if (FFlag::LuauSolverV2) @@ -572,8 +571,6 @@ std::vector findBlockedArgTypesIn(AstExprCall* expr, NotNullparent.get()) { if (scope->interiorFreeTypes) @@ -591,7 +588,7 @@ void trackInteriorFreeType(Scope* scope, TypeId ty) void trackInteriorFreeTypePack(Scope* scope, TypePackId tp) { LUAU_ASSERT(tp); - if (!FFlag::LuauNonReentrantGeneralization3) + if (!FFlag::LuauEagerGeneralization) return; for (; scope; scope = scope->parent.get()) diff --git a/Analysis/src/Unifier2.cpp b/Analysis/src/Unifier2.cpp index 7a51d8be..f9bf17b8 100644 --- a/Analysis/src/Unifier2.cpp +++ b/Analysis/src/Unifier2.cpp @@ -18,8 +18,8 @@ #include LUAU_FASTINT(LuauTypeInferRecursionLimit) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) +LUAU_FASTFLAG(LuauEagerGeneralization) namespace Luau { @@ -329,12 +329,12 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn) for (TypePackId genericPack : subFn->genericPacks) { - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) genericPack = follow(genericPack); - // TODO: Clip this follow() with DebugLuauGreedyGeneralization + // TODO: Clip this follow() with LuauEagerGeneralization const GenericTypePack* gen = get(follow(genericPack)); if (gen) genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity); @@ -413,16 +413,27 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable) { const Property& superProp = superPropOpt->second; - if (subProp.isReadOnly() && superProp.isReadOnly()) - result &= unify(*subProp.readTy, *superPropOpt->second.readTy); - else if (subProp.isReadOnly()) - result &= unify(*subProp.readTy, superProp.type()); - else if (superProp.isReadOnly()) - result &= unify(subProp.type(), *superProp.readTy); + if (FFlag::LuauEnableWriteOnlyProperties) + { + if (subProp.readTy && superProp.readTy) + result &= unify(*subProp.readTy, *superProp.readTy); + + if (subProp.writeTy && superProp.writeTy) + result &= unify(*superProp.writeTy, *subProp.writeTy); + } else { - result &= unify(subProp.type(), superProp.type()); - result &= unify(superProp.type(), subProp.type()); + if (subProp.isReadOnly() && superProp.isReadOnly()) + result &= unify(*subProp.readTy, *superPropOpt->second.readTy); + else if (subProp.isReadOnly()) + result &= unify(*subProp.readTy, superProp.type()); + else if (superProp.isReadOnly()) + result &= unify(subProp.type(), *superProp.readTy); + else + { + result &= unify(subProp.type(), superProp.type()); + result &= unify(superProp.type(), subProp.type()); + } } } } @@ -454,7 +465,7 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable) { result &= unify(subTable->indexer->indexType, superTable->indexer->indexType); result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType); - if (FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauEagerGeneralization) { // FIXME: We can probably do something more efficient here. result &= unify(superTable->indexer->indexType, subTable->indexer->indexType); diff --git a/Ast/include/Luau/Parser.h b/Ast/include/Luau/Parser.h index e7629606..52dd76ea 100644 --- a/Ast/include/Luau/Parser.h +++ b/Ast/include/Luau/Parser.h @@ -245,6 +245,8 @@ private: }; TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional accessLocation, Lexeme begin); + // Remove with FFlagLuauStoreCSTData2 + AstTableIndexer* parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional accessLocation, Lexeme begin); AstTypeOrPack parseFunctionType(bool allowPack, const AstArray& attributes); AstType* parseFunctionTypeTail( diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 51053f7a..ccfebf19 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -18,10 +18,12 @@ 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(LuauStoreCSTData2) LUAU_FASTFLAGVARIABLE(LuauDeclareExternType) LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer) LUAU_FASTFLAGVARIABLE(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauStoreLocalAnnotationColonPositions) +LUAU_FASTFLAGVARIABLE(LuauCSTForReturnTypeFunctionTail) LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false) // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix @@ -522,10 +524,17 @@ AstStat* Parser::parseRepeat() restoreLocals(localsBegin); - AstStatRepeat* node = allocator.alloc(Location(start, cond->location), cond, body, hasUntil); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(untilPosition); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstStatRepeat* node = allocator.alloc(Location(start, cond->location), cond, body, hasUntil); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(untilPosition); + return node; + } + else + { + return allocator.alloc(Location(start, cond->location), cond, body, hasUntil); + } } // do block end @@ -545,7 +554,7 @@ AstStat* Parser::parseDo() if (body->hasEnd) body->location.end = endLocation.end; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstNodeMap[body] = allocator.alloc(endLocation.begin); return body; @@ -628,11 +637,18 @@ AstStat* Parser::parseFor() bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo); body->hasEnd = hasEnd; - AstStatFor* node = allocator.alloc(Location(start, end), var, from, to, step, body, hasDo, matchDo.location); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(varname.colonPosition, equalsPosition, endCommaPosition, stepCommaPosition); + if (FFlag::LuauStoreCSTData2) + { + AstStatFor* node = allocator.alloc(Location(start, end), var, from, to, step, body, hasDo, matchDo.location); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(varname.colonPosition, equalsPosition, endCommaPosition, stepCommaPosition); - return node; + return node; + } + else + { + return allocator.alloc(Location(start, end), var, from, to, step, body, hasDo, matchDo.location); + } } else { @@ -642,7 +658,7 @@ AstStat* Parser::parseFor() if (lexer.current().type == ',') { - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) { Position initialCommaPosition = lexer.current().location.begin; nextLexeme(); @@ -661,7 +677,7 @@ AstStat* Parser::parseFor() TempVector values(scratchExpr); TempVector valuesCommaPositions(scratchPosition); - parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr); + parseExprList(values, (FFlag::LuauStoreCSTData2 && options.storeCstData) ? &valuesCommaPositions : nullptr); Lexeme matchDo = lexer.current(); bool hasDo = expectAndConsume(Lexeme::ReservedDo, "for loop"); @@ -686,17 +702,23 @@ AstStat* Parser::parseFor() bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo); body->hasEnd = hasEnd; - AstStatForIn* node = - allocator.alloc(Location(start, end), copy(vars), copy(values), body, hasIn, inLocation, hasDo, matchDo.location); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2) { - if (FFlag::LuauStoreLocalAnnotationColonPositions) - cstNodeMap[node] = - allocator.alloc(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions)); - else - cstNodeMap[node] = allocator.alloc(AstArray{}, varsCommaPosition, copy(valuesCommaPositions)); + AstStatForIn* node = + allocator.alloc(Location(start, end), copy(vars), copy(values), body, hasIn, inLocation, hasDo, matchDo.location); + if (options.storeCstData) + { + if (FFlag::LuauStoreLocalAnnotationColonPositions) + cstNodeMap[node] = allocator.alloc(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions)); + else + cstNodeMap[node] = allocator.alloc(AstArray{}, varsCommaPosition, copy(valuesCommaPositions)); + } + return node; + } + else + { + return allocator.alloc(Location(start, end), copy(vars), copy(values), body, hasIn, inLocation, hasDo, matchDo.location); } - return node; } } @@ -771,10 +793,17 @@ AstStat* Parser::parseFunctionStat(const AstArray& attributes) matchRecoveryStopOnToken[Lexeme::ReservedEnd]--; - AstStatFunction* node = allocator.alloc(Location(start, body->location), expr, body); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(matchFunction.location.begin); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstStatFunction* node = allocator.alloc(Location(start, body->location), expr, body); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(matchFunction.location.begin); + return node; + } + else + { + return allocator.alloc(Location(start, body->location), expr, body); + } } @@ -917,10 +946,17 @@ AstStat* Parser::parseLocal(const AstArray& attributes) Location location{start.begin, body->location.end}; - AstStatLocalFunction* node = allocator.alloc(location, var, body); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(localKeywordPosition, functionKeywordPosition); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstStatLocalFunction* node = allocator.alloc(location, var, body); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(localKeywordPosition, functionKeywordPosition); + return node; + } + else + { + return allocator.alloc(location, var, body); + } } else { @@ -939,7 +975,7 @@ AstStat* Parser::parseLocal(const AstArray& attributes) TempVector names(scratchBinding); AstArray varsCommaPositions; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) parseBindingList(names, false, &varsCommaPositions); else parseBindingList(names); @@ -959,7 +995,7 @@ AstStat* Parser::parseLocal(const AstArray& attributes) nextLexeme(); - parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr); + parseExprList(values, (FFlag::LuauStoreCSTData2 && options.storeCstData) ? &valuesCommaPositions : nullptr); } for (size_t i = 0; i < names.size(); ++i) @@ -967,17 +1003,23 @@ AstStat* Parser::parseLocal(const AstArray& attributes) Location end = values.empty() ? lexer.previousLocation() : values.back()->location; - AstStatLocal* node = allocator.alloc(Location(start, end), copy(vars), copy(values), equalsSignLocation); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2) { - if (FFlag::LuauStoreLocalAnnotationColonPositions) - cstNodeMap[node] = - allocator.alloc(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions)); - else - cstNodeMap[node] = allocator.alloc(AstArray{}, varsCommaPositions, copy(valuesCommaPositions)); - } + AstStatLocal* node = allocator.alloc(Location(start, end), copy(vars), copy(values), equalsSignLocation); + if (options.storeCstData) + { + if (FFlag::LuauStoreLocalAnnotationColonPositions) + cstNodeMap[node] = allocator.alloc(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions)); + else + cstNodeMap[node] = allocator.alloc(AstArray{}, varsCommaPositions, copy(valuesCommaPositions)); + } - return node; + return node; + } + else + { + return allocator.alloc(Location(start, end), copy(vars), copy(values), equalsSignLocation); + } } } @@ -992,14 +1034,21 @@ AstStat* Parser::parseReturn() TempVector commaPositions(scratchPosition); if (!blockFollow(lexer.current()) && lexer.current().type != ';') - parseExprList(list, options.storeCstData ? &commaPositions : nullptr); + parseExprList(list, (FFlag::LuauStoreCSTData2 && options.storeCstData) ? &commaPositions : nullptr); Location end = list.empty() ? start : list.back()->location; - AstStatReturn* node = allocator.alloc(Location(start, end), copy(list)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(copy(commaPositions)); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstStatReturn* node = allocator.alloc(Location(start, end), copy(list)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(commaPositions)); + return node; + } + else + { + return allocator.alloc(Location(start, end), copy(list)); + } } // type Name [`<' varlist `>'] `=' Type @@ -1022,7 +1071,7 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported, Position t Position genericsOpenPosition{0, 0}; AstArray genericsCommaPositions; Position genericsClosePosition{0, 0}; - auto [generics, genericPacks] = options.storeCstData + auto [generics, genericPacks] = FFlag::LuauStoreCSTData2 && options.storeCstData ? parseGenericTypeList( /* withDefaultValues= */ true, &genericsOpenPosition, &genericsCommaPositions, &genericsClosePosition ) @@ -1033,13 +1082,20 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported, Position t AstType* type = parseType(); - AstStatTypeAlias* node = - allocator.alloc(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc( - typeKeywordPosition, genericsOpenPosition, genericsCommaPositions, genericsClosePosition, equalsPosition - ); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstStatTypeAlias* node = + allocator.alloc(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc( + typeKeywordPosition, genericsOpenPosition, genericsCommaPositions, genericsClosePosition, equalsPosition + ); + return node; + } + else + { + return allocator.alloc(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported); + } } // type function Name `(' arglist `)' `=' funcbody `end' @@ -1071,11 +1127,18 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Positio bool hasErrors = parseErrors.size() > errorsAtStart; - AstStatTypeFunction* node = - allocator.alloc(Location(start, body->location), fnName->name, fnName->location, body, exported, hasErrors); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(typeKeywordPosition, matchFn.location.begin); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstStatTypeFunction* node = + allocator.alloc(Location(start, body->location), fnName->name, fnName->location, body, exported, hasErrors); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(typeKeywordPosition, matchFn.location.begin); + return node; + } + else + { + return allocator.alloc(Location(start, body->location), fnName->name, fnName->location, body, exported, hasErrors); + } } AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArray& attributes) @@ -1360,7 +1423,10 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray values(scratchExprAux); TempVector valuesCommaPositions(scratchPosition); - parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr); + parseExprList(values, FFlag::LuauStoreCSTData2 && options.storeCstData ? &valuesCommaPositions : nullptr); - AstStatAssign* node = allocator.alloc(Location(initial->location, values.back()->location), copy(vars), copy(values)); - cstNodeMap[node] = allocator.alloc(copy(varsCommaPositions), equalsPosition, copy(valuesCommaPositions)); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstStatAssign* node = allocator.alloc(Location(initial->location, values.back()->location), copy(vars), copy(values)); + cstNodeMap[node] = allocator.alloc(copy(varsCommaPositions), equalsPosition, copy(valuesCommaPositions)); + return node; + } + else + { + return allocator.alloc(Location(initial->location, values.back()->location), copy(vars), copy(values)); + } } // var [`+=' | `-=' | `*=' | `/=' | `%=' | `^=' | `..='] exp @@ -1537,10 +1623,17 @@ AstStat* Parser::parseCompoundAssignment(AstExpr* initial, AstExprBinary::Op op) AstExpr* value = parseExpr(); - AstStatCompoundAssign* node = allocator.alloc(Location(initial->location, value->location), op, initial, value); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(opPosition); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstStatCompoundAssign* node = allocator.alloc(Location(initial->location, value->location), op, initial, value); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(opPosition); + return node; + } + else + { + return allocator.alloc(Location(initial->location, value->location), op, initial, value); + } } std::pair> Parser::prepareFunctionArguments(const Location& start, bool hasself, const TempVector& args) @@ -1575,10 +1668,10 @@ std::pair Parser::parseFunctionBody( if (attributes.size > 0) start = attributes.data[0]->location; - auto* cstNode = options.storeCstData ? allocator.alloc() : nullptr; + auto* cstNode = FFlag::LuauStoreCSTData2 && options.storeCstData ? allocator.alloc() : nullptr; auto [generics, genericPacks] = - cstNode + FFlag::LuauStoreCSTData2 && cstNode ? parseGenericTypeList( /* withDefaultValues= */ false, &cstNode->openGenericsPosition, &cstNode->genericsCommaPositions, &cstNode->closeGenericsPosition ) @@ -1656,31 +1749,56 @@ std::pair Parser::parseFunctionBody( bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchFunction); body->hasEnd = hasEnd; - AstExprFunction* node = allocator.alloc( - Location(start, end), - attributes, - generics, - genericPacks, - self, - vars, - vararg, - varargLocation, - body, - functionStack.size(), - debugname, - typelist, - varargAnnotation, - argLocation - ); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2) { - cstNode->functionKeywordPosition = matchFunction.location.begin; - if (FFlag::LuauStoreLocalAnnotationColonPositions) - cstNode->argsAnnotationColonPositions = extractAnnotationColonPositions(args); - cstNodeMap[node] = cstNode; - } + AstExprFunction* node = allocator.alloc( + Location(start, end), + attributes, + generics, + genericPacks, + self, + vars, + vararg, + varargLocation, + body, + functionStack.size(), + debugname, + typelist, + varargAnnotation, + argLocation + ); + if (options.storeCstData) + { + cstNode->functionKeywordPosition = matchFunction.location.begin; + if (FFlag::LuauStoreLocalAnnotationColonPositions) + cstNode->argsAnnotationColonPositions = extractAnnotationColonPositions(args); + cstNodeMap[node] = cstNode; + } - return {node, funLocal}; + return {node, funLocal}; + } + else + { + return { + allocator.alloc( + Location(start, end), + attributes, + generics, + genericPacks, + self, + vars, + vararg, + varargLocation, + body, + functionStack.size(), + debugname, + typelist, + varargAnnotation, + argLocation + ), + funLocal + }; + } } std::pair Parser::parseFunctionBody_DEPRECATED( @@ -1698,10 +1816,10 @@ std::pair Parser::parseFunctionBody_DEPRECATED( if (attributes.size > 0) start = attributes.data[0]->location; - auto* cstNode = options.storeCstData ? allocator.alloc() : nullptr; + auto* cstNode = FFlag::LuauStoreCSTData2 && options.storeCstData ? allocator.alloc() : nullptr; auto [generics, genericPacks] = - cstNode + FFlag::LuauStoreCSTData2 && cstNode ? parseGenericTypeList( /* withDefaultValues= */ false, &cstNode->openGenericsPosition, &cstNode->genericsCommaPositions, &cstNode->closeGenericsPosition ) @@ -1768,29 +1886,54 @@ std::pair Parser::parseFunctionBody_DEPRECATED( bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchFunction); body->hasEnd = hasEnd; - AstExprFunction* node = allocator.alloc( - Location(start, end), - attributes, - generics, - genericPacks, - self, - vars, - vararg, - varargLocation, - body, - functionStack.size(), - debugname, - typelist, - varargAnnotation, - argLocation - ); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2) { - cstNode->functionKeywordPosition = matchFunction.location.begin; - cstNodeMap[node] = cstNode; - } + AstExprFunction* node = allocator.alloc( + Location(start, end), + attributes, + generics, + genericPacks, + self, + vars, + vararg, + varargLocation, + body, + functionStack.size(), + debugname, + typelist, + varargAnnotation, + argLocation + ); + if (options.storeCstData) + { + cstNode->functionKeywordPosition = matchFunction.location.begin; + cstNodeMap[node] = cstNode; + } - return {node, funLocal}; + return {node, funLocal}; + } + else + { + return { + allocator.alloc( + Location(start, end), + attributes, + generics, + genericPacks, + self, + vars, + vararg, + varargLocation, + body, + functionStack.size(), + debugname, + typelist, + varargAnnotation, + argLocation + ), + funLocal + }; + } } // explist ::= {exp `,'} exp @@ -1800,7 +1943,7 @@ void Parser::parseExprList(TempVector& result, TempVector* c while (lexer.current().type == ',') { - if (commaPositions) + if (FFlag::LuauStoreCSTData2 && commaPositions) commaPositions->push_back(lexer.current().location.begin); nextLexeme(); @@ -1851,7 +1994,7 @@ std::tuple Parser::parseBindingList( { TempVector localCommaPositions(scratchPosition); - if (commaPositions && initialCommaPosition) + if (FFlag::LuauStoreCSTData2 && commaPositions && initialCommaPosition) localCommaPositions.push_back(*initialCommaPosition); while (true) @@ -1871,7 +2014,7 @@ std::tuple Parser::parseBindingList( tailAnnotation = parseVariadicArgumentTypePack(); } - if (commaPositions) + if (FFlag::LuauStoreCSTData2 && commaPositions) *commaPositions = copy(localCommaPositions); return {true, varargLocation, tailAnnotation}; @@ -1881,12 +2024,12 @@ std::tuple Parser::parseBindingList( if (lexer.current().type != ',') break; - if (commaPositions) + if (FFlag::LuauStoreCSTData2 && commaPositions) localCommaPositions.push_back(lexer.current().location.begin); nextLexeme(); } - if (commaPositions) + if (FFlag::LuauStoreCSTData2 && commaPositions) *commaPositions = copy(localCommaPositions); return {false, Location(), nullptr}; @@ -1921,7 +2064,7 @@ AstTypePack* Parser::parseTypeList( // Fill in previous argument names with empty slots while (resultNames.size() < result.size()) resultNames.push_back({}); - if (nameColonPositions) + if (FFlag::LuauStoreCSTData2 && nameColonPositions) { while (nameColonPositions->size() < result.size()) nameColonPositions->push_back({}); @@ -1930,7 +2073,7 @@ AstTypePack* Parser::parseTypeList( resultNames.push_back(AstArgumentName{AstName(lexer.current().name), lexer.current().location}); nextLexeme(); - if (nameColonPositions) + if (FFlag::LuauStoreCSTData2 && nameColonPositions) nameColonPositions->push_back(lexer.current().location.begin); expectAndConsume(':'); } @@ -1938,7 +2081,7 @@ AstTypePack* Parser::parseTypeList( { // If we have a type with named arguments, provide elements for all types resultNames.push_back({}); - if (nameColonPositions) + if (FFlag::LuauStoreCSTData2 && nameColonPositions) nameColonPositions->push_back({}); } @@ -1946,7 +2089,7 @@ AstTypePack* Parser::parseTypeList( if (lexer.current().type != ',') break; - if (commaPositions) + if (FFlag::LuauStoreCSTData2 && commaPositions) commaPositions->push_back(lexer.current().location.begin); nextLexeme(); @@ -1968,7 +2111,7 @@ AstTypePack* Parser::parseOptionalReturnType(Position* returnSpecifierPosition) if (lexer.current().type == Lexeme::SkinnyArrow) report(lexer.current().location, "Function return type annotations are written after ':' instead of '->'"); - if (returnSpecifierPosition) + if (FFlag::LuauStoreCSTData2 && returnSpecifierPosition) *returnSpecifierPosition = lexer.current().location.begin; nextLexeme(); @@ -2002,7 +2145,7 @@ std::optional Parser::parseOptionalReturnType_DEPRECATED(Luau::Posi if (lexer.current().type == Lexeme::SkinnyArrow) report(lexer.current().location, "Function return type annotations are written after ':' instead of '->'"); - if (returnSpecifierPosition) + if (FFlag::LuauStoreCSTData2 && returnSpecifierPosition) *returnSpecifierPosition = lexer.current().location.begin; nextLexeme(); @@ -2058,12 +2201,15 @@ AstTypePack* Parser::parseReturnType() TempVector result(scratchType); TempVector> resultNames(scratchOptArgName); TempVector commaPositions(scratchPosition); + TempVector> nameColonPositions(scratchOptPosition); AstTypePack* varargAnnotation = nullptr; // possibly () -> ReturnType if (lexer.current().type != ')') { - if (options.storeCstData) + if (FFlag::LuauCSTForReturnTypeFunctionTail && options.storeCstData) + varargAnnotation = parseTypeList(result, resultNames, &commaPositions, &nameColonPositions); + else if (options.storeCstData) varargAnnotation = parseTypeList(result, resultNames, &commaPositions); else varargAnnotation = parseTypeList(result, resultNames); @@ -2105,8 +2251,23 @@ AstTypePack* Parser::parseReturnType() return node; } + Position returnArrowPosition = lexer.current().location.begin; AstType* tail = parseFunctionTypeTail(begin, {nullptr, 0}, {}, {}, copy(result), copy(resultNames), varargAnnotation); + if (FFlag::LuauCSTForReturnTypeFunctionTail && options.storeCstData && tail->is()) + { + cstNodeMap[tail] = allocator.alloc( + Position{0, 0}, + AstArray{}, + Position{0, 0}, + location.begin, + copy(nameColonPositions), + copy(commaPositions), + closeParenthesesPosition, + returnArrowPosition + ); + } + AstTypePackExplicit* node = allocator.alloc(Location{location, tail->location}, AstTypeList{copy(&tail, 1), nullptr}); if (options.storeCstData) cstNodeMap[node] = allocator.alloc(); @@ -2186,6 +2347,8 @@ std::pair Parser::parseReturnType_DEPRECATED() std::pair Parser::extractStringDetails() { + LUAU_ASSERT(FFlag::LuauStoreCSTData2); + CstExprConstantString::QuoteStyle style; unsigned int blockDepth = 0; @@ -2238,6 +2401,26 @@ Parser::TableIndexerResult Parser::parseTableIndexer(AstTableAccess access, std: }; } +// Remove with FFlagLuauStoreCSTData2 +AstTableIndexer* Parser::parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional accessLocation, Lexeme begin) +{ + if (!FFlag::LuauParseStringIndexer) + { + begin = lexer.current(); + nextLexeme(); // [ + } + + AstType* index = parseType(); + + expectMatchAndConsume(']', begin); + + expectAndConsume(':', "table field"); + + AstType* result = parseType(); + + return allocator.alloc(AstTableIndexer{index, result, Location(begin.location, result->location), access, accessLocation}); +} + // TableProp ::= Name `:' Type // TablePropOrIndexer ::= TableProp | TableIndexer // PropList ::= TablePropOrIndexer {fieldsep TablePropOrIndexer} [fieldsep] @@ -2289,7 +2472,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) { CstExprConstantString::QuoteStyle style; unsigned int blockDepth = 0; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) std::tie(style, blockDepth) = extractStringDetails(); Position stringPosition = lexer.current().location.begin; @@ -2309,7 +2492,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) if (chars && !containsNull) { props.push_back(AstTableProp{AstName(chars->data), begin.location, type, access, accessLocation}); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstItems.push_back(CstTypeTable::Item{ CstTypeTable::Item::Kind::StringProperty, begin.location.begin, @@ -2330,24 +2513,35 @@ AstType* Parser::parseTableType(bool inDeclarationContext) { // maybe we don't need to parse the entire badIndexer... // however, we either have { or [ to lint, not the entire table type or the bad indexer. - AstTableIndexer* badIndexer = parseTableIndexer(access, accessLocation, begin).node; + AstTableIndexer* badIndexer; + if (FFlag::LuauStoreCSTData2) + badIndexer = parseTableIndexer(access, accessLocation, begin).node; + else + badIndexer = parseTableIndexer_DEPRECATED(access, accessLocation, begin); // we lose all additional indexer expressions from the AST after error recovery here report(badIndexer->location, "Cannot have more than one table indexer"); } else { - auto tableIndexerResult = parseTableIndexer(access, accessLocation, begin); - indexer = tableIndexerResult.node; - if (options.storeCstData) - cstItems.push_back(CstTypeTable::Item{ - CstTypeTable::Item::Kind::Indexer, - tableIndexerResult.indexerOpenPosition, - tableIndexerResult.indexerClosePosition, - tableIndexerResult.colonPosition, - tableSeparator(), - lexer.current().location.begin, - }); + if (FFlag::LuauStoreCSTData2) + { + auto tableIndexerResult = parseTableIndexer(access, accessLocation, begin); + indexer = tableIndexerResult.node; + if (options.storeCstData) + cstItems.push_back(CstTypeTable::Item{ + CstTypeTable::Item::Kind::Indexer, + tableIndexerResult.indexerOpenPosition, + tableIndexerResult.indexerClosePosition, + tableIndexerResult.colonPosition, + tableSeparator(), + lexer.current().location.begin, + }); + } + else + { + indexer = parseTableIndexer_DEPRECATED(access, accessLocation, begin); + } } } } @@ -2375,7 +2569,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) AstType* type = parseType(inDeclarationContext); props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation}); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstItems.push_back(CstTypeTable::Item{ CstTypeTable::Item::Kind::Property, Position{0, 0}, @@ -2395,7 +2589,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) CstExprConstantString::QuoteStyle style; unsigned int blockDepth = 0; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) std::tie(style, blockDepth) = extractStringDetails(); Position stringPosition = lexer.current().location.begin; @@ -2415,7 +2609,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) if (chars && !containsNull) { props.push_back(AstTableProp{AstName(chars->data), begin.location, type, access, accessLocation}); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstItems.push_back(CstTypeTable::Item{ CstTypeTable::Item::Kind::StringProperty, begin.location.begin, @@ -2436,26 +2630,39 @@ AstType* Parser::parseTableType(bool inDeclarationContext) { // maybe we don't need to parse the entire badIndexer... // however, we either have { or [ to lint, not the entire table type or the bad indexer. - // the last param in the parseTableIndexer is ignored - AstTableIndexer* badIndexer = parseTableIndexer(access, accessLocation, lexer.current()).node; + AstTableIndexer* badIndexer; + if (FFlag::LuauStoreCSTData2) + // the last param in the parseTableIndexer is ignored + badIndexer = parseTableIndexer(access, accessLocation, lexer.current()).node; + else + // the last param in the parseTableIndexer is ignored + badIndexer = parseTableIndexer_DEPRECATED(access, accessLocation, lexer.current()); // we lose all additional indexer expressions from the AST after error recovery here report(badIndexer->location, "Cannot have more than one table indexer"); } else { - // the last param in the parseTableIndexer is ignored - auto tableIndexerResult = parseTableIndexer(access, accessLocation, lexer.current()); - indexer = tableIndexerResult.node; - if (options.storeCstData) - cstItems.push_back(CstTypeTable::Item{ - CstTypeTable::Item::Kind::Indexer, - tableIndexerResult.indexerOpenPosition, - tableIndexerResult.indexerClosePosition, - tableIndexerResult.colonPosition, - tableSeparator(), - lexer.current().location.begin, - }); + if (FFlag::LuauStoreCSTData2) + { + // the last param in the parseTableIndexer is ignored + auto tableIndexerResult = parseTableIndexer(access, accessLocation, lexer.current()); + indexer = tableIndexerResult.node; + if (options.storeCstData) + cstItems.push_back(CstTypeTable::Item{ + CstTypeTable::Item::Kind::Indexer, + tableIndexerResult.indexerOpenPosition, + tableIndexerResult.indexerClosePosition, + tableIndexerResult.colonPosition, + tableSeparator(), + lexer.current().location.begin, + }); + } + else + { + // the last param in the parseTableIndexer is ignored + indexer = parseTableIndexer_DEPRECATED(access, accessLocation, lexer.current()); + } } } else if (props.empty() && !indexer && !(lexer.current().type == Lexeme::Name && lexer.lookahead().type == ':')) @@ -2482,7 +2689,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) AstType* type = parseType(inDeclarationContext); props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation}); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstItems.push_back(CstTypeTable::Item{ CstTypeTable::Item::Kind::Property, Position{0, 0}, @@ -2510,10 +2717,17 @@ AstType* Parser::parseTableType(bool inDeclarationContext) if (!expectMatchAndConsume('}', matchBrace, /* searchForMissing = */ true)) end = lexer.previousLocation(); - AstTypeTable* node = allocator.alloc(Location(start, end), copy(props), indexer); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(copy(cstItems), isArray); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstTypeTable* node = allocator.alloc(Location(start, end), copy(props), indexer); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(cstItems), isArray); + return node; + } + else + { + return allocator.alloc(Location(start, end), copy(props), indexer); + } } // ReturnType ::= Type | `(' TypeList `)' @@ -2529,7 +2743,7 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray Position genericsOpenPosition{0, 0}; AstArray genericsCommaPositions; Position genericsClosePosition{0, 0}; - auto [generics, genericPacks] = options.storeCstData + auto [generics, genericPacks] = FFlag::LuauStoreCSTData2 && options.storeCstData ? parseGenericTypeList( /* withDefaultValues= */ false, &genericsOpenPosition, &genericsCommaPositions, &genericsClosePosition ) @@ -2549,7 +2763,7 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray if (lexer.current().type != ')') { - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) varargAnnotation = parseTypeList(params, names, &argCommaPositions, &nameColonPositions); else varargAnnotation = parseTypeList(params, names); @@ -2572,11 +2786,18 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray { if (allowPack) { - AstTypePackExplicit* node = allocator.alloc(begin.location, AstTypeList{paramTypes, nullptr}); - if (options.storeCstData) - cstNodeMap[node] = - allocator.alloc(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions)); - return {{}, node}; + if (FFlag::LuauStoreCSTData2) + { + AstTypePackExplicit* node = allocator.alloc(begin.location, AstTypeList{paramTypes, nullptr}); + if (options.storeCstData) + cstNodeMap[node] = + allocator.alloc(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions)); + return {{}, node}; + } + else + { + return {{}, allocator.alloc(begin.location, AstTypeList{paramTypes, nullptr})}; + } } else { @@ -2586,30 +2807,45 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray if (!forceFunctionType && !returnTypeIntroducer && allowPack) { - AstTypePackExplicit* node = allocator.alloc(begin.location, AstTypeList{paramTypes, varargAnnotation}); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions)); - return {{}, node}; + if (FFlag::LuauStoreCSTData2) + { + AstTypePackExplicit* node = allocator.alloc(begin.location, AstTypeList{paramTypes, varargAnnotation}); + if (options.storeCstData) + cstNodeMap[node] = + allocator.alloc(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions)); + return {{}, node}; + } + else + { + return {{}, allocator.alloc(begin.location, AstTypeList{paramTypes, varargAnnotation})}; + } } AstArray> paramNames = copy(names); - Position returnArrowPosition = lexer.current().location.begin; - AstType* node = parseFunctionTypeTail(begin, attributes, generics, genericPacks, paramTypes, paramNames, varargAnnotation); - if (options.storeCstData && node->is()) + if (FFlag::LuauStoreCSTData2) { - cstNodeMap[node] = allocator.alloc( - genericsOpenPosition, - genericsCommaPositions, - genericsClosePosition, - parameterStart.location.begin, - copy(nameColonPositions), - copy(argCommaPositions), - closeArgsLocation.begin, - returnArrowPosition - ); + Position returnArrowPosition = lexer.current().location.begin; + AstType* node = parseFunctionTypeTail(begin, attributes, generics, genericPacks, paramTypes, paramNames, varargAnnotation); + if (options.storeCstData && node->is()) + { + cstNodeMap[node] = allocator.alloc( + genericsOpenPosition, + genericsCommaPositions, + genericsClosePosition, + parameterStart.location.begin, + copy(nameColonPositions), + copy(argCommaPositions), + closeArgsLocation.begin, + returnArrowPosition + ); + } + return {node, {}}; + } + else + { + return {parseFunctionTypeTail(begin, attributes, generics, genericPacks, paramTypes, paramNames, varargAnnotation), {}}; } - return {node, {}}; } AstType* Parser::parseFunctionTypeTail( @@ -2704,7 +2940,7 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin) isUnion = true; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) { if (type == nullptr && !leadingPosition.has_value()) leadingPosition = separatorPosition; @@ -2734,7 +2970,7 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin) isIntersection = true; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) { if (type == nullptr && !leadingPosition.has_value()) leadingPosition = separatorPosition; @@ -2767,20 +3003,31 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin) location.end = parts.back()->location.end; - if (isUnion) + if (FFlag::LuauStoreCSTData2) { - AstTypeUnion* node = allocator.alloc(location, copy(parts)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(leadingPosition, copy(separatorPositions)); - return node; - } + if (isUnion) + { + AstTypeUnion* node = allocator.alloc(location, copy(parts)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(leadingPosition, copy(separatorPositions)); + return node; + } - if (isIntersection) + if (isIntersection) + { + AstTypeIntersection* node = allocator.alloc(location, copy(parts)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(leadingPosition, copy(separatorPositions)); + return node; + } + } + else { - AstTypeIntersection* node = allocator.alloc(location, copy(parts)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(leadingPosition, copy(separatorPositions)); - return node; + if (isUnion) + return allocator.alloc(location, copy(parts)); + + if (isIntersection) + return allocator.alloc(location, copy(parts)); } LUAU_ASSERT(false); @@ -2868,22 +3115,35 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) } else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) { - CstExprConstantString::QuoteStyle style; - unsigned int blockDepth = 0; - if (options.storeCstData) - std::tie(style, blockDepth) = extractStringDetails(); - - AstArray originalString; - if (std::optional> value = parseCharArray(options.storeCstData ? &originalString : nullptr)) + if (FFlag::LuauStoreCSTData2) { - AstArray svalue = *value; - auto node = allocator.alloc(start, svalue); + CstExprConstantString::QuoteStyle style; + unsigned int blockDepth = 0; if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(originalString, style, blockDepth); - return {node}; + std::tie(style, blockDepth) = extractStringDetails(); + + AstArray originalString; + if (std::optional> value = parseCharArray(options.storeCstData ? &originalString : nullptr)) + { + AstArray svalue = *value; + auto node = allocator.alloc(start, svalue); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(originalString, style, blockDepth); + return {node}; + } + else + return {reportTypeError(start, {}, "String literal contains malformed escape sequence")}; } else - return {reportTypeError(start, {}, "String literal contains malformed escape sequence")}; + { + if (std::optional> value = parseCharArray()) + { + AstArray svalue = *value; + return {allocator.alloc(start, svalue)}; + } + else + return {reportTypeError(start, {}, "String literal contains malformed escape sequence")}; + } } else if (lexer.current().type == Lexeme::InterpStringBegin || lexer.current().type == Lexeme::InterpStringSimple) { @@ -2905,12 +3165,24 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) if (lexer.current().type == '.') { - prefixPointPosition = lexer.current().location.begin; - nextLexeme(); + if (FFlag::LuauStoreCSTData2) + { + prefixPointPosition = lexer.current().location.begin; + nextLexeme(); - prefix = name.name; - prefixLocation = name.location; - name = parseIndexName("field name", *prefixPointPosition); + prefix = name.name; + prefixLocation = name.location; + name = parseIndexName("field name", *prefixPointPosition); + } + else + { + Position pointPosition = lexer.current().location.begin; + nextLexeme(); + + prefix = name.name; + prefixLocation = name.location; + name = parseIndexName("field name", pointPosition); + } } else if (lexer.current().type == Lexeme::Dot3) { @@ -2928,10 +3200,17 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) expectMatchAndConsume(')', typeofBegin); - AstTypeTypeof* node = allocator.alloc(Location(start, end), expr); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(typeofBegin.location.begin, end.begin); - return {node, {}}; + if (FFlag::LuauStoreCSTData2) + { + AstTypeTypeof* node = allocator.alloc(Location(start, end), expr); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(typeofBegin.location.begin, end.begin); + return {node, {}}; + } + else + { + return {allocator.alloc(Location(start, end), expr), {}}; + } } bool hasParameters = false; @@ -2943,7 +3222,7 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) if (lexer.current().type == '<') { hasParameters = true; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) parameters = parseTypeParams(¶metersOpeningPosition, ¶metersCommaPositions, ¶metersClosingPosition); else parameters = parseTypeParams(); @@ -2951,13 +3230,23 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) Location end = lexer.previousLocation(); - AstTypeReference* node = - allocator.alloc(Location(start, end), prefix, name.name, prefixLocation, name.location, hasParameters, parameters); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc( - prefixPointPosition, parametersOpeningPosition, copy(parametersCommaPositions), parametersClosingPosition - ); - return {node, {}}; + if (FFlag::LuauStoreCSTData2) + { + AstTypeReference* node = + allocator.alloc(Location(start, end), prefix, name.name, prefixLocation, name.location, hasParameters, parameters); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc( + prefixPointPosition, parametersOpeningPosition, copy(parametersCommaPositions), parametersClosingPosition + ); + return {node, {}}; + } + else + { + return { + allocator.alloc(Location(start, end), prefix, name.name, prefixLocation, name.location, hasParameters, parameters), + {} + }; + } } else if (lexer.current().type == '{') { @@ -3002,10 +3291,17 @@ AstTypePack* Parser::parseVariadicArgumentTypePack() // This will not fail because of the lookahead guard. expectAndConsume(Lexeme::Dot3, "generic type pack annotation"); - AstTypePackGeneric* node = allocator.alloc(Location(name.location, end), name.name); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(end.begin); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstTypePackGeneric* node = allocator.alloc(Location(name.location, end), name.name); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(end.begin); + return node; + } + else + { + return allocator.alloc(Location(name.location, end), name.name); + } } // Variadic: T else @@ -3033,10 +3329,17 @@ AstTypePack* Parser::parseTypePack() // This will not fail because of the lookahead guard. expectAndConsume(Lexeme::Dot3, "generic type pack annotation"); - AstTypePackGeneric* node = allocator.alloc(Location(name.location, end), name.name); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(end.begin); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstTypePackGeneric* node = allocator.alloc(Location(name.location, end), name.name); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(end.begin); + return node; + } + else + { + return allocator.alloc(Location(name.location, end), name.name); + } } // TODO: shouldParseTypePack can be removed and parseTypePack can be called unconditionally instead @@ -3220,7 +3523,7 @@ AstExpr* Parser::parseExpr(unsigned int limit) AstExpr* subexpr = parseExpr(unaryPriority); expr = allocator.alloc(Location(start, subexpr->location), *uop, subexpr); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstNodeMap[expr] = allocator.alloc(opPosition); } else @@ -3243,7 +3546,7 @@ AstExpr* Parser::parseExpr(unsigned int limit) AstExpr* next = parseExpr(binaryPriority[*op].right); expr = allocator.alloc(Location(start, next->location), *op, expr, next); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstNodeMap[expr] = allocator.alloc(opPosition); op = parseBinaryOp(lexer.current()); @@ -3350,7 +3653,7 @@ AstExpr* Parser::parsePrimaryExpr(bool asStatement) expectMatchAndConsume(']', matchBracket); expr = allocator.alloc(Location(start, end), expr, index); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstNodeMap[expr] = allocator.alloc(matchBracket.position, closeBracketPosition); } else if (lexer.current().type == ':') @@ -3401,7 +3704,7 @@ AstExpr* Parser::parseAssertionExpr() if (lexer.current().type == Lexeme::DoubleColon) { CstExprTypeAssertion* cstNode = nullptr; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) { Position opPosition = lexer.current().location.begin; cstNode = allocator.alloc(opPosition); @@ -3409,7 +3712,7 @@ AstExpr* Parser::parseAssertionExpr() nextLexeme(); AstType* annotation = parseType(); AstExprTypeAssertion* node = allocator.alloc(Location(start, annotation->location), expr, annotation); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstNodeMap[node] = cstNode; return node; } @@ -3594,17 +3897,24 @@ AstExpr* Parser::parseFunctionArgs(AstExpr* func, bool self) TempVector commaPositions(scratchPosition); if (lexer.current().type != ')') - parseExprList(args, options.storeCstData ? &commaPositions : nullptr); + parseExprList(args, (FFlag::LuauStoreCSTData2 && options.storeCstData) ? &commaPositions : nullptr); Location end = lexer.current().location; Position argEnd = end.end; expectMatchAndConsume(')', matchParen); - AstExprCall* node = allocator.alloc(Location(func->location, end), func, copy(args), self, Location(argStart, argEnd)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(matchParen.position, lexer.previousLocation().begin, copy(commaPositions)); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstExprCall* node = allocator.alloc(Location(func->location, end), func, copy(args), self, Location(argStart, argEnd)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(matchParen.position, lexer.previousLocation().begin, copy(commaPositions)); + return node; + } + else + { + return allocator.alloc(Location(func->location, end), func, copy(args), self, Location(argStart, argEnd)); + } } else if (lexer.current().type == '{') { @@ -3612,21 +3922,35 @@ AstExpr* Parser::parseFunctionArgs(AstExpr* func, bool self) AstExpr* expr = parseTableConstructor(); Position argEnd = lexer.previousLocation().end; - AstExprCall* node = - allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, Location(argStart, argEnd)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(std::nullopt, std::nullopt, AstArray{nullptr, 0}); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstExprCall* node = + allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, Location(argStart, argEnd)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(std::nullopt, std::nullopt, AstArray{nullptr, 0}); + return node; + } + else + { + return allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, Location(argStart, argEnd)); + } } else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) { Location argLocation = lexer.current().location; AstExpr* expr = parseString(); - AstExprCall* node = allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, argLocation); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(std::nullopt, std::nullopt, AstArray{nullptr, 0}); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstExprCall* node = allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, argLocation); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(std::nullopt, std::nullopt, AstArray{nullptr, 0}); + return node; + } + else + { + return allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, argLocation); + } } else { @@ -3662,6 +3986,7 @@ LUAU_NOINLINE void Parser::reportAmbiguousCallError() std::optional Parser::tableSeparator() { + LUAU_ASSERT(FFlag::LuauStoreCSTData2); if (lexer.current().type == ',') return CstExprTable::Comma; else if (lexer.current().type == ';') @@ -3706,7 +4031,7 @@ AstExpr* Parser::parseTableConstructor() AstExpr* value = parseExpr(); items.push_back({AstExprTable::Item::General, key, value}); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstItems.push_back({indexerOpenPosition, indexerClosePosition, equalsPosition, tableSeparator(), lexer.current().location.begin}); } else if (lexer.current().type == Lexeme::Name && lexer.lookahead().type == '=') @@ -3727,7 +4052,7 @@ AstExpr* Parser::parseTableConstructor() func->debugname = name.name; items.push_back({AstExprTable::Item::Record, key, value}); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstItems.push_back({std::nullopt, std::nullopt, equalsPosition, tableSeparator(), lexer.current().location.begin}); } else @@ -3735,7 +4060,7 @@ AstExpr* Parser::parseTableConstructor() AstExpr* expr = parseExpr(); items.push_back({AstExprTable::Item::List, nullptr, expr}); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) cstItems.push_back({std::nullopt, std::nullopt, std::nullopt, tableSeparator(), lexer.current().location.begin}); } @@ -3758,10 +4083,17 @@ AstExpr* Parser::parseTableConstructor() if (!expectMatchAndConsume('}', matchBrace)) end = lexer.previousLocation(); - AstExprTable* node = allocator.alloc(Location(start, end), copy(items)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(copy(cstItems)); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstExprTable* node = allocator.alloc(Location(start, end), copy(items)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(cstItems)); + return node; + } + else + { + return allocator.alloc(Location(start, end), copy(items)); + } } AstExpr* Parser::parseIfElseExpr() @@ -3788,7 +4120,8 @@ AstExpr* Parser::parseIfElseExpr() hasElse = true; falseExpr = parseIfElseExpr(); recursionCounter = oldRecursionCount; - isElseIf = true; + if (FFlag::LuauStoreCSTData2) + isElseIf = true; } else { @@ -3798,10 +4131,17 @@ AstExpr* Parser::parseIfElseExpr() Location end = falseExpr->location; - AstExprIfElse* node = allocator.alloc(Location(start, end), condition, hasThen, trueExpr, hasElse, falseExpr); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(thenPosition, elsePosition, isElseIf); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstExprIfElse* node = allocator.alloc(Location(start, end), condition, hasThen, trueExpr, hasElse, falseExpr); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(thenPosition, elsePosition, isElseIf); + return node; + } + else + { + return allocator.alloc(Location(start, end), condition, hasThen, trueExpr, hasElse, falseExpr); + } } // Name @@ -3868,7 +4208,7 @@ std::pair, AstArray> Parser::pars if (lexer.current().type == '<') { Lexeme begin = lexer.current(); - if (openPosition) + if (FFlag::LuauStoreCSTData2 && openPosition) *openPosition = begin.location.begin; nextLexeme(); @@ -3899,10 +4239,17 @@ std::pair, AstArray> Parser::pars { AstTypePack* typePack = parseTypePack(); - AstGenericTypePack* node = allocator.alloc(nameLocation, name, typePack); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(ellipsisPosition, equalsPosition); - namePacks.push_back(node); + if (FFlag::LuauStoreCSTData2) + { + AstGenericTypePack* node = allocator.alloc(nameLocation, name, typePack); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(ellipsisPosition, equalsPosition); + namePacks.push_back(node); + } + else + { + namePacks.push_back(allocator.alloc(nameLocation, name, typePack)); + } } else { @@ -3911,10 +4258,17 @@ std::pair, AstArray> Parser::pars if (type) report(type->location, "Expected type pack after '=', got type"); - AstGenericTypePack* node = allocator.alloc(nameLocation, name, typePack); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(ellipsisPosition, equalsPosition); - namePacks.push_back(node); + if (FFlag::LuauStoreCSTData2) + { + AstGenericTypePack* node = allocator.alloc(nameLocation, name, typePack); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(ellipsisPosition, equalsPosition); + namePacks.push_back(node); + } + else + { + namePacks.push_back(allocator.alloc(nameLocation, name, typePack)); + } } } else @@ -3922,10 +4276,17 @@ std::pair, AstArray> Parser::pars if (seenDefault) report(lexer.current().location, "Expected default type pack after type pack name"); - AstGenericTypePack* node = allocator.alloc(nameLocation, name, nullptr); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(ellipsisPosition, std::nullopt); - namePacks.push_back(node); + if (FFlag::LuauStoreCSTData2) + { + AstGenericTypePack* node = allocator.alloc(nameLocation, name, nullptr); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(ellipsisPosition, std::nullopt); + namePacks.push_back(node); + } + else + { + namePacks.push_back(allocator.alloc(nameLocation, name, nullptr)); + } } } else @@ -3938,26 +4299,40 @@ std::pair, AstArray> Parser::pars AstType* defaultType = parseType(); - AstGenericType* node = allocator.alloc(nameLocation, name, defaultType); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(equalsPosition); - names.push_back(node); + if (FFlag::LuauStoreCSTData2) + { + AstGenericType* node = allocator.alloc(nameLocation, name, defaultType); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(equalsPosition); + names.push_back(node); + } + else + { + names.push_back(allocator.alloc(nameLocation, name, defaultType)); + } } else { if (seenDefault) report(lexer.current().location, "Expected default type after type name"); - AstGenericType* node = allocator.alloc(nameLocation, name, nullptr); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(std::nullopt); - names.push_back(node); + if (FFlag::LuauStoreCSTData2) + { + AstGenericType* node = allocator.alloc(nameLocation, name, nullptr); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(std::nullopt); + names.push_back(node); + } + else + { + names.push_back(allocator.alloc(nameLocation, name, nullptr)); + } } } if (lexer.current().type == ',') { - if (commaPositions) + if (FFlag::LuauStoreCSTData2 && commaPositions) localCommaPositions.push_back(lexer.current().location.begin); nextLexeme(); @@ -3971,12 +4346,12 @@ std::pair, AstArray> Parser::pars break; } - if (closePosition) + if (FFlag::LuauStoreCSTData2 && closePosition) *closePosition = lexer.current().location.begin; expectMatchAndConsume('>', begin); } - if (commaPositions) + if (FFlag::LuauStoreCSTData2 && commaPositions) *commaPositions = copy(localCommaPositions); AstArray generics = copy(names); @@ -3991,7 +4366,7 @@ AstArray Parser::parseTypeParams(Position* openingPosition, TempV if (lexer.current().type == '<') { Lexeme begin = lexer.current(); - if (openingPosition) + if (FFlag::LuauStoreCSTData2 && openingPosition) *openingPosition = begin.location.begin; nextLexeme(); @@ -4072,7 +4447,7 @@ AstArray Parser::parseTypeParams(Position* openingPosition, TempV if (lexer.current().type == ',') { - if (commaPositions) + if (FFlag::LuauStoreCSTData2 && commaPositions) commaPositions->push_back(lexer.current().location.begin); nextLexeme(); } @@ -4080,7 +4455,7 @@ AstArray Parser::parseTypeParams(Position* openingPosition, TempV break; } - if (closingPosition) + if (FFlag::LuauStoreCSTData2 && closingPosition) *closingPosition = lexer.current().location.begin; expectMatchAndConsume('>', begin); } @@ -4096,8 +4471,11 @@ std::optional> Parser::parseCharArray(AstArray* originalStr ); scratchData.assign(lexer.current().data, lexer.current().getLength()); - if (originalString) - *originalString = copy(scratchData); + if (FFlag::LuauStoreCSTData2) + { + if (originalString) + *originalString = copy(scratchData); + } if (lexer.current().type == Lexeme::QuotedString || lexer.current().type == Lexeme::InterpStringSimple) { @@ -4135,21 +4513,31 @@ AstExpr* Parser::parseString() LUAU_ASSERT(false && "Invalid string type"); } - CstExprConstantString::QuoteStyle fullStyle; - unsigned int blockDepth; - if (options.storeCstData) - std::tie(fullStyle, blockDepth) = extractStringDetails(); - - AstArray originalString; - if (std::optional> value = parseCharArray(options.storeCstData ? &originalString : nullptr)) + if (FFlag::LuauStoreCSTData2) { - AstExprConstantString* node = allocator.alloc(location, *value, style); + CstExprConstantString::QuoteStyle fullStyle; + unsigned int blockDepth; if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(originalString, fullStyle, blockDepth); - return node; + std::tie(fullStyle, blockDepth) = extractStringDetails(); + + AstArray originalString; + if (std::optional> value = parseCharArray(options.storeCstData ? &originalString : nullptr)) + { + AstExprConstantString* node = allocator.alloc(location, *value, style); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(originalString, fullStyle, blockDepth); + return node; + } + else + return reportExprError(location, {}, "String literal contains malformed escape sequence"); } else - return reportExprError(location, {}, "String literal contains malformed escape sequence"); + { + if (std::optional> value = parseCharArray()) + return allocator.alloc(location, *value, style); + else + return reportExprError(location, {}, "String literal contains malformed escape sequence"); + } } AstExpr* Parser::parseInterpString() @@ -4174,7 +4562,7 @@ AstExpr* Parser::parseInterpString() scratchData.assign(currentLexeme.data, currentLexeme.getLength()); - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) { sourceStrings.push_back(copy(scratchData)); stringPositions.push_back(currentLexeme.location.begin); @@ -4244,10 +4632,15 @@ AstExpr* Parser::parseInterpString() AstArray> stringsArray = copy(strings); AstArray expressionsArray = copy(expressions); - AstExprInterpString* node = allocator.alloc(Location{startLocation, endLocation}, stringsArray, expressionsArray); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(copy(sourceStrings), copy(stringPositions)); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstExprInterpString* node = allocator.alloc(Location{startLocation, endLocation}, stringsArray, expressionsArray); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(sourceStrings), copy(stringPositions)); + return node; + } + else + return allocator.alloc(Location{startLocation, endLocation}, stringsArray, expressionsArray); } AstExpr* Parser::parseNumber() @@ -4256,7 +4649,7 @@ AstExpr* Parser::parseNumber() scratchData.assign(lexer.current().data, lexer.current().getLength()); AstArray sourceData; - if (options.storeCstData) + if (FFlag::LuauStoreCSTData2 && options.storeCstData) sourceData = copy(scratchData); // Remove all internal _ - they don't hold any meaning and this allows parsing code to just pass the string pointer to strtod et al @@ -4272,10 +4665,17 @@ AstExpr* Parser::parseNumber() if (result == ConstantNumberParseResult::Malformed) return reportExprError(start, {}, "Malformed number"); - AstExprConstantNumber* node = allocator.alloc(start, value, result); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(sourceData); - return node; + if (FFlag::LuauStoreCSTData2) + { + AstExprConstantNumber* node = allocator.alloc(start, value, result); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(sourceData); + return node; + } + else + { + return allocator.alloc(start, value, result); + } } AstLocal* Parser::pushLocal(const Binding& binding) diff --git a/CodeGen/include/Luau/CodeGen.h b/CodeGen/include/Luau/CodeGen.h index 2e689fe2..9ed6c0d4 100644 --- a/CodeGen/include/Luau/CodeGen.h +++ b/CodeGen/include/Luau/CodeGen.h @@ -121,6 +121,8 @@ void create(lua_State* L, SharedCodeGenContext* codeGenContext); // Enable or disable native execution according to `enabled` argument void setNativeExecutionEnabled(lua_State* L, bool enabled); +void disableNativeExecutionForFunction(lua_State* L, const int level) noexcept; + // Given a name, this function must return the index of the type which matches the type array used all CompilationOptions and AssemblyOptions // If the type is unknown, 0xff has to be returned using UserdataRemapperCallback = uint8_t(void* context, const char* name, size_t nameLength); diff --git a/CodeGen/src/CodeGenA64.cpp b/CodeGen/src/CodeGenA64.cpp index 560fbd8d..6505f788 100644 --- a/CodeGen/src/CodeGenA64.cpp +++ b/CodeGen/src/CodeGenA64.cpp @@ -12,6 +12,8 @@ #include "lstate.h" +LUAU_DYNAMIC_FASTFLAG(AddReturnExectargetCheck); + namespace Luau { namespace CodeGen @@ -179,6 +181,14 @@ void emitReturn(AssemblyBuilderA64& build, ModuleHelpers& helpers) build.ldr(x1, mem(rClosure, offsetof(Closure, l.p))); // cl->l.p aka proto + if (DFFlag::AddReturnExectargetCheck) + { + // Get new instruction location + CODEGEN_ASSERT(offsetof(Proto, exectarget) == offsetof(Proto, execdata) + 8); + build.ldp(x3, x4, mem(x1, offsetof(Proto, execdata))); + build.cbz(x4, helpers.exitContinueVmClearNativeFlag); + } + CODEGEN_ASSERT(offsetof(Proto, code) == offsetof(Proto, k) + 8); build.ldp(rConstants, rCode, mem(x1, offsetof(Proto, k))); // proto->k, proto->code @@ -188,9 +198,12 @@ void emitReturn(AssemblyBuilderA64& build, ModuleHelpers& helpers) build.ldr(x2, mem(x2, offsetof(CallInfo, savedpc))); // cip->savedpc build.sub(x2, x2, rCode); - // Get new instruction location and jump to it - CODEGEN_ASSERT(offsetof(Proto, exectarget) == offsetof(Proto, execdata) + 8); - build.ldp(x3, x4, mem(x1, offsetof(Proto, execdata))); + if (!DFFlag::AddReturnExectargetCheck) + { + // Get new instruction location and jump to it + CODEGEN_ASSERT(offsetof(Proto, exectarget) == offsetof(Proto, execdata) + 8); + build.ldp(x3, x4, mem(x1, offsetof(Proto, execdata))); + } build.ldr(w2, mem(x3, x2)); build.add(x4, x4, x2); build.br(x4); diff --git a/CodeGen/src/CodeGenContext.cpp b/CodeGen/src/CodeGenContext.cpp index 82dfa17e..15631542 100644 --- a/CodeGen/src/CodeGenContext.cpp +++ b/CodeGen/src/CodeGenContext.cpp @@ -671,6 +671,21 @@ void setNativeExecutionEnabled(lua_State* L, bool enabled) L->global->ecb.enter = enabled ? onEnter : onEnterDisabled; } +void disableNativeExecutionForFunction(lua_State* L, const int level) noexcept +{ + CODEGEN_ASSERT(unsigned(level) < unsigned(L->ci - L->base_ci)); + + const CallInfo* ci = L->ci - level; + const TValue* o = ci->func; + CODEGEN_ASSERT(ttisfunction(o)); + + Proto* proto = clvalue(o)->l.p; + CODEGEN_ASSERT(proto); + + CODEGEN_ASSERT(proto->codeentry != proto->code); + onDestroyFunction(L, proto); +} + static uint8_t userdataRemapperWrap(lua_State* L, const char* str, size_t len) { if (BaseCodeGenContext* codegenCtx = getCodeGenContext(L)) diff --git a/CodeGen/src/EmitCommonX64.cpp b/CodeGen/src/EmitCommonX64.cpp index 36b5130e..897c93e2 100644 --- a/CodeGen/src/EmitCommonX64.cpp +++ b/CodeGen/src/EmitCommonX64.cpp @@ -14,6 +14,9 @@ #include + +LUAU_DYNAMIC_FASTFLAGVARIABLE(AddReturnExectargetCheck, false); + namespace Luau { namespace CodeGen @@ -458,6 +461,7 @@ void emitReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers) // Registers alive: r9 (cip) RegisterX64 proto = rcx; RegisterX64 execdata = rbx; + RegisterX64 exectarget = r10; // Change closure build.mov(rax, qword[cip + offsetof(CallInfo, func)]); @@ -471,6 +475,13 @@ void emitReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers) build.test(byte[cip + offsetof(CallInfo, flags)], LUA_CALLINFO_NATIVE); build.jcc(ConditionX64::Zero, helpers.exitContinueVm); // Continue in interpreter if function has no native data + if (DFFlag::AddReturnExectargetCheck) + { + build.mov(exectarget, qword[proto + offsetof(Proto, exectarget)]); + build.test(exectarget, exectarget); + build.jcc(ConditionX64::Zero, helpers.exitContinueVmClearNativeFlag); + } + // Change constants build.mov(rConstants, qword[proto + offsetof(Proto, k)]); @@ -486,7 +497,15 @@ void emitReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers) // Get new instruction location and jump to it build.mov(edx, dword[execdata + rax]); - build.add(rdx, qword[proto + offsetof(Proto, exectarget)]); + + if (DFFlag::AddReturnExectargetCheck) + { + build.add(rdx, exectarget); + } + else + { + build.add(rdx, qword[proto + offsetof(Proto, exectarget)]); + } build.jmp(rdx); } diff --git a/VM/src/lgcdebug.cpp b/VM/src/lgcdebug.cpp index 0a96c2e8..30800171 100644 --- a/VM/src/lgcdebug.cpp +++ b/VM/src/lgcdebug.cpp @@ -15,6 +15,7 @@ #include LUAU_FASTFLAG(LuauCurrentLineBounds) +LUAU_FASTFLAGVARIABLE(LuauHeapNameDetails) static void validateobjref(global_State* g, GCObject* f, GCObject* t) { @@ -728,10 +729,20 @@ static void enumclosure(EnumContext* ctx, Closure* cl) char buf[LUA_IDSIZE]; - if (p->source) - snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "", p->linedefined, getstr(p->source)); + if (FFlag::LuauHeapNameDetails) + { + if (p->source) + snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined, getstr(p->source)); + else + snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined); + } else - snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "", p->linedefined); + { + if (p->source) + snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "", p->linedefined, getstr(p->source)); + else + snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "", p->linedefined); + } enumnode(ctx, obj2gco(cl), sizeLclosure(cl->nupvalues), buf); } @@ -799,10 +810,21 @@ static void enumthread(EnumContext* ctx, lua_State* th) char buf[LUA_IDSIZE]; - if (p->source) - snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "", p->linedefined, getstr(p->source)); + if (FFlag::LuauHeapNameDetails) + { + if (p->source) + snprintf(buf, sizeof(buf), "thread at %s:%d %s", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined, getstr(p->source)); + else + snprintf(buf, sizeof(buf), "thread at %s:%d", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined); + } else - snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "", p->linedefined); + { + + if (p->source) + snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "", p->linedefined, getstr(p->source)); + else + snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "", p->linedefined); + } enumnode(ctx, obj2gco(th), size, buf); } @@ -835,7 +857,21 @@ static void enumproto(EnumContext* ctx, Proto* p) ctx->edge(ctx->context, enumtopointer(obj2gco(p)), p->execdata, "[native]"); } - enumnode(ctx, obj2gco(p), size, p->source ? getstr(p->source) : NULL); + if (FFlag::LuauHeapNameDetails) + { + char buf[LUA_IDSIZE]; + + if (p->source) + snprintf(buf, sizeof(buf), "proto %s:%d %s", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined, getstr(p->source)); + else + snprintf(buf, sizeof(buf), "proto %s:%d", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined); + + enumnode(ctx, obj2gco(p), size, buf); + } + else + { + enumnode(ctx, obj2gco(p), size, p->source ? getstr(p->source) : NULL); + } if (p->sizek) enumedges(ctx, obj2gco(p), p->k, p->sizek, "constants"); diff --git a/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index 4ef72223..ccc6cb35 100644 --- a/tests/Autocomplete.test.cpp +++ b/tests/Autocomplete.test.cpp @@ -19,7 +19,7 @@ LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2) LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) LUAU_FASTINT(LuauTypeInferRecursionLimit) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization) using namespace Luau; @@ -4454,10 +4454,10 @@ TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union") auto ac = autocomplete('1'); - if (FFlag::LuauSolverV2 && FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization) { - // This `if` statement is because `LuauNonReentrantGeneralization3` - // sets some flags + // This `if` statement is because `LuauEagerGeneralization` + // sets some flags CHECK(ac.entryMap.count("BaseMethod") > 0); CHECK(ac.entryMap.count("Method") > 0); } diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index 597d8a63..f658eef5 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -40,6 +40,7 @@ LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC) LUAU_FASTFLAG(LuauYieldableContinuations) LUAU_FASTFLAG(LuauCurrentLineBounds) LUAU_FASTFLAG(LuauLoadNoOomThrow) +LUAU_FASTFLAG(LuauHeapNameDetails) static lua_CompileOptions defaultOptions() { @@ -2280,6 +2281,8 @@ TEST_CASE("StringConversion") TEST_CASE("GCDump") { + ScopedFastFlag luauHeapNameDetails{FFlag::LuauHeapNameDetails, true}; + // internal function, declared in lgc.h - not exposed via lua.h extern void luaC_dump(lua_State * L, void* file, const char* (*categoryName)(lua_State* L, uint8_t memcat)); extern void luaC_enumheap( @@ -2320,7 +2323,19 @@ TEST_CASE("GCDump") lua_State* CL = lua_newthread(L); - lua_pushstring(CL, "local x x = {} local function f() x[1] = math.abs(42) end function foo() coroutine.yield() end foo() return f"); + lua_pushstring(CL, R"( +local x +x = {} +local function f() + x[1] = math.abs(42) +end +function foo() + coroutine.yield() +end +foo() +return f +)"); + lua_pushstring(CL, "=GCDump"); lua_loadstring(CL); lua_resume(CL, nullptr, 0); @@ -2365,8 +2380,19 @@ TEST_CASE("GCDump") { EnumContext& context = *(EnumContext*)ctx; - if (tt == LUA_TUSERDATA) - CHECK(strcmp(name, "u42") == 0); + if (name) + { + std::string_view sv{name}; + + if (tt == LUA_TUSERDATA) + CHECK(sv == "u42"); + else if (tt == LUA_TPROTO) + CHECK((sv == "proto unnamed:1 =GCDump" || sv == "proto foo:7 =GCDump" || sv == "proto f:4 =GCDump")); + else if (tt == LUA_TFUNCTION) + CHECK((sv == "test" || sv == "unnamed:1 =GCDump" || sv == "foo:7 =GCDump" || sv == "f:4 =GCDump")); + else if (tt == LUA_TTHREAD) + CHECK(sv == "thread at unnamed:1 =GCDump"); + } context.nodes[gco] = {gco, tt, memcat, size, name ? name : ""}; }, diff --git a/tests/Differ.test.cpp b/tests/Differ.test.cpp index 8bf8dd1f..bf200533 100644 --- a/tests/Differ.test.cpp +++ b/tests/Differ.test.cpp @@ -234,8 +234,6 @@ TEST_CASE_FIXTURE(DifferFixture, "right_cyclic_table_left_table_property_wrong") TEST_CASE_FIXTURE(DifferFixture, "equal_table_two_cyclic_tables_are_not_different") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( local function id(x: a): a return x @@ -1473,8 +1471,6 @@ TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "equal_metatable") TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "metatable_normal") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( local metaFoo = { metaBar = 5 diff --git a/tests/FragmentAutocomplete.test.cpp b/tests/FragmentAutocomplete.test.cpp index 05967407..11d4244c 100644 --- a/tests/FragmentAutocomplete.test.cpp +++ b/tests/FragmentAutocomplete.test.cpp @@ -26,13 +26,6 @@ using namespace Luau; LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTFLAG(LuauBetterReverseDependencyTracking) -LUAU_FASTFLAG(LuauAutocompleteUsesModuleForTypeCompatibility) -LUAU_FASTFLAG(LuauBetterCursorInCommentDetection) -LUAU_FASTFLAG(LuauAllFreeTypesHaveScopes) -LUAU_FASTFLAG(LuauClonedTableAndFunctionTypesMustHaveScopes) -LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode) -LUAU_FASTFLAG(LuauCloneTypeAliasBindings) -LUAU_FASTFLAG(LuauDoNotClonePersistentBindings) LUAU_FASTFLAG(LuauBetterScopeSelection) LUAU_FASTFLAG(LuauBlockDiffFragmentSelection) LUAU_FASTFLAG(LuauFragmentAcMemoryLeak) @@ -67,14 +60,8 @@ struct FragmentAutocompleteFixtureImpl : BaseType { static_assert(std::is_base_of_v, "BaseType must be a descendant of Fixture"); - ScopedFastFlag luauAllFreeTypesHaveScopes{FFlag::LuauAllFreeTypesHaveScopes, true}; - ScopedFastFlag luauClonedTableAndFunctionTypesMustHaveScopes{FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes, true}; - ScopedFastFlag luauDisableNewSolverAssertsInMixedMode{FFlag::LuauDisableNewSolverAssertsInMixedMode, true}; - ScopedFastFlag luauCloneTypeAliasBindings{FFlag::LuauCloneTypeAliasBindings, true}; - ScopedFastFlag luauDoNotClonePersistentBindings{FFlag::LuauDoNotClonePersistentBindings, true}; ScopedFastFlag luauBetterScopeSelection{FFlag::LuauBetterScopeSelection, true}; ScopedFastFlag luauBlockDiffFragmentSelection{FFlag::LuauBlockDiffFragmentSelection, true}; - ScopedFastFlag luauAutocompleteUsesModuleForTypeCompatibility{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true}; ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true}; ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true}; ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true}; @@ -2526,7 +2513,6 @@ l TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "do_not_recommend_results_in_multiline_comment") { - ScopedFastFlag sff = {FFlag::LuauBetterCursorInCommentDetection, true}; std::string source = R"(--[[ )"; std::string dest = R"(--[[ @@ -2547,7 +2533,6 @@ a TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments_simple") { - ScopedFastFlag sff = {FFlag::LuauBetterCursorInCommentDetection, true}; const std::string source = R"( -- sel -- retur @@ -2583,7 +2568,6 @@ bar baz ]] )"; - ScopedFastFlag sff{FFlag::LuauBetterCursorInCommentDetection, true}; autocompleteFragmentInBothSolvers( source, source, @@ -2628,7 +2612,6 @@ baz TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments") { - ScopedFastFlag sff = {FFlag::LuauBetterCursorInCommentDetection, true}; const std::string source = R"( -- sel -- retur @@ -2708,7 +2691,6 @@ if x == 5 local x = 5 if x == 5 then -- a comment )"; - ScopedFastFlag sff = {FFlag::LuauBetterCursorInCommentDetection, true}; autocompleteFragmentInBothSolvers( source, updated, @@ -2964,7 +2946,6 @@ return module TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "ice_caused_by_mixed_mode_use") { - ScopedFastFlag sff{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true}; const std::string source = std::string("--[[\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 " + diff --git a/tests/Generalization.test.cpp b/tests/Generalization.test.cpp index 97b9d058..efd3218e 100644 --- a/tests/Generalization.test.cpp +++ b/tests/Generalization.test.cpp @@ -15,7 +15,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(DebugLuauForbidInternalTypes) LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall) @@ -226,7 +226,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "('a) -> 'a") TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})") { - ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true}; + ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true}; TableType tt; tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType}; @@ -260,7 +260,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: number | string)) -> string?") TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()") { - ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true}; + ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true}; auto [aTy, aFree] = freshType(); auto [bTy, bFree] = freshType(); diff --git a/tests/InferPolarity.test.cpp b/tests/InferPolarity.test.cpp index e87a6389..84e0c2d3 100644 --- a/tests/InferPolarity.test.cpp +++ b/tests/InferPolarity.test.cpp @@ -8,13 +8,13 @@ using namespace Luau; -LUAU_FASTFLAG(LuauNonReentrantGeneralization3); +LUAU_FASTFLAG(LuauEagerGeneralization); TEST_SUITE_BEGIN("InferPolarity"); TEST_CASE_FIXTURE(Fixture, "T where T = { m: (a) -> T }") { - ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true}; + ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true}; TypeArena arena; ScopePtr globalScope = std::make_shared(builtinTypes->anyTypePack); diff --git a/tests/Linter.test.cpp b/tests/Linter.test.cpp index 1817767b..b24b6e70 100644 --- a/tests/Linter.test.cpp +++ b/tests/Linter.test.cpp @@ -10,7 +10,7 @@ LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LintRedundantNativeAttribute); LUAU_FASTFLAG(LuauDeprecatedAttribute); -LUAU_FASTFLAG(LuauNonReentrantGeneralization3); +LUAU_FASTFLAG(LuauEagerGeneralization); using namespace Luau; @@ -1942,7 +1942,7 @@ print(foo:bar(2.0)) TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperations") { // FIXME: For now this flag causes a stack overflow on Windows. - ScopedFastFlag _{FFlag::LuauNonReentrantGeneralization3, false}; + ScopedFastFlag _{FFlag::LuauEagerGeneralization, false}; LintResult result = lint(R"( local t = {} diff --git a/tests/Module.test.cpp b/tests/Module.test.cpp index 9564f081..8d4704c9 100644 --- a/tests/Module.test.cpp +++ b/tests/Module.test.cpp @@ -280,8 +280,6 @@ TEST_CASE_FIXTURE(Fixture, "clone_class") TEST_CASE_FIXTURE(Fixture, "clone_free_types") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - TypeArena arena; TypeId freeTy = freshType(NotNull{&arena}, builtinTypes, nullptr); TypePackVar freeTp(FreeTypePack{TypeLevel{}}); diff --git a/tests/Normalize.test.cpp b/tests/Normalize.test.cpp index 60cbb3a1..408bf54b 100644 --- a/tests/Normalize.test.cpp +++ b/tests/Normalize.test.cpp @@ -14,8 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauNormalizeIntersectionLimit) LUAU_FASTINT(LuauNormalizeUnionLimit) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget) LUAU_FASTFLAG(LuauSimplifyOutOfLine) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) @@ -1177,57 +1176,6 @@ end )"); } -#if 0 -TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_limit_function_intersection_complexity") -{ - ScopedFastInt luauTypeInferRecursionLimit{FInt::LuauTypeInferRecursionLimit, 80}; - ScopedFastInt luauNormalizeIntersectionLimit{FInt::LuauNormalizeIntersectionLimit, 50}; - ScopedFastInt luauNormalizeUnionLimit{FInt::LuauNormalizeUnionLimit, 20}; - - ScopedFastFlag _[] = { - {FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true}, - {FFlag::DebugLuauGreedyGeneralization, true}, - {FFlag::LuauSubtypeGenericsAndNegations, true}, - {FFlag::LuauNoMoreInjectiveTypeFunctions, true} - }; - - CheckResult result = check(R"( -function _(_).readu32(l0) -return ({[_(_(_))]=_,[_(if _ then _)]=_,n0=_,})[_],nil -end -_(_)[_(n32)] %= _(_(_)) - )"); - - LUAU_REQUIRE_ERRORS(result); -} - -TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_propagate_normalization_failures") -{ - ScopedFastInt luauNormalizeIntersectionLimit{FInt::LuauNormalizeIntersectionLimit, 50}; - ScopedFastInt luauNormalizeUnionLimit{FInt::LuauNormalizeUnionLimit, 20}; - - ScopedFastFlag _[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}, - {FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true}, - {FFlag::LuauSimplifyOutOfLine, true}, - {FFlag::LuauNonReentrantGeneralization3, false}, - {FFlag::DebugLuauGreedyGeneralization, true}, - {FFlag::LuauNoMoreInjectiveTypeFunctions, true}, - {FFlag::LuauSubtypeGenericsAndNegations, true}, - }; - - CheckResult result = check(R"( -function _(_,"").readu32(l0) -return ({[_(_(_))]=_,[_(if _ then _,_())]=_,[""]=_,})[_],nil -end -_().readu32 %= _(_(_(_),_)) - )"); - - LUAU_REQUIRE_ERRORS(result); -} -#endif - TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_flatten_type_pack_cycle") { ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, true}}; @@ -1258,7 +1206,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_union_type_pack_cycle") {FFlag::LuauNoMoreInjectiveTypeFunctions, true}, {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}, {FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true}, - {FFlag::DebugLuauGreedyGeneralization, true} + {FFlag::LuauEagerGeneralization, true} }; ScopedFastInt sfi{FInt::LuauTypeInferRecursionLimit, 0}; diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index 0a374269..f9cf3f13 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -20,6 +20,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauParseStringIndexer) LUAU_FASTFLAG(LuauDeclareExternType) +LUAU_FASTFLAG(LuauStoreCSTData2) LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix) // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix @@ -1260,8 +1261,6 @@ until false TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_local_function") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - try { parse(R"(-- i am line 1 @@ -1295,8 +1294,6 @@ end TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_failsafe_earlier") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - try { parse(R"(-- i am line 1 @@ -2902,6 +2899,8 @@ TEST_CASE_FIXTURE(Fixture, "function_start_locations_are_before_attributes") TEST_CASE_FIXTURE(Fixture, "for_loop_with_single_var_has_comma_positions_of_size_zero") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; + ParseOptions parseOptions; parseOptions.storeCstData = true; @@ -3364,8 +3363,6 @@ TEST_CASE_FIXTURE(Fixture, "AstName_comparison") TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - try { parse(R"( diff --git a/tests/Subtyping.test.cpp b/tests/Subtyping.test.cpp index c7116d74..4d0d625a 100644 --- a/tests/Subtyping.test.cpp +++ b/tests/Subtyping.test.cpp @@ -17,6 +17,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations) +LUAU_FASTFLAG(LuauEagerGeneralization) using namespace Luau; @@ -1647,4 +1648,19 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation") CHECK(result.isSubtype); } +TEST_CASE_FIXTURE(SubtypeFixture, "free_types_might_be_subtypes") +{ + ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true}; + + TypeId argTy = arena.freshType(builtinTypes, moduleScope.get()); + FreeType* freeArg = getMutable(argTy); + REQUIRE(freeArg); + freeArg->lowerBound = arena.addType(SingletonType{StringSingleton{"five"}}); + freeArg->upperBound = builtinTypes->stringType; + + SubtypingResult result = isSubtype(builtinTypes->stringType, argTy); + CHECK(result.isSubtype); + REQUIRE(1 == result.assumedConstraints.size()); +} + TEST_SUITE_END(); diff --git a/tests/ToDot.test.cpp b/tests/ToDot.test.cpp index 668ed66d..2bda6e81 100644 --- a/tests/ToDot.test.cpp +++ b/tests/ToDot.test.cpp @@ -322,8 +322,6 @@ n3 [label="TableType 3"]; TEST_CASE_FIXTURE(Fixture, "free") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - Type type{TypeVariant{FreeType{TypeLevel{0, 0}, builtinTypes->neverType, builtinTypes->unknownType}}}; ToDotOptions opts; opts.showPointers = false; diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp index 501d2a7a..dfc93060 100644 --- a/tests/ToString.test.cpp +++ b/tests/ToString.test.cpp @@ -6,6 +6,7 @@ #include "Fixture.h" #include "Luau/TypeChecker2.h" +#include "Luau/TypePack.h" #include "ScopedFlags.h" #include "doctest.h" @@ -14,6 +15,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAttributeSyntax) +LUAU_FASTFLAG(LuauFixEmptyTypePackStringification) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) TEST_SUITE_BEGIN("ToString"); @@ -492,6 +494,17 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_array_uses_array_syntax") CHECK_EQ("{string}", toString(Type{ttv})); } +TEST_CASE_FIXTURE(Fixture, "the_empty_type_pack_should_be_parenthesized") +{ + ScopedFastFlag sff{FFlag::LuauFixEmptyTypePackStringification, true}; + + TypePackVar emptyTypePack{TypePack{}}; + CHECK_EQ(toString(&emptyTypePack), "()"); + + auto unitToUnit = Type{FunctionType{&emptyTypePack, &emptyTypePack}}; + CHECK_EQ(toString(&unitToUnit), "() -> ()"); +} + TEST_CASE_FIXTURE(Fixture, "generic_packs_are_stringified_differently_from_generic_types") { diff --git a/tests/Transpiler.test.cpp b/tests/Transpiler.test.cpp index c587fa7c..65c43b7c 100644 --- a/tests/Transpiler.test.cpp +++ b/tests/Transpiler.test.cpp @@ -12,8 +12,10 @@ using namespace Luau; +LUAU_FASTFLAG(LuauStoreCSTData2) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions) +LUAU_FASTFLAG(LuauCSTForReturnTypeFunctionTail) TEST_SUITE_BEGIN("TranspilerTests"); @@ -47,6 +49,7 @@ TEST_CASE("string_literals_containing_utf8") TEST_CASE("if_stmt_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( if This then Once() end)"; CHECK_EQ(one, transpile(one).code); @@ -95,15 +98,31 @@ TEST_CASE("elseif_chains_indent_sensibly") TEST_CASE("strips_type_annotations") { const std::string code = R"( local s: string= 'hello there' )"; - const std::string expected = R"( local s = 'hello there' )"; - CHECK_EQ(expected, transpile(code).code); + if (FFlag::LuauStoreCSTData2) + { + const std::string expected = R"( local s = 'hello there' )"; + CHECK_EQ(expected, transpile(code).code); + } + else + { + const std::string expected = R"( local s = 'hello there' )"; + CHECK_EQ(expected, transpile(code).code); + } } TEST_CASE("strips_type_assertion_expressions") { const std::string code = R"( local s= some_function() :: any+ something_else() :: number )"; - const std::string expected = R"( local s= some_function() + something_else() )"; - CHECK_EQ(expected, transpile(code).code); + if (FFlag::LuauStoreCSTData2) + { + const std::string expected = R"( local s= some_function() + something_else() )"; + CHECK_EQ(expected, transpile(code).code); + } + else + { + const std::string expected = R"( local s= some_function() + something_else() )"; + CHECK_EQ(expected, transpile(code).code); + } } TEST_CASE("function_taking_ellipsis") @@ -130,6 +149,7 @@ TEST_CASE("for_loop") TEST_CASE("for_loop_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( for index = 1, 10 do call(index) end )"; CHECK_EQ(one, transpile(one).code); @@ -154,6 +174,7 @@ TEST_CASE("for_in_loop") TEST_CASE("for_in_loop_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( for k, v in ipairs(x) do end )"; CHECK_EQ(one, transpile(one).code); @@ -172,6 +193,7 @@ TEST_CASE("for_in_loop_spaces_around_tokens") TEST_CASE("for_in_single_variable") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( for key in pairs(x) do end )"; CHECK_EQ(one, transpile(one).code); } @@ -184,6 +206,7 @@ TEST_CASE("while_loop") TEST_CASE("while_loop_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( while f(x) do print() end )"; CHECK_EQ(one, transpile(one).code); @@ -205,6 +228,7 @@ TEST_CASE("repeat_until_loop") TEST_CASE("repeat_until_loop_condition_on_new_line") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( repeat print() @@ -236,6 +260,7 @@ TEST_CASE("local_assignment") TEST_CASE("local_assignment_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( local x = 1 )"; CHECK_EQ(one, transpile(one).code); @@ -269,6 +294,7 @@ TEST_CASE("local_function") TEST_CASE("local_function_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( local function p(o, m, ...) end )"; CHECK_EQ(one, transpile(one).code); @@ -287,6 +313,7 @@ TEST_CASE("function") TEST_CASE("function_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string two = R"( function p(o, m, ...) end )"; CHECK_EQ(two, transpile(two).code); @@ -315,6 +342,7 @@ TEST_CASE("function_spaces_around_tokens") TEST_CASE("function_with_types_spaces_around_tokens") { ScopedFastFlag sffs[] = { + {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true}, }; @@ -375,7 +403,7 @@ TEST_CASE("function_with_types_spaces_around_tokens") TEST_CASE("returns_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( return 1 )"; CHECK_EQ(one, transpile(one).code); @@ -388,7 +416,7 @@ TEST_CASE("returns_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = string )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -437,7 +465,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "type_alias_with_defaults_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = string )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -498,7 +526,7 @@ TEST_CASE("table_literal_closing_brace_at_correct_position") TEST_CASE("table_literal_with_semicolon_separators") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { x = 1; y = 2 } )"; @@ -508,7 +536,7 @@ TEST_CASE("table_literal_with_semicolon_separators") TEST_CASE("table_literal_with_trailing_separators") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { x = 1, y = 2, } )"; @@ -518,7 +546,7 @@ TEST_CASE("table_literal_with_trailing_separators") TEST_CASE("table_literal_with_spaces_around_separator") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { x = 1 , y = 2 } )"; @@ -528,7 +556,7 @@ TEST_CASE("table_literal_with_spaces_around_separator") TEST_CASE("table_literal_with_spaces_around_equals") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { x = 1 } )"; @@ -538,7 +566,7 @@ TEST_CASE("table_literal_with_spaces_around_equals") TEST_CASE("table_literal_multiline_with_indexers") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { ["my first value"] = "x"; @@ -566,7 +594,15 @@ TEST_CASE("spaces_between_keywords_even_if_it_pushes_the_line_estimation_off") // Luau::Parser doesn't exactly preserve the string representation of numbers in Lua, so we can find ourselves // falling out of sync with the original code. We need to push keywords out so that there's at least one space between them. const std::string code = R"( if math.abs(raySlope) < .01 then return 0 end )"; - CHECK_EQ(code, transpile(code).code); + if (FFlag::LuauStoreCSTData2) + { + CHECK_EQ(code, transpile(code).code); + } + else + { + const std::string expected = R"( if math.abs(raySlope) < 0.01 then return 0 end)"; + CHECK_EQ(expected, transpile(code).code); + } } TEST_CASE("numbers") @@ -578,26 +614,34 @@ TEST_CASE("numbers") TEST_CASE("infinity") { const std::string code = R"( local a = 1e500 local b = 1e400 )"; - CHECK_EQ(code, transpile(code).code); + if (FFlag::LuauStoreCSTData2) + { + CHECK_EQ(code, transpile(code).code); + } + else + { + const std::string expected = R"( local a = 1e500 local b = 1e500 )"; + CHECK_EQ(expected, transpile(code).code); + } } TEST_CASE("numbers_with_separators") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = 123_456_789 )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("hexadecimal_numbers") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = 0xFFFF )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("binary_numbers") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = 0b0101 )"; CHECK_EQ(code, transpile(code).code); } @@ -610,28 +654,28 @@ TEST_CASE("single_quoted_strings") TEST_CASE("double_quoted_strings") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = "hello world" )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("simple_interp_string") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = `hello world` )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("raw_strings") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = [[ hello world ]] )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("raw_strings_with_blocks") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = [==[ hello world ]==] )"; CHECK_EQ(code, transpile(code).code); } @@ -650,7 +694,7 @@ TEST_CASE("escaped_strings_2") TEST_CASE("escaped_strings_newline") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( print("foo \ bar") @@ -660,14 +704,14 @@ TEST_CASE("escaped_strings_newline") TEST_CASE("escaped_strings_raw") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local x = [=[\v<((do|load)file|require)\s*\(?['"]\zs[^'"]+\ze['"]]=] )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("position_correctly_updated_when_writing_multiline_string") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call([[ testing @@ -713,56 +757,56 @@ TEST_CASE("function_call_parentheses_multiple_args_no_space") TEST_CASE("function_call_parentheses_multiple_args_space_before_commas") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call(arg1 ,arg3 ,arg3) )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_spaces_before_parentheses") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call () )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_spaces_within_parentheses") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call( ) )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_string_double_quotes") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call "string" )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_string_single_quotes") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call 'string' )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_string_no_space") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call'string' )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_table_literal") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call { x = 1 } )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_table_literal_no_space") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call{x=1} )"; CHECK_EQ(code, transpile(code).code); } @@ -807,7 +851,7 @@ TEST_CASE("emit_a_do_block_in_cases_of_potentially_ambiguous_syntax") TEST_CASE_FIXTURE(Fixture, "parentheses_multiline") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local test = ( x @@ -819,6 +863,9 @@ local test = ( TEST_CASE_FIXTURE(Fixture, "stmt_semicolon") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( local test = 1; )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -828,6 +875,7 @@ TEST_CASE_FIXTURE(Fixture, "stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon") { + ScopedFastFlag sff{FFlag::LuauStoreCSTData2, true}; std::string code = R"( do return; @@ -838,6 +886,9 @@ TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon") TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( if init then x = string.sub(x, utf8.offset(x, init)); @@ -848,6 +899,9 @@ TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( if (t < 1) then return c/2*t*t + b end; )"; @@ -856,6 +910,9 @@ TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2") TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( for i,v in ... do end; @@ -865,6 +922,9 @@ TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "while_do_semicolon") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( while true do end; @@ -874,6 +934,9 @@ TEST_CASE_FIXTURE(Fixture, "while_do_semicolon") TEST_CASE_FIXTURE(Fixture, "function_definition_semicolon") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( function foo() end; @@ -951,7 +1014,16 @@ TEST_CASE("a_table_key_can_be_the_empty_string") TEST_CASE("always_emit_a_space_after_local_keyword") { std::string code = "do local aZZZZ = Workspace.P1.Shape local bZZZZ = Enum.PartType.Cylinder end"; - CHECK_EQ(code, transpile(code).code); + + if (FFlag::LuauStoreCSTData2) + { + CHECK_EQ(code, transpile(code).code); + } + else + { + std::string expected = "do local aZZZZ = Workspace.P1 .Shape local bZZZZ= Enum.PartType.Cylinder end"; + CHECK_EQ(expected, transpile(code).code); + } } TEST_CASE_FIXTURE(Fixture, "types_should_not_be_considered_cyclic_if_they_are_not_recursive") @@ -988,13 +1060,23 @@ TEST_CASE_FIXTURE(Fixture, "type_lists_should_be_emitted_correctly") end )"; - std::string expected = R"( + std::string expected = FFlag::LuauStoreCSTData2 ? R"( local a:(a:string,b:number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number) end local b:(...string)->(...number)=function(...:string): ...number end + local c:()->()=function(): () + end + )" + : R"( + local a:(string,number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number) + end + + local b:(...string)->(...number)=function(...:string): ...number + end + local c:()->()=function(): () end )"; @@ -1034,7 +1116,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_assertion") TEST_CASE_FIXTURE(Fixture, "type_assertion_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = "local a = 5 :: number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1051,7 +1133,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else") TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = "local a = if 1 then 2 elseif 3 then 4 else 5"; CHECK_EQ(code, transpile(code).code); @@ -1059,7 +1141,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions") TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local x = if yes then nil @@ -1075,7 +1157,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2") TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = "local a = if 1 then 2 else 3"; CHECK_EQ(code, transpile(code).code); @@ -1112,7 +1194,7 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_between_else_if") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( return if a then "was a" else @@ -1140,7 +1222,7 @@ local a: Import.Type TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local _: Foo.Type )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1169,6 +1251,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens") { ScopedFastFlag sffs[] = { + {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true}, }; std::string code = R"( local _: Type )"; @@ -1190,6 +1273,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_for_loop_annotation_spaces_around_tokens") { ScopedFastFlag sffs[] = { + {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true}, }; std::string code = R"( for i: number = 1, 10 do end )"; @@ -1230,7 +1314,7 @@ local b: Packed<(number, string)> TEST_CASE_FIXTURE(Fixture, "type_packs_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type _ = Packed< T...> )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1288,7 +1372,11 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_2") TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3") { std::string code = "local a: nil | (string & number)"; - CHECK_EQ(code, transpile(code, {}, true).code); + + if (FFlag::LuauStoreCSTData2) + CHECK_EQ(code, transpile(code, {}, true).code); + else + CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code); } TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested") @@ -1308,6 +1396,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested_2") TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function") { ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, }; std::string code = "type FnB = () -> U... & T"; @@ -1317,6 +1406,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function") TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = "local a: | string | number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1326,6 +1418,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe") TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = "local a: string | number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1335,6 +1430,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = "local a: & string & number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1344,6 +1442,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand") TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = "local a: string & number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1353,6 +1454,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = "local a: string | (Foo & Bar)"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1377,6 +1481,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection") TEST_CASE_FIXTURE(Fixture, "transpile_preserve_union_optional_style") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = "local a: string | nil"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1408,7 +1515,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_varargs") TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string one = "local _ = a.name"; CHECK_EQ(one, transpile(one, {}, true).code); @@ -1421,7 +1528,7 @@ TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "index_name_ends_with_digit") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = "sparkles.Color = Color3.new()"; CHECK_EQ(code, transpile(code, {}, true).code); } @@ -1435,7 +1542,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_index_expr") TEST_CASE_FIXTURE(Fixture, "index_expr_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string one = "local _ = a[2]"; CHECK_EQ(one, transpile(one, {}, true).code); @@ -1479,7 +1586,7 @@ local _ = # e TEST_CASE_FIXTURE(Fixture, "binary_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local _ = 1+1 local _ = 1 +1 @@ -1521,7 +1628,7 @@ a ..= ' - result' TEST_CASE_FIXTURE(Fixture, "compound_assignment_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string one = R"( a += 1 )"; CHECK_EQ(one, transpile(one, {}, true).code); @@ -1538,7 +1645,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_assign_multiple") TEST_CASE_FIXTURE(Fixture, "transpile_assign_spaces_around_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string one = "a = 1"; CHECK_EQ(one, transpile(one).code); @@ -1574,7 +1681,11 @@ local f: (T, S...)->(number) = foo TEST_CASE_FIXTURE(Fixture, "transpile_union_reverse") { std::string code = "local a: nil | number"; - CHECK_EQ(code, transpile(code, {}, true).code); + + if (FFlag::LuauStoreCSTData2) + CHECK_EQ(code, transpile(code, {}, true).code); + else + CHECK_EQ("local a: number?", transpile(code, {}, true).code); } TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple") @@ -1689,6 +1800,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple_types") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp") { + ScopedFastFlag fflags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( local _ = `hello {name}` )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1696,6 +1810,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline") { + ScopedFastFlag fflags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( local _ = `hello { name }!` )"; @@ -1705,6 +1822,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line") { + ScopedFastFlag fflags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( error( `a {b} c` @@ -1716,6 +1836,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local _ = `hello \ world!` )"; @@ -1724,6 +1845,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape") TEST_CASE_FIXTURE(Fixture, "transpile_string_literal_escape") { + ScopedFastFlag fflags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( local _ = ` bracket = \{, backtick = \` = {'ok'} ` )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1738,6 +1862,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_functions") TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type function foo() end )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1753,6 +1878,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_typeof_spaces_around_tokens") { + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type X = typeof(x) )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1777,14 +1903,14 @@ TEST_CASE("transpile_single_quoted_string_types") TEST_CASE("transpile_double_quoted_string_types") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( type a = "hello world" )"; CHECK_EQ(code, transpile(code, {}, true).code); } TEST_CASE("transpile_raw_string_types") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type a = [[ hello world ]] )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1794,14 +1920,14 @@ TEST_CASE("transpile_raw_string_types") TEST_CASE("transpile_escaped_string_types") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( type a = "\\b\\t\\n\\\\" )"; CHECK_EQ(code, transpile(code, {}, true).code); } TEST_CASE("transpile_type_table_semicolon_separators") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( type Foo = { bar: number; @@ -1813,7 +1939,7 @@ TEST_CASE("transpile_type_table_semicolon_separators") TEST_CASE("transpile_type_table_access_modifiers") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { read bar: number, @@ -1834,7 +1960,7 @@ TEST_CASE("transpile_type_table_access_modifiers") TEST_CASE("transpile_type_table_spaces_between_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { bar: number, } )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1877,7 +2003,7 @@ TEST_CASE("transpile_type_table_spaces_between_tokens") TEST_CASE("transpile_type_table_preserve_original_indexer_style") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { [number]: string @@ -1893,7 +2019,7 @@ TEST_CASE("transpile_type_table_preserve_original_indexer_style") TEST_CASE("transpile_type_table_preserve_indexer_location") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { [number]: string, @@ -1922,7 +2048,7 @@ TEST_CASE("transpile_type_table_preserve_indexer_location") TEST_CASE("transpile_type_table_preserve_property_definition_style") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { ["$$typeof1"]: string, @@ -1934,7 +2060,7 @@ TEST_CASE("transpile_type_table_preserve_property_definition_style") TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { [ "$$typeof1"]: string, @@ -1946,6 +2072,9 @@ TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens") TEST_CASE("transpile_types_preserve_parentheses_style") { + ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, + }; std::string code = R"( type Foo = number )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1985,6 +2114,7 @@ end TEST_CASE("transpile_type_function_unnamed_arguments") { ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, }; std::string code = R"( type Foo = () -> () )"; @@ -2021,6 +2151,7 @@ TEST_CASE("transpile_type_function_unnamed_arguments") TEST_CASE("transpile_type_function_named_arguments") { ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, }; std::string code = R"( type Foo = (x: string) -> () )"; @@ -2051,6 +2182,7 @@ TEST_CASE("transpile_type_function_named_arguments") TEST_CASE("transpile_type_function_generics") { ScopedFastFlag flags[] = { + {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, }; std::string code = R"( type Foo = () -> () )"; @@ -2087,6 +2219,7 @@ TEST_CASE("transpile_type_function_generics") TEST_CASE("transpile_type_function_return_types") { ScopedFastFlag fflags[] = { + {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, }; std::string code = R"( type Foo = () -> () )"; @@ -2132,6 +2265,23 @@ TEST_CASE("transpile_type_function_return_types") CHECK_EQ(code, transpile(code, {}, true).code); } +TEST_CASE("transpile_chained_function_types") +{ + ScopedFastFlag fflags[] = { + {FFlag::LuauStoreCSTData2, true}, + {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, + {FFlag::LuauCSTForReturnTypeFunctionTail, true}, + }; + std::string code = R"( type Foo = () -> () -> () )"; + CHECK_EQ(code, transpile(code, {}, true).code); + + code = R"( type Foo = () -> () -> () )"; + CHECK_EQ(code, transpile(code, {}, true).code); + + code = R"( type Foo = () -> () -> () )"; + CHECK_EQ(code, transpile(code, {}, true).code); +} + TEST_CASE("fuzzer_nil_optional") { const std::string code = R"( local x: nil? )"; @@ -2140,7 +2290,7 @@ TEST_CASE("fuzzer_nil_optional") TEST_CASE("transpile_function_attributes") { - + ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( @native function foo() diff --git a/tests/TypeFunction.test.cpp b/tests/TypeFunction.test.cpp index e379d351..1eac0247 100644 --- a/tests/TypeFunction.test.cpp +++ b/tests/TypeFunction.test.cpp @@ -14,7 +14,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauHasPropProperBlock) LUAU_FASTFLAG(LuauSimplifyOutOfLine) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) @@ -1725,7 +1725,7 @@ struct TFFixture TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}}; const ScopedFastFlag sff[1] = { - {FFlag::DebugLuauGreedyGeneralization, true}, + {FFlag::LuauEagerGeneralization, true}, }; BuiltinTypeFunctions builtinTypeFunctions; diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index db470126..e2df2c9b 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -9,6 +9,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests"); @@ -1986,13 +1987,20 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_bool") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; + if (FFlag::LuauEagerGeneralization) + { + // FIXME: CLI-151985 + // This test breaks because we can't see that eq is already fully reduced. + return; + } + CheckResult result = check(R"( type function compare(arg) return types.singleton(types.singleton(false) == arg) end -local function ok(idx: compare): true return idx end -local function ok(idx: compare): false return idx end +local function ok1(idx: compare): true return idx end +local function ok2(idx: compare): false return idx end )"); LUAU_REQUIRE_NO_ERRORS(result); @@ -2002,6 +2010,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_string") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; + if (FFlag::LuauEagerGeneralization) + { + // FIXME: CLI-151985 + // This test breaks because we can't see that eq is already fully reduced. + return; + } + CheckResult result = check(R"( type function compare(arg) return types.singleton(types.singleton("") == arg) diff --git a/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index c6d084dc..758d2b48 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/tests/TypeInfer.aliases.test.cpp @@ -12,7 +12,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations) LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2) -LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion) +LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2) LUAU_FASTFLAG(LuauSkipMalformedTypeAliases) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) @@ -355,8 +355,6 @@ TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_typ // Check that recursive intersection type doesn't generate an OOM TEST_CASE_FIXTURE(Fixture, "cli_38393_recursive_intersection_oom") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( function _(l0:(t0)&((t0)&(((t0)&((t0)->()))->(typeof(_),typeof(# _)))),l39,...):any end @@ -970,9 +968,6 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_locations") */ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_lose_track_of_PendingExpansionTypes_after_substitution") { - // CLI-114134 - We need egraphs to properly simplify these types. - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - fileResolver.source["game/ReactCurrentDispatcher"] = R"( export type BasicStateAction = ((S) -> S) | S export type Dispatch = (A) -> () @@ -1258,7 +1253,7 @@ TEST_CASE_FIXTURE(Fixture, "exported_type_function_location_is_accessible_on_mod TEST_CASE_FIXTURE(Fixture, "fuzzer_cursed_type_aliases") { - ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion, true}; + ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2, true}; // This used to crash under the new solver: we would like this to continue // to not crash. @@ -1298,5 +1293,16 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_dont_crash_on_duplicate_with_typeof") CHECK(get(result.errors[0])); } +TEST_CASE_FIXTURE(Fixture, "fuzzer_more_cursed_aliases") +{ + ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2, true}; + + LUAU_REQUIRE_ERRORS(check(R"( +export type t138 = t0 +export type t0 = t0 + )")); +} + + TEST_SUITE_END(); diff --git a/tests/TypeInfer.builtins.test.cpp b/tests/TypeInfer.builtins.test.cpp index c80467c8..bc3988da 100644 --- a/tests/TypeInfer.builtins.test.cpp +++ b/tests/TypeInfer.builtins.test.cpp @@ -11,7 +11,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauTableCloneClonesType3) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) TEST_SUITE_BEGIN("BuiltinTests"); @@ -460,7 +460,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_pack_reduce") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauSolverV2 && FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization) CHECK("{ [number]: string | string | string, n: number }" == toString(requireType("t"))); else if (FFlag::LuauSolverV2) CHECK_EQ("{ [number]: string, n: number }", toString(requireType("t"))); diff --git a/tests/TypeInfer.classes.test.cpp b/tests/TypeInfer.classes.test.cpp index 14a61025..4c7dd2a1 100644 --- a/tests/TypeInfer.classes.test.cpp +++ b/tests/TypeInfer.classes.test.cpp @@ -129,8 +129,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "we_can_infer_that_a_parameter_must_be_a_pa TEST_CASE_FIXTURE(ExternTypeFixture, "we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( function makeClone(o) return BaseClass.Clone(o) @@ -472,9 +470,6 @@ Type 'number' could not be converted into 'string')"; TEST_CASE_FIXTURE(ExternTypeFixture, "class_type_mismatch_with_name_conflict") { - // CLI-116433 - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( local i = ChildClass.New() type ChildClass = { x: number } diff --git a/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index 9ba1a055..06aa914d 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/tests/TypeInfer.functions.test.cpp @@ -22,7 +22,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauHasPropProperBlock) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) @@ -1038,9 +1038,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_lea TEST_CASE_FIXTURE(Fixture, "too_many_return_values") { - // FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly. - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( --!strict @@ -1472,9 +1469,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "variadic_any_is_compatible_with_a_generic_Ty TEST_CASE_FIXTURE(Fixture, "infer_anonymous_function_arguments_outside_call") { - // FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( type Table = { x: number, y: number } local f: (Table) -> number = function(t) return t.x + t.y end @@ -1694,7 +1688,7 @@ t.f = function(x) end )"); - if (FFlag::DebugLuauGreedyGeneralization && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2) { // FIXME CLI-151985 LUAU_CHECK_ERROR_COUNT(3, result); @@ -1779,7 +1773,7 @@ t.f = function(x) end )"); - if (FFlag::DebugLuauGreedyGeneralization && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2) { // FIXME CLI-151985 LUAU_CHECK_ERROR_COUNT(2, result); @@ -1981,10 +1975,15 @@ TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_ CHECK_EQ("(a) -> a", toString(requireType("f"))); - if (FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2) + { + LUAU_CHECK_NO_ERRORS(result); + CHECK("({ read p: { read q: a } }) -> (a & ~(false?))?" == toString(requireType("g"))); + } + else if (FFlag::LuauSolverV2) { // FIXME CLI-143852: Depends on interleaving generalization and type function reduction. - LUAU_REQUIRE_ERRORS(result); + LUAU_CHECK_ERRORS(result); CHECK_EQ("({ read p: unknown }) -> (*error-type* | ~(false?))?", toString(requireType("g"))); } else @@ -2941,7 +2940,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types") { // The new solver should ideally be able to do better here, but this is no worse than the old solver. - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { LUAU_REQUIRE_ERROR_COUNT(2, result); auto tm1 = get(result.errors[0]); diff --git a/tests/TypeInfer.intersectionTypes.test.cpp b/tests/TypeInfer.intersectionTypes.test.cpp index 41753dcb..aee9b14a 100644 --- a/tests/TypeInfer.intersectionTypes.test.cpp +++ b/tests/TypeInfer.intersectionTypes.test.cpp @@ -334,9 +334,6 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed") TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect") { - // CLI-116476 Subtyping between type alias and an equivalent but not named type isn't working. - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( type X = { x: (number) -> number } type Y = { y: (string) -> string } diff --git a/tests/TypeInfer.loops.test.cpp b/tests/TypeInfer.loops.test.cpp index b9cfc056..258d0dd3 100644 --- a/tests/TypeInfer.loops.test.cpp +++ b/tests/TypeInfer.loops.test.cpp @@ -325,9 +325,6 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error") TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function") { - // We report a spuriouus duplicate error here. - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( local bad_iter = 5 @@ -1130,7 +1127,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_on_never_gives_never") LUAU_REQUIRE_NO_ERRORS(result); if (FFlag::LuauSolverV2) - CHECK("nil" == toString(requireType("ans"))); + CHECK("nil" == toString(requireType("ans"))); else CHECK(toString(requireType("ans")) == "never"); } @@ -1404,7 +1401,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1413") local bar = foo - foo + foo foo = bar end - end + end )")); } diff --git a/tests/TypeInfer.modules.test.cpp b/tests/TypeInfer.modules.test.cpp index 55dd27ed..19a70513 100644 --- a/tests/TypeInfer.modules.test.cpp +++ b/tests/TypeInfer.modules.test.cpp @@ -13,7 +13,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) @@ -787,7 +787,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "leaky_generics") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { CHECK_EQ("(unknown) -> unknown", toString(requireTypeAtPosition({13, 23}))); } diff --git a/tests/TypeInfer.operators.test.cpp b/tests/TypeInfer.operators.test.cpp index 61fe9dc9..0868edd9 100644 --- a/tests/TypeInfer.operators.test.cpp +++ b/tests/TypeInfer.operators.test.cpp @@ -17,7 +17,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) TEST_SUITE_BEGIN("TypeInferOperators"); @@ -29,7 +29,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { // FIXME: Regression CHECK("(string & ~(false?)) | number" == toString(*requireType("s"))); @@ -51,7 +51,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { // FIXME: Regression. CHECK("(string & ~(false?)) | number" == toString(*requireType("s"))); @@ -72,7 +72,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { // FIXME: Regression CHECK("(string & ~(false?)) | string" == toString(requireType("s"))); @@ -634,7 +634,20 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error") local a = -foo )"); - if (FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2) + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + + CHECK("string" == toString(requireType("a"))); + + TypeMismatch* tm = get(result.errors[0]); + REQUIRE(tm); + + // FIXME: This error is a bit weird. + CHECK("({ @metatable { __unm: (boolean) -> string }, { value: number } }) -> string" == toString(tm->wantedType, {true})); + CHECK("(boolean) -> string" == toString(tm->givenType)); + } + else if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); diff --git a/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index b6cc4493..0945a883 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/tests/TypeInfer.provisional.test.cpp @@ -13,6 +13,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) +LUAU_FASTFLAG(LuauStoreCSTData2) LUAU_FASTINT(LuauNormalizeCacheLimit) LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTINT(LuauTypeInferIterationLimit) @@ -48,7 +49,7 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") end )"; - const std::string expected = R"( + const std::string expected = FFlag::LuauStoreCSTData2 ? R"( function f(a:{fn:()->(a,b...)}): () if type(a) == 'boolean' then local a1:boolean=a @@ -56,9 +57,18 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") local a2:{fn:()->(a,b...)}=a end end + )" + : R"( + function f(a:{fn:()->(a,b...)}): () + if type(a) == 'boolean'then + local a1:boolean=a + elseif a.fn()then + local a2:{fn:()->(a,b...)}=a + end + end )"; - const std::string expectedWithNewSolver = R"( + const std::string expectedWithNewSolver = FFlag::LuauStoreCSTData2 ? R"( function f(a:{fn:()->(unknown,...unknown)}): () if type(a) == 'boolean' then local a1:{fn:()->(unknown,...unknown)}&boolean=a @@ -66,9 +76,18 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a end end + )" + : R"( + function f(a:{fn:()->(unknown,...unknown)}): () + if type(a) == 'boolean'then + local a1:{fn:()->(unknown,...unknown)}&boolean=a + elseif a.fn()then + local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a + end + end )"; - const std::string expectedWithEqSat = R"( + const std::string expectedWithEqSat = FFlag::LuauStoreCSTData2 ? R"( function f(a:{fn:()->(unknown,...unknown)}): () if type(a) == 'boolean' then local a1:{fn:()->(unknown,...unknown)}&boolean=a @@ -76,6 +95,15 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") local a2:{fn:()->(unknown,...unknown)}&negate=a end end + )" + : R"( + function f(a:{fn:()->(unknown,...unknown)}): () + if type(a) == 'boolean'then + local a1:{fn:()->(unknown,...unknown)}&boolean=a + elseif a.fn()then + local a2:{fn:()->(unknown,...unknown)}&negate=a + end + end )"; if (FFlag::LuauSolverV2 && !FFlag::DebugLuauEqSatSimplification) diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 065f243a..bade0ce6 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -10,11 +10,13 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable) LUAU_FASTFLAG(LuauWeakNilRefinementType) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauSimplificationTableExternType) +LUAU_FASTFLAG(LuauBetterCannotCallFunctionPrimitive) +LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck) LUAU_FASTFLAG(LuauAvoidDoubleNegation) using namespace Luau; @@ -108,6 +110,13 @@ struct RefinementExternTypeFixture : BuiltinsFixture {"IsA", Property{isA}}, }; + TypeId scriptConnection = arena.addType(ExternType("ExternScriptConnection", {}, inst, std::nullopt, {}, nullptr, "Test", {})); + TypePackId disconnectArgs = arena.addTypePack({scriptConnection}); + TypeId disconnect = arena.addType(FunctionType{disconnectArgs, builtinTypes->emptyTypePack}); + getMutable(scriptConnection)->props = { + {"Disconnect", Property{disconnect}}, + }; + TypeId folder = frontend.globals.globalTypes.addType(ExternType{"Folder", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); TypeId part = frontend.globals.globalTypes.addType(ExternType{"Part", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); getMutable(part)->props = { @@ -123,6 +132,7 @@ struct RefinementExternTypeFixture : BuiltinsFixture frontend.globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vec3}; frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, inst}; + frontend.globals.globalScope->exportedTypeBindings["ExternScriptConnection"] = TypeFun{{}, scriptConnection}; frontend.globals.globalScope->exportedTypeBindings["Folder"] = TypeFun{{}, folder}; frontend.globals.globalScope->exportedTypeBindings["Part"] = TypeFun{{}, part}; frontend.globals.globalScope->exportedTypeBindings["WeldConstraint"] = TypeFun{{}, weldConstraint}; @@ -760,7 +770,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonoptional_type_can_narrow_to_nil_if_sense_ LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) { CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil" CHECK("string & unknown & unknown & ~nil" == toString(requireTypeAtPosition({6, 24}))); // type(v) ~= "nil" @@ -2504,7 +2514,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi end )")); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) CHECK_EQ("nil & string & unknown", toString(requireTypeAtPosition({4, 24}))); else CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24}))); @@ -2648,5 +2658,77 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1451") )")); } +TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function") +{ + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + + CheckResult result = check(R"( + type Disconnectable = { + Disconnect: (self: Disconnectable) -> (...any); + } | { + disconnect: (self: Disconnectable) -> (...any) + } | ExternScriptConnection + + local x: Disconnectable = workspace.ChildAdded:Connect(function() + print("child added") + end) + + if type(x.Disconnect) == "function" then + x:Disconnect() + end + )"); + + if (FFlag::LuauTypeCheckerStricterIndexCheck) + { + LUAU_REQUIRE_ERROR_COUNT(2, result); + + CHECK_EQ(toString(result.errors[0]), "Key 'Disconnect' is missing from 't2 where t1 = ExternScriptConnection | t2 | { Disconnect: (t1) -> (...any) } ; t2 = { disconnect: (t1) -> (...any) }' in the type 't1 where t1 = ExternScriptConnection | { Disconnect: (t1) -> (...any) } | { disconnect: (t1) -> (...any) }'"); + + if (FFlag::LuauBetterCannotCallFunctionPrimitive) + CHECK_EQ(toString(result.errors[1]), "The type function is not precise enough for us to determine the appropriate result type of this call."); + else + CHECK_EQ(toString(result.errors[1]), "Cannot call a value of type function"); + } + else + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + + if (FFlag::LuauBetterCannotCallFunctionPrimitive) + CHECK_EQ(toString(result.errors[0]), "The type function is not precise enough for us to determine the appropriate result type of this call."); + else + CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type function"); + } +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1835") +{ + LUAU_REQUIRE_NO_ERRORS(check(R"( + --!strict + local t: {name: string}? = nil + + function f() + local name = if t then t.name else "name" + end + )")); + + LUAU_REQUIRE_NO_ERRORS(check(R"( + --!strict + local t: {name: string}? = nil + + function f() + if t then end + local name = if t then t.name else "name" + end + )")); + + CheckResult result = check(R"( + local t: {name: string}? = nil + if t then end + print(t.name) + local name = if t then t.name else "name" + )"); + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(get(result.errors[0])); +} TEST_SUITE_END(); diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index 1e7521df..00e1e43a 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -21,8 +21,8 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) @@ -30,6 +30,11 @@ LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck) LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauSimplifyOutOfLine) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) +LUAU_FASTFLAG(LuauDisablePrimitiveInferenceInLargeTables) +LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit) +LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations) +LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions) TEST_SUITE_BEGIN("TableTests"); @@ -594,9 +599,6 @@ TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_assignmen TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_function_call") { - // CLI-114873 - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( --!strict function get(x) return x.opts["MYOPT"] end @@ -700,7 +702,7 @@ TEST_CASE_FIXTURE(Fixture, "indexers_get_quantified_too") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauSolverV2 && FFlag::LuauNonReentrantGeneralization3) + if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization) CHECK("({a}) -> ()" == toString(requireType("swap"))); else if (FFlag::LuauSolverV2) CHECK("({unknown}) -> ()" == toString(requireType("swap"))); @@ -1153,8 +1155,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_inferred") TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_both_ways") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( type VectorMt = { __add: (Vector, number) -> Vector } local vectorMt: VectorMt @@ -2210,7 +2210,6 @@ local Test: {Table} = { TEST_CASE_FIXTURE(Fixture, "common_table_element_general") { - // CLI-115275 - Bidirectional inference does not always propagate indexer types into the expression DOES_NOT_PASS_NEW_SOLVER_GUARD(); CheckResult result = check(R"( @@ -2380,7 +2379,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table local c : string = t.m("hi") )"); - if (FFlag::DebugLuauGreedyGeneralization && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2) { // FIXME CLI-151985 LUAU_CHECK_ERROR_COUNT(2, result); @@ -2651,7 +2650,6 @@ Type 'number' could not be converted into 'string' in an invariant context)"; TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table") { - // Table properties like HasSuper.p must be invariant. The new solver rightly rejects this program. DOES_NOT_PASS_NEW_SOLVER_GUARD(); CheckResult result = check(R"( @@ -2702,9 +2700,6 @@ Table type '{ x: number, y: number }' not compatible with type 'Super' because t TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer") { - // CLI-114791 Bidirectional inference should be able to cause the inference engine to forget that a table literal has some property - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( --!strict type Super = { x : number } @@ -3752,7 +3747,12 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shap TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type") { - ScopedFastFlag sffs[] = {{FFlag::LuauNonReentrantGeneralization3, true}, {FFlag::LuauReportSubtypingErrors, true}}; + ScopedFastFlag sff[] = { + {FFlag::LuauReportSubtypingErrors, true}, + {FFlag::LuauEagerGeneralization, true}, + {FFlag::LuauSubtypeGenericsAndNegations, true}, + {FFlag::LuauNoMoreInjectiveTypeFunctions, true} + }; CheckResult result = check(R"( local function f(s) @@ -4412,7 +4412,7 @@ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported CHECK(Location{{5, 18}, {5, 23}} == result.errors[3].location); } -TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported") +TEST_CASE_FIXTURE(Fixture, "read_and_write_only_indexers_are_unsupported") { CheckResult result = check(R"( type T = {read [string]: number} @@ -4442,6 +4442,22 @@ TEST_CASE_FIXTURE(Fixture, "infer_write_property") CHECK("({ y: number }) -> ()" == toString(requireType("f"))); } +TEST_CASE_FIXTURE(Fixture, "new_solver_supports_read_write_properties") +{ + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + ScopedFastFlag sff2{FFlag::LuauEnableWriteOnlyProperties, true}; + + CheckResult result = check(R"( + type W = {read x: number} + type X = {write x: boolean} + + type Y = {read ["prop"]: boolean} + type Z = {write ["prop"]: string} + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression") { CheckResult result = check(R"( @@ -4495,6 +4511,59 @@ TEST_CASE_FIXTURE(Fixture, "write_to_read_only_property") CHECK(PropertyAccessViolation::CannotWrite == pav->context); } +TEST_CASE_FIXTURE(Fixture, "write_to_write_only_property") +{ + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true}; + + CheckResult result = check(R"( + function f(t: {write x: number}) + t.x = 5 + end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + +TEST_CASE_FIXTURE(Fixture, "bidirectional_typechecking_with_write_only_property") +{ + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true}; + + CheckResult result = check(R"( + function f(t: {write x: number}) + t.x = 5 + end + + f({ x = 2 }) + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + +TEST_CASE_FIXTURE(Fixture, "read_from_write_only_property") +{ + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true}; + + CheckResult result = check(R"( + function f(t: {write x: number}) + local foo = t.x + end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + + CHECK("Property x of table '{ write x: number }' is write-only" == toString(result.errors[0])); + + PropertyAccessViolation* pav = get(result.errors[0]); + REQUIRE(pav); + + CHECK("{ write x: number }" == toString(pav->table, {true})); + CHECK("x" == pav->key); + CHECK(PropertyAccessViolation::CannotRead == pav->context); +} + TEST_CASE_FIXTURE(Fixture, "write_to_unusually_named_read_only_property") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; @@ -4510,7 +4579,7 @@ TEST_CASE_FIXTURE(Fixture, "write_to_unusually_named_read_only_property") CHECK("Property \"hello world\" of table '{ read [\"hello world\"]: number }' is read-only" == toString(result.errors[0])); } -TEST_CASE_FIXTURE(Fixture, "write_annotations_are_unsupported_even_with_the_new_solver") +TEST_CASE_FIXTURE(Fixture, "write_annotations_are_supported_with_the_new_solver") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; @@ -4519,10 +4588,15 @@ TEST_CASE_FIXTURE(Fixture, "write_annotations_are_unsupported_even_with_the_new_ end )"); - LUAU_REQUIRE_ERROR_COUNT(1, result); + if (FFlag::LuauEnableWriteOnlyProperties) + LUAU_REQUIRE_NO_ERRORS(result); + else + { + LUAU_REQUIRE_ERROR_COUNT(1, result); - CHECK("write keyword is illegal here" == toString(result.errors[0])); - CHECK(Location{{1, 23}, {1, 28}} == result.errors[0].location); + CHECK("write keyword is illegal here" == toString(result.errors[0])); + CHECK(Location{{1, 23}, {1, 28}} == result.errors[0].location); + } } TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported") @@ -4549,10 +4623,8 @@ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported CHECK(Location{{5, 18}, {5, 23}} == result.errors[3].location); } -TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported") +TEST_CASE_FIXTURE(Fixture, "read_and_write_only_indexers_are_unsupported") { - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( type T = {read [string]: number} type U = {write [string]: boolean} @@ -4571,7 +4643,11 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag sff[] = {{FFlag::LuauNonReentrantGeneralization3, true}}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization, true}, + {FFlag::LuauSubtypeGenericsAndNegations, true}, + {FFlag::LuauNoMoreInjectiveTypeFunctions, true} + }; CheckResult result = check(R"( function oc(player, speaker) @@ -4622,7 +4698,7 @@ TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array") end )"); - if (FFlag::LuauSolverV2 && !FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauSolverV2 && !FFlag::LuauEagerGeneralization) { LUAU_CHECK_ERROR_COUNT(1, result); LUAU_CHECK_ERROR(result, NotATable); @@ -4670,7 +4746,7 @@ TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_another_ LUAU_REQUIRE_NO_ERRORS(result); // FIXME CLI-114134. We need to simplify types more consistently. - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) CHECK("({number} & {number}, unknown) -> ()" == toString(requireType("f"))); else CHECK_EQ("(unknown & {number} & {number}, unknown) -> ()", toString(requireType("f"))); @@ -5212,8 +5288,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "write_only_table_field_duplicate") } )"); - LUAU_CHECK_ERROR_COUNT(1, result); - CHECK_EQ("write keyword is illegal here", toString(result.errors[0])); + if (FFlag::LuauEnableWriteOnlyProperties) + LUAU_REQUIRE_NO_ERRORS(result); + else + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK_EQ("write keyword is illegal here", toString(result.errors[0])); + } } TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_musnt_assert") @@ -5661,5 +5742,81 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment") CHECK_EQ("boolean", toString(tm->wantedType)); } +TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, + }; + + ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2}; + + CheckResult result = check(R"( + type Word = "foo" | "bar" + local words: { Word } = { "foo", "bar", "foo" } + )"); + // We expect this to have errors now, as we've set the limit for inference + // in tables so low. + LUAU_REQUIRE_ERROR_COUNT(3, result); +} + +TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_nested_tables") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, + }; + + ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2}; + + CheckResult result = check(R"( + type Word = "foo" | "bar" + local words: {{ Word }} = {{ "foo", "bar", "foo" }} + )"); + LUAU_REQUIRE_ERROR_COUNT(3, result); +} + +TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, + }; + + ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2}; + + CheckResult result = check(R"( + type Word = "foo" | "bar" + local words: { Word } = { "foo", "bar", "foo" } + local otherWords: { Word } = {"foo"} + )"); + LUAU_REQUIRE_ERROR_COUNT(3, result); + for (const auto& err: result.errors) + // Check that all of the errors are localized to `words`, not `otherWords` + CHECK(err.location.begin.line == 2); +} + + +#if 0 + +TEST_CASE_FIXTURE(Fixture, "extremely_large_table" * doctest::timeout(2.0)) +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, + }; + + const std::string source = + "local res = {\n" + + rep("\"foo\",\n", 100'000) + + "}"; + LUAU_REQUIRE_NO_ERRORS(check(source)); + CHECK_EQ("{string}", toString(requireType("res"), {true})); +} + +#endif TEST_SUITE_END(); diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index 9deeb3d7..cb669b1a 100644 --- a/tests/TypeInfer.test.cpp +++ b/tests/TypeInfer.test.cpp @@ -28,7 +28,7 @@ LUAU_FASTFLAG(LuauTypeCheckerAcceptNumberConcats) LUAU_FASTFLAG(LuauPreprocessTypestatedArgument) LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2) LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) LUAU_FASTFLAG(LuauHasPropProperBlock) LUAU_FASTFLAG(LuauStringPartLengthLimit) @@ -37,6 +37,8 @@ LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauAvoidDoubleNegation) LUAU_FASTFLAG(LuauInsertErrorTypesIntoIndexerResult) LUAU_FASTFLAG(LuauSimplifyOutOfLine) +LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations) +LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) using namespace Luau; @@ -445,7 +447,7 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit") #endif ScopedFastInt luauRecursionLimit{FInt::LuauRecursionLimit, limit + 100}; ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit - 100}; - ScopedFastFlag _{FFlag::LuauNonReentrantGeneralization3, false}; + ScopedFastFlag _{FFlag::LuauEagerGeneralization, false}; CheckResult result = check(R"(("foo"))" + rep(":lower()", limit)); @@ -1258,9 +1260,6 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_cache_limit_normalizer") TEST_CASE_FIXTURE(Fixture, "follow_on_new_types_in_substitution") { - // CLI-114134 - DOES_NOT_PASS_NEW_SOLVER_GUARD(); - CheckResult result = check(R"( local obj = {} @@ -2042,8 +2041,10 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert") ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauHasPropProperBlock, true}, - {FFlag::LuauNonReentrantGeneralization3, true}, - {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true} + {FFlag::LuauEagerGeneralization, true}, + {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}, + {FFlag::LuauSubtypeGenericsAndNegations, true}, + {FFlag::LuauNoMoreInjectiveTypeFunctions, true}, }; auto result = check(R"( @@ -2078,8 +2079,10 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauNonReentrantGeneralization3, true}, + {FFlag::LuauEagerGeneralization, true}, {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}, + {FFlag::LuauSubtypeGenericsAndNegations, true}, + {FFlag::LuauNoMoreInjectiveTypeFunctions, true}, }; CheckResult result = check(R"( @@ -2106,15 +2109,19 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2") LUAU_REQUIRE_NO_ERROR(result, ConstraintSolvingIncompleteError); } +#if 0 + TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauHasPropProperBlock, true}, - {FFlag::LuauNonReentrantGeneralization3, true}, + {FFlag::LuauEagerGeneralization, true}, {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}, {FFlag::LuauStringPartLengthLimit, true}, {FFlag::LuauSimplificationRecheckAssumption, true}, + {FFlag::LuauSubtypeGenericsAndNegations, true}, + {FFlag::LuauNoMoreInjectiveTypeFunctions, true}, }; LUAU_REQUIRE_ERRORS(check(R"( @@ -2128,6 +2135,8 @@ local _ )")); } +#endif + TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_missing_follow_table_freeze") { ScopedFastFlag _{FFlag::LuauMagicFreezeCheckBlocked2, true}; diff --git a/tests/TypeInfer.typePacks.test.cpp b/tests/TypeInfer.typePacks.test.cpp index 3df04b71..b52c9308 100644 --- a/tests/TypeInfer.typePacks.test.cpp +++ b/tests/TypeInfer.typePacks.test.cpp @@ -12,10 +12,11 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauInstantiateInSubtyping) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauFixEmptyTypePackStringification) TEST_SUITE_BEGIN("TypePackTests"); @@ -99,7 +100,7 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) CHECK_EQ("((c...) -> (b...), (a) -> (c...), a) -> (b...)", toString(requireType("apply"))); else CHECK_EQ("((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply"))); @@ -339,7 +340,10 @@ local c: Packed REQUIRE(ttvA->instantiatedTypeParams.size() == 1); REQUIRE(ttvA->instantiatedTypePackParams.size() == 1); CHECK_EQ(toString(ttvA->instantiatedTypeParams[0], {true}), "number"); - CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), ""); + if (FFlag::LuauFixEmptyTypePackStringification) + CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), "()"); + else + CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), ""); auto ttvB = get(requireType("b")); REQUIRE(ttvB); diff --git a/tests/TypeInfer.typestates.test.cpp b/tests/TypeInfer.typestates.test.cpp index 7c9383db..90c6ca73 100644 --- a/tests/TypeInfer.typestates.test.cpp +++ b/tests/TypeInfer.typestates.test.cpp @@ -8,11 +8,12 @@ LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow) LUAU_FASTFLAG(LuauReportSubtypingErrors) -LUAU_FASTFLAG(LuauNonReentrantGeneralization3) -LUAU_FASTFLAG(LuauDfgMatchCGScopes) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauPreprocessTypestatedArgument) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) +LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations) +LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions) using namespace Luau; @@ -414,7 +415,13 @@ TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions") TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_future_assignments") { - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauReportSubtypingErrors, true}, {FFlag::LuauNonReentrantGeneralization3, true}}; + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauReportSubtypingErrors, true}, + {FFlag::LuauEagerGeneralization, true}, + {FFlag::LuauSubtypeGenericsAndNegations, true}, + {FFlag::LuauNoMoreInjectiveTypeFunctions, true}, + }; CheckResult result = check(R"( local f @@ -851,24 +858,22 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assign_in_an_if_branch_without_else") TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_table_freeze_in_binary_expr") { - ScopedFastFlag sffs[] = { - {FFlag::LuauPreprocessTypestatedArgument, true}, - {FFlag::LuauDfgMatchCGScopes, true}, - }; - - // Previously this would ICE due to mismatched scopes between the - // constraint generator and the data flow graph. - LUAU_REQUIRE_ERRORS(check(R"( - local _ - if _ or table.freeze(_,_) or table.freeze(_,_) then - end - )")); + ScopedFastFlag _{FFlag::LuauSolverV2, true}; + // CLI-154237: This currently throws an exception due to a mismatch between + // the scopes created in the data flow graph versus the constraint generator. + CHECK_THROWS_AS( + check(R"( + local _ + if _ or table.freeze(_,_) or table.freeze(_,_) then + end + )"), + Luau::InternalCompilerError + ); } TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_in_conditional") { - ScopedFastFlag _{FFlag::LuauDfgMatchCGScopes, true}; - + ScopedFastFlag _{FFlag::LuauSolverV2, true}; // NOTE: This _probably_ should be disallowed, but it is representing that // type stating functions in short circuiting binary expressions do not // reflect their type states. @@ -883,21 +888,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_in_conditional") TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_table_freeze_in_conditional_expr") { - ScopedFastFlag sffs[] = { - {FFlag::LuauPreprocessTypestatedArgument, true}, - {FFlag::LuauDfgMatchCGScopes, true}, - }; - - // Previously this would ICE due to mismatched scopes between the - // constraint generator and the data flow graph. - LUAU_REQUIRE_ERRORS(check(R"( - --!strict - local _ - if - if table.freeze(_,_) then _ else _ - then - end - )")); + ScopedFastFlag _{FFlag::LuauSolverV2, true}; + // CLI-154237: This currently throws an exception due to a mismatch between + // the scopes created in the data flow graph versus the constraint generator. + CHECK_THROWS_AS( + check(R"( + local _ + if + if table.freeze(_,_) then _ else _ + then + end + )"), + Luau::InternalCompilerError + ); } TEST_SUITE_END(); diff --git a/tests/TypeInfer.unionTypes.test.cpp b/tests/TypeInfer.unionTypes.test.cpp index 136fab44..2a2e5a3b 100644 --- a/tests/TypeInfer.unionTypes.test.cpp +++ b/tests/TypeInfer.unionTypes.test.cpp @@ -10,7 +10,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(DebugLuauGreedyGeneralization) +LUAU_FASTFLAG(LuauEagerGeneralization) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) TEST_SUITE_BEGIN("UnionTypes"); @@ -902,7 +902,7 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauGreedyGeneralization) + if (FFlag::LuauEagerGeneralization) CHECK_EQ( "(({ read x: a } & { x: number }) | ({ read x: a } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f")) ); diff --git a/tests/TypeInfer.unknownnever.test.cpp b/tests/TypeInfer.unknownnever.test.cpp index 5294634a..c26797a9 100644 --- a/tests/TypeInfer.unknownnever.test.cpp +++ b/tests/TypeInfer.unknownnever.test.cpp @@ -8,7 +8,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions); -LUAU_FASTFLAG(DebugLuauGreedyGeneralization); TEST_SUITE_BEGIN("TypeInferUnknownNever"); diff --git a/tests/TypePath.test.cpp b/tests/TypePath.test.cpp index 72eb107c..d54506af 100644 --- a/tests/TypePath.test.cpp +++ b/tests/TypePath.test.cpp @@ -17,7 +17,6 @@ using namespace Luau::TypePath; LUAU_FASTFLAG(LuauSolverV2); LUAU_DYNAMIC_FASTINT(LuauTypePathMaximumTraverseSteps); -LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds); struct TypePathFixture : Fixture { diff --git a/tools/test_dcr.py b/tools/test_dcr.py index 3de92b37..a06946c6 100644 --- a/tools/test_dcr.py +++ b/tools/test_dcr.py @@ -103,6 +103,12 @@ def main(): parser.add_argument( "path", action="store", help="Path to the Luau.UnitTest executable" ) + parser.add_argument( + "--fflags", + dest="flags", + action="store", + help="Set extra FFlags", + ) parser.add_argument( "--dump", dest="dump", @@ -136,9 +142,11 @@ def main(): failList = loadFailList() - flags = ["true", "LuauSolverV2"] + flags = "true,LuauSolverV2" + if args.flags: + flags += "," + args.flags - commandLine = [args.path, "--reporters=xml", "--fflags=" + ",".join(flags)] + commandLine = [args.path, "--reporters=xml", "--fflags=" + flags] if args.random_seed: commandLine.append("--random-seed=" + str(args.random_seed))