From 59658182835dc39f598a4fc16ba0b177b8e6f55b Mon Sep 17 00:00:00 2001 From: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Date: Tue, 27 May 2025 14:24:46 -0700 Subject: [PATCH] Sync to upstream/release/675 (#1845) ## General - Introduce `Frontend::parseModules` for parsing a group of modules at once. - Support chained function types in the CST. ## New Type Solver - Enable write-only table properties (described in [this RFC](https://rfcs.luau.org/property-writeonly.html)). - Disable singleton inference for large tables to improve performance. - Fix a bug that occurs when we try to expand a type alias to itself. - Catch cancelation during the type-checking phase in addition to during constraint solving. - Fix stringification of the empty type pack: `()`. - Improve errors for calls being rejected on the primitive `function` type. - Rework generalization: We now generalize types as soon as the last constraint relating to them is finished. We think this will reduce the number of cases where type inference fails to complete and reduce the number of instances where `*blocked*` types appear in the inference result. ## VM/Runtime - Dynamically disable native execution for functions that incur a slowdown (relative to bytecode execution). - Improve names for `thread`/`closure`/`proto` in the Luau heap dump. --- Co-authored-by: Andy Friesen Co-authored-by: Ariel Weiss Co-authored-by: Aviral Goel Co-authored-by: Hunter Goldstein Co-authored-by: Talha Pathan Co-authored-by: Varun Saini Co-authored-by: Vighnesh Vijay Co-authored-by: Vyacheslav Egorov --------- Co-authored-by: Hunter Goldstein Co-authored-by: Alexander Youngblood Co-authored-by: Menarul Alam Co-authored-by: Aviral Goel Co-authored-by: Vighnesh Co-authored-by: Vyacheslav Egorov Co-authored-by: Ariel Weiss Co-authored-by: Andy Friesen --- Analysis/include/Luau/ConstraintGenerator.h | 4 + Analysis/include/Luau/ConstraintSolver.h | 2 +- Analysis/include/Luau/Frontend.h | 1 + Analysis/include/Luau/OverloadResolution.h | 2 +- Analysis/include/Luau/Subtyping.h | 5 + Analysis/src/AutocompleteCore.cpp | 86 +- Analysis/src/BuiltinDefinitions.cpp | 6 +- Analysis/src/Clone.cpp | 9 +- Analysis/src/Constraint.cpp | 14 +- Analysis/src/ConstraintGenerator.cpp | 93 +- Analysis/src/ConstraintSolver.cpp | 71 +- Analysis/src/DataFlowGraph.cpp | 40 +- Analysis/src/Error.cpp | 14 +- Analysis/src/FragmentAutocomplete.cpp | 11 +- Analysis/src/Frontend.cpp | 85 +- Analysis/src/Generalization.cpp | 117 +- Analysis/src/InferPolarity.cpp | 4 +- Analysis/src/OverloadResolution.cpp | 7 +- Analysis/src/Subtyping.cpp | 65 +- Analysis/src/ToString.cpp | 55 +- Analysis/src/Transpiler.cpp | 1092 ++++++++++++++++++- Analysis/src/Type.cpp | 1 - Analysis/src/TypeArena.cpp | 1 - Analysis/src/TypeAttach.cpp | 4 +- Analysis/src/TypeChecker2.cpp | 45 +- Analysis/src/TypeFunction.cpp | 20 +- Analysis/src/TypePath.cpp | 5 - Analysis/src/TypeUtils.cpp | 9 +- Analysis/src/Unifier2.cpp | 39 +- Ast/include/Luau/Parser.h | 2 + Ast/src/Parser.cpp | 1046 ++++++++++++------ CodeGen/include/Luau/CodeGen.h | 2 + CodeGen/src/CodeGenA64.cpp | 19 +- CodeGen/src/CodeGenContext.cpp | 15 + CodeGen/src/EmitCommonX64.cpp | 21 +- VM/src/lgcdebug.cpp | 50 +- tests/Autocomplete.test.cpp | 8 +- tests/Conformance.test.cpp | 32 +- tests/Differ.test.cpp | 4 - tests/FragmentAutocomplete.test.cpp | 19 - tests/Generalization.test.cpp | 6 +- tests/InferPolarity.test.cpp | 4 +- tests/Linter.test.cpp | 4 +- tests/Module.test.cpp | 2 - tests/Normalize.test.cpp | 56 +- tests/Parser.test.cpp | 9 +- tests/Subtyping.test.cpp | 16 + tests/ToDot.test.cpp | 2 - tests/ToString.test.cpp | 13 + tests/Transpiler.test.cpp | 272 +++-- tests/TypeFunction.test.cpp | 4 +- tests/TypeFunction.user.test.cpp | 19 +- tests/TypeInfer.aliases.test.cpp | 20 +- tests/TypeInfer.builtins.test.cpp | 4 +- tests/TypeInfer.classes.test.cpp | 5 - tests/TypeInfer.functions.test.cpp | 23 +- tests/TypeInfer.intersectionTypes.test.cpp | 3 - tests/TypeInfer.loops.test.cpp | 7 +- tests/TypeInfer.modules.test.cpp | 4 +- tests/TypeInfer.operators.test.cpp | 23 +- tests/TypeInfer.provisional.test.cpp | 34 +- tests/TypeInfer.refinements.test.cpp | 88 +- tests/TypeInfer.tables.test.cpp | 213 +++- tests/TypeInfer.test.cpp | 27 +- tests/TypeInfer.typePacks.test.cpp | 10 +- tests/TypeInfer.typestates.test.cpp | 67 +- tests/TypeInfer.unionTypes.test.cpp | 4 +- tests/TypeInfer.unknownnever.test.cpp | 1 - tests/TypePath.test.cpp | 1 - tools/test_dcr.py | 12 +- 70 files changed, 3153 insertions(+), 925 deletions(-) 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))