From 8863bfc950d52e9e7b468354d353ca43623da4f6 Mon Sep 17 00:00:00 2001 From: Vighnesh-V Date: Fri, 8 Aug 2025 10:18:16 -0700 Subject: [PATCH] Sync to upstream/release/686 (#1948) ## General This week has been spent mostly on fixing bugs in incremental autocomplete as well as making the new Type Solver more stable. - Fixes a bug where registered "require" aliases were case-sensitive instead of case-insensitive. ### New Type Solver - Adjust literal sub typing logic to account for unreduced type functions - Implement a number of subtyping stack utilization improvements - Emit a single error if an internal type escapes a module's interface - Checked function errors in the New Non Strict warn about incorrect argument use with one-indexed positions, e.g. `argument #1 was used incorrectly` instead of `argument #0 was used incorrectly`. - Improvements to type function reduction that let us progress further while reducing - Augment the generalization system to not emit duplicate constraints. - Fix a bug where we didn't seal tables in modules that failed to complete typechecking. ### Fragment Autocomplete - Provide richer autocomplete suggestions inside of for loops - Provide richer autocomplete suggestions inside of interpolated string expressions - Improve the quality of error messages when typing out interpolated strings. ### Compiler - Fixes REX encoding of extended byte registers for the x86 assembly code generation. - Fixes for table shape constant data encoding --- Co-authored-by: Andy Friesen Co-authored-by: Ariel Weiss Co-authored-by: Hunter Goldstein Co-authored-by: Sora Kanosue Co-authored-by: Varun Saini Co-authored-by: Vighnesh Vijay Co-authored-by: Vyacheslav Egorov --- Analysis/include/Luau/Constraint.h | 3 - Analysis/include/Luau/ConstraintSolver.h | 21 +- Analysis/include/Luau/DataFlowGraph.h | 4 - Analysis/include/Luau/FragmentAutocomplete.h | 1 - Analysis/include/Luau/Frontend.h | 4 + Analysis/include/Luau/Subtyping.h | 18 +- Analysis/include/Luau/SubtypingVariance.h | 19 + Analysis/include/Luau/TypeInfer.h | 1 - Analysis/src/AstQuery.cpp | 3 +- Analysis/src/AutocompleteCore.cpp | 10 +- Analysis/src/BuiltinDefinitions.cpp | 243 ++------ Analysis/src/BuiltinTypeFunctions.cpp | 102 ++-- Analysis/src/Clone.cpp | 44 +- Analysis/src/Constraint.cpp | 159 ----- Analysis/src/ConstraintGenerator.cpp | 207 +++---- Analysis/src/ConstraintSolver.cpp | 607 +++++++------------ Analysis/src/DataFlowGraph.cpp | 126 +--- Analysis/src/EmbeddedBuiltinDefinitions.cpp | 30 +- Analysis/src/EqSatSimplification.cpp | 20 +- Analysis/src/Error.cpp | 20 +- Analysis/src/FragmentAutocomplete.cpp | 289 ++++----- Analysis/src/Frontend.cpp | 76 +-- Analysis/src/Generalization.cpp | 140 ++--- Analysis/src/InferPolarity.cpp | 57 +- Analysis/src/Module.cpp | 45 +- Analysis/src/NonStrictTypeChecker.cpp | 47 +- Analysis/src/Normalize.cpp | 3 +- Analysis/src/OverloadResolution.cpp | 28 +- Analysis/src/Simplify.cpp | 76 +-- Analysis/src/Substitution.cpp | 28 +- Analysis/src/Subtyping.cpp | 160 ++--- Analysis/src/ToDot.cpp | 55 +- Analysis/src/ToString.cpp | 57 +- Analysis/src/Type.cpp | 10 +- Analysis/src/TypeAttach.cpp | 115 ++-- Analysis/src/TypeChecker2.cpp | 237 +++----- Analysis/src/TypeFunction.cpp | 12 +- Analysis/src/TypeFunctionRuntime.cpp | 5 +- Analysis/src/TypeFunctionRuntimeBuilder.cpp | 4 +- Analysis/src/TypeInfer.cpp | 61 +- Analysis/src/TypeUtils.cpp | 29 +- Analysis/src/Unifier2.cpp | 59 +- Ast/include/Luau/Lexer.h | 14 +- Ast/include/Luau/Parser.h | 1 - Ast/src/Lexer.cpp | 8 + Ast/src/Parser.cpp | 419 +++---------- CodeGen/src/AssemblyBuilderX64.cpp | 83 ++- Compiler/src/Compiler.cpp | 7 +- Require/Runtime/src/RequireImpl.cpp | 20 +- Sources.cmake | 1 + tests/AssemblyBuilderX64.test.cpp | 34 +- tests/Autocomplete.test.cpp | 14 +- tests/Conformance.test.cpp | 70 ++- tests/Fixture.cpp | 2 + tests/Fixture.h | 2 - tests/FragmentAutocomplete.test.cpp | 210 ++++++- tests/Frontend.test.cpp | 3 - tests/Generalization.test.cpp | 27 +- tests/InferPolarity.test.cpp | 7 +- tests/Module.test.cpp | 37 +- tests/NonStrictTypeChecker.test.cpp | 23 +- tests/Parser.test.cpp | 140 ++++- tests/RequireByString.test.cpp | 14 + tests/RuntimeLimits.test.cpp | 69 ++- tests/Subtyping.test.cpp | 6 +- tests/ToString.test.cpp | 6 - tests/TypeFunction.test.cpp | 96 ++- tests/TypeFunction.user.test.cpp | 40 +- tests/TypeInfer.aliases.test.cpp | 11 +- tests/TypeInfer.annotations.test.cpp | 37 +- tests/TypeInfer.anyerror.test.cpp | 10 +- tests/TypeInfer.builtins.test.cpp | 29 +- tests/TypeInfer.definitions.test.cpp | 18 +- tests/TypeInfer.functions.test.cpp | 98 ++- tests/TypeInfer.generics.test.cpp | 56 +- tests/TypeInfer.intersectionTypes.test.cpp | 9 +- tests/TypeInfer.modules.test.cpp | 56 +- tests/TypeInfer.negations.test.cpp | 6 +- tests/TypeInfer.oop.test.cpp | 18 +- tests/TypeInfer.refinements.test.cpp | 47 +- tests/TypeInfer.singletons.test.cpp | 8 +- tests/TypeInfer.tables.test.cpp | 258 ++------ tests/TypeInfer.test.cpp | 31 +- tests/TypeInfer.typePacks.test.cpp | 9 +- tests/TypeInfer.typestates.test.cpp | 5 +- tests/TypeInfer.unionTypes.test.cpp | 3 - tests/TypeInfer.unknownnever.test.cpp | 6 +- tools/natvis/CodeGen.natvis | 5 + 88 files changed, 2100 insertions(+), 3208 deletions(-) create mode 100644 Analysis/include/Luau/SubtypingVariance.h diff --git a/Analysis/include/Luau/Constraint.h b/Analysis/include/Luau/Constraint.h index f9b68eb2..0d5ad81b 100644 --- a/Analysis/include/Luau/Constraint.h +++ b/Analysis/include/Luau/Constraint.h @@ -337,9 +337,6 @@ struct Constraint std::vector> dependencies; - // Clip with LuauUseOrderedTypeSetsInConstraints - DenseHashSet getMaybeMutatedFreeTypes_DEPRECATED() const; - TypeIds getMaybeMutatedFreeTypes() const; }; diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h index e7781e88..64c82033 100644 --- a/Analysis/include/Luau/ConstraintSolver.h +++ b/Analysis/include/Luau/ConstraintSolver.h @@ -13,6 +13,7 @@ #include "Luau/Normalize.h" #include "Luau/OrderedSet.h" #include "Luau/Substitution.h" +#include "Luau/SubtypingVariance.h" #include "Luau/ToString.h" #include "Luau/Type.h" #include "Luau/TypeCheckLimits.h" @@ -41,6 +42,20 @@ struct HashBlockedConstraintId size_t operator()(const BlockedConstraintId& bci) const; }; +struct SubtypeConstraintRecord +{ + TypeId subTy = nullptr; + TypeId superTy = nullptr; + SubtypingVariance variance = SubtypingVariance::Invalid; + + bool operator==(const SubtypeConstraintRecord& other) const; +}; + +struct HashSubtypeConstraintRecord +{ + size_t operator()(const SubtypeConstraintRecord& c) const; +}; + struct ModuleResolver; struct InstantiationSignature @@ -127,16 +142,14 @@ struct ConstraintSolver // A mapping from free types to the number of unresolved constraints that mention them. DenseHashMap unresolvedConstraints{{}}; - // Clip with LuauUseOrderedTypeSetsInConstraints - std::unordered_map, DenseHashSet> maybeMutatedFreeTypes_DEPRECATED; - std::unordered_map> mutatedFreeTypeToConstraint_DEPRECATED; - std::unordered_map, TypeIds> maybeMutatedFreeTypes; std::unordered_map> mutatedFreeTypeToConstraint; // Irreducible/uninhabited type functions or type pack functions. DenseHashSet uninhabitedTypeFunctions{{}}; + DenseHashMap seenConstraints{{}}; + // The set of types that will definitely be unchanged by generalization. DenseHashSet generalizedTypes_{nullptr}; const NotNull> generalizedTypes{&generalizedTypes_}; diff --git a/Analysis/include/Luau/DataFlowGraph.h b/Analysis/include/Luau/DataFlowGraph.h index 31c93494..6d544c16 100644 --- a/Analysis/include/Luau/DataFlowGraph.h +++ b/Analysis/include/Luau/DataFlowGraph.h @@ -93,9 +93,6 @@ struct DfgScope std::optional lookup(DefId def, const std::string& key) const; void inherit(const DfgScope* childScope); - - bool canUpdateDefinition(Symbol symbol) const; - bool canUpdateDefinition(DefId def, const std::string& key) const; }; struct DataFlowResult @@ -133,7 +130,6 @@ private: /// A stack of scopes used by the visitor to see where we are. ScopeStack scopeStack; NotNull currentScope(); - DfgScope* currentScope_DEPRECATED(); struct FunctionCapture { diff --git a/Analysis/include/Luau/FragmentAutocomplete.h b/Analysis/include/Luau/FragmentAutocomplete.h index 6dfd1378..d1023ecd 100644 --- a/Analysis/include/Luau/FragmentAutocomplete.h +++ b/Analysis/include/Luau/FragmentAutocomplete.h @@ -75,7 +75,6 @@ struct FragmentAutocompleteResult { ModulePtr incrementalModule; Scope* freshScope; - TypeArena arenaForAutocomplete_DEPRECATED; AutocompleteResult acResults; }; diff --git a/Analysis/include/Luau/Frontend.h b/Analysis/include/Luau/Frontend.h index 915e4311..4887583f 100644 --- a/Analysis/include/Luau/Frontend.h +++ b/Analysis/include/Luau/Frontend.h @@ -170,6 +170,8 @@ struct Frontend double timeParse = 0; double timeCheck = 0; double timeLint = 0; + + size_t dynamicConstraintsCreated = 0; }; Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options = {}); @@ -243,6 +245,7 @@ private: std::optional environmentScope, bool forAutocomplete, bool recordJsonLog, + Frontend::Stats& stats, TypeCheckLimits typeCheckLimits ); @@ -333,6 +336,7 @@ ModulePtr check( FrontendOptions options, TypeCheckLimits limits, bool recordJsonLog, + Frontend::Stats& stats, std::function writeJsonLog ); diff --git a/Analysis/include/Luau/Subtyping.h b/Analysis/include/Luau/Subtyping.h index 96a877f1..90cbd7e3 100644 --- a/Analysis/include/Luau/Subtyping.h +++ b/Analysis/include/Luau/Subtyping.h @@ -4,6 +4,7 @@ #include "Luau/DenseHash.h" #include "Luau/EqSatSimplification.h" #include "Luau/Set.h" +#include "Luau/SubtypingVariance.h" #include "Luau/TypeCheckLimits.h" #include "Luau/TypeFunction.h" #include "Luau/TypeFwd.h" @@ -32,17 +33,6 @@ struct TableIndexer; struct TypeArena; struct TypeCheckLimits; -enum class SubtypingVariance -{ - // Used for an empty key. Should never appear in actual code. - Invalid, - Covariant, - // This is used to identify cases where we have a covariant + a - // contravariant reason and we need to merge them. - Contravariant, - Invariant, -}; - struct SubtypingReasoning { // The path, relative to the _root subtype_, where subtyping failed. @@ -327,6 +317,12 @@ private: [[noreturn]] void unexpected(TypeId ty); [[noreturn]] void unexpected(TypePackId tp); + + SubtypingResult trySemanticSubtyping(SubtypingEnvironment& env, + TypeId subTy, + TypeId superTy, + NotNull scope, + SubtypingResult& original); }; } // namespace Luau diff --git a/Analysis/include/Luau/SubtypingVariance.h b/Analysis/include/Luau/SubtypingVariance.h new file mode 100644 index 00000000..4fd7cd4b --- /dev/null +++ b/Analysis/include/Luau/SubtypingVariance.h @@ -0,0 +1,19 @@ +// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details + +#pragma once + +namespace Luau +{ + +enum class SubtypingVariance +{ + // Used for an empty key. Should never appear in actual code. + Invalid, + Covariant, + // This is used to identify cases where we have a covariant + a + // contravariant reason and we need to merge them. + Contravariant, + Invariant, +}; + +} diff --git a/Analysis/include/Luau/TypeInfer.h b/Analysis/include/Luau/TypeInfer.h index 524908f0..6628df52 100644 --- a/Analysis/include/Luau/TypeInfer.h +++ b/Analysis/include/Luau/TypeInfer.h @@ -130,7 +130,6 @@ struct TypeChecker const PredicateVec& predicates = {} ); WithPredicate checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional expectedType = std::nullopt); - WithPredicate checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional expectedType = std::nullopt); WithPredicate checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr); WithPredicate checkExpr(const ScopePtr& scope, const AstExprError& expr); WithPredicate checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional expectedType = std::nullopt); diff --git a/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index 97d17641..6ba94ec4 100644 --- a/Analysis/src/AstQuery.cpp +++ b/Analysis/src/AstQuery.cpp @@ -12,7 +12,6 @@ #include LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau { @@ -526,7 +525,7 @@ static std::optional getMetatableDocumentation( return std::nullopt; TypeId followed; - if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (FFlag::LuauSolverV2) { if (indexIt->second.readTy) followed = follow(*indexIt->second.readTy); diff --git a/Analysis/src/AutocompleteCore.cpp b/Analysis/src/AutocompleteCore.cpp index 49a22fa2..f04996ad 100644 --- a/Analysis/src/AutocompleteCore.cpp +++ b/Analysis/src/AutocompleteCore.cpp @@ -21,11 +21,9 @@ #include LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTINT(LuauTypeInferIterationLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames) -LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows) LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3) static const std::unordered_set kStatementStartingKeywords = @@ -79,8 +77,7 @@ static ParenthesesRecommendation getParenRecommendationForIntersect(const Inters ParenthesesRecommendation rec = ParenthesesRecommendation::None; for (Luau::TypeId partId : intersect->parts) { - if (FFlag::LuauAutocompleteMissingFollows) - partId = follow(partId); + partId = follow(partId); if (auto partFunc = Luau::get(partId)) { rec = std::max(rec, getParenRecommendationForFunc(partFunc, nodes)); @@ -370,7 +367,7 @@ static void autocompleteProps( if (indexIt != mtable->props.end()) { TypeId followed; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2) followed = follow(*indexIt->second.readTy); else followed = follow(indexIt->second.type_DEPRECATED()); @@ -1636,8 +1633,7 @@ static std::optional autocompleteStringParams( { for (TypeId part : intersect->parts) { - if (FFlag::LuauAutocompleteMissingFollows) - part = follow(part); + part = follow(part); if (auto candidateFunctionType = Luau::get(part)) { if (std::optional ret = performCallback(candidateFunctionType)) diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index 1510912f..fbf1237a 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -33,9 +33,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3) -LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements) LUAU_FASTFLAGVARIABLE(LuauUpdateSetMetatableTypeSignature) LUAU_FASTFLAGVARIABLE(LuauUpdateGetMetatableTypeSignature) LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) @@ -339,14 +337,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC auto it = stringMetatableTable->props.find("__index"); LUAU_ASSERT(it != stringMetatableTable->props.end()); - - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - addGlobalBinding(globals, "string", *it->second.readTy, "@luau"); - addGlobalBinding(globals, "string", *it->second.writeTy, "@luau"); - } - else - addGlobalBinding(globals, "string", it->second.type_DEPRECATED(), "@luau"); + addGlobalBinding(globals, "string", *it->second.readTy, "@luau"); + addGlobalBinding(globals, "string", *it->second.writeTy, "@luau"); // Setup 'vector' metatable if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end()) @@ -504,20 +496,10 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC ttv->props["foreach"].deprecated = true; ttv->props["foreachi"].deprecated = true; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - attachMagicFunction(*ttv->props["pack"].readTy, std::make_shared()); - if (FFlag::LuauTableCloneClonesType3) - attachMagicFunction(*ttv->props["clone"].readTy, std::make_shared()); - attachMagicFunction(*ttv->props["freeze"].readTy, std::make_shared()); - } - else - { - attachMagicFunction(ttv->props["pack"].type_DEPRECATED(), std::make_shared()); - if (FFlag::LuauTableCloneClonesType3) - attachMagicFunction(ttv->props["clone"].type_DEPRECATED(), std::make_shared()); - attachMagicFunction(ttv->props["freeze"].type_DEPRECATED(), std::make_shared()); - } + attachMagicFunction(*ttv->props["pack"].readTy, std::make_shared()); + if (FFlag::LuauTableCloneClonesType3) + attachMagicFunction(*ttv->props["clone"].readTy, std::make_shared()); + attachMagicFunction(*ttv->props["freeze"].readTy, std::make_shared()); } TypeId requireTy = getGlobalBinding(globals, "require"); @@ -665,114 +647,68 @@ bool MagicFormat::infer(const MagicFunctionCallContext& context) { TypeArena* arena = context.solver->arena; - if (FFlag::LuauStringFormatImprovements) + auto iter = begin(context.arguments); + + // we'll suppress any errors for `string.format` if the format string is error suppressing. + if (iter == end(context.arguments) || shouldSuppressErrors(context.solver->normalizer, follow(*iter)) == ErrorSuppression::Suppress) { - auto iter = begin(context.arguments); - - // we'll suppress any errors for `string.format` if the format string is error suppressing. - if (iter == end(context.arguments) || shouldSuppressErrors(context.solver->normalizer, follow(*iter)) == ErrorSuppression::Suppress) - { - TypePackId resultPack = arena->addTypePack({context.solver->builtinTypes->stringType}); - asMutable(context.result)->ty.emplace(resultPack); - return true; - } - - AstExprConstantString* fmt = nullptr; - if (auto index = context.callSite->func->as(); index && context.callSite->self) - fmt = unwrapGroup(index->expr)->as(); - - if (!context.callSite->self && context.callSite->args.size > 0) - fmt = context.callSite->args.data[0]->as(); - - std::optional formatString; - if (fmt) - formatString = {fmt->value.data, fmt->value.size}; - else if (auto singleton = get(follow(*iter))) - { - if (auto stringSingleton = get(singleton)) - formatString = {stringSingleton->value}; - } - - if (!formatString) - return false; - - std::vector expected = parseFormatString(context.solver->builtinTypes, formatString->data(), formatString->size()); - const auto& [params, tail] = flatten(context.arguments); - - size_t paramOffset = 1; - - // unify the prefix one argument at a time - needed if any of the involved types are free - for (size_t i = 0; i < expected.size() && i + paramOffset < params.size(); ++i) - { - context.solver->unify(context.constraint, params[i + paramOffset], expected[i]); - } - - // if we know the argument count or if we have too many arguments for sure, we can issue an error - size_t numActualParams = params.size(); - size_t numExpectedParams = expected.size() + 1; // + 1 for the format string - - if (numExpectedParams != numActualParams && (!tail || numExpectedParams < numActualParams)) - context.solver->reportError(TypeError{context.callSite->location, CountMismatch{numExpectedParams, std::nullopt, numActualParams}}); - - // This is invoked at solve time, so we just need to provide a type for the result of :/.format TypePackId resultPack = arena->addTypePack({context.solver->builtinTypes->stringType}); asMutable(context.result)->ty.emplace(resultPack); - return true; } - else + + AstExprConstantString* fmt = nullptr; + if (auto index = context.callSite->func->as(); index && context.callSite->self) + fmt = unwrapGroup(index->expr)->as(); + + if (!context.callSite->self && context.callSite->args.size > 0) + fmt = context.callSite->args.data[0]->as(); + + std::optional formatString; + if (fmt) + formatString = {fmt->value.data, fmt->value.size}; + else if (auto singleton = get(follow(*iter))) { - AstExprConstantString* fmt = nullptr; - if (auto index = context.callSite->func->as(); index && context.callSite->self) - { - if (auto group = index->expr->as()) - fmt = group->expr->as(); - else - fmt = index->expr->as(); - } - - if (!context.callSite->self && context.callSite->args.size > 0) - fmt = context.callSite->args.data[0]->as(); - - if (!fmt) - return false; - - std::vector expected = parseFormatString(context.solver->builtinTypes, fmt->value.data, fmt->value.size); - const auto& [params, tail] = flatten(context.arguments); - - size_t paramOffset = 1; - - // unify the prefix one argument at a time - needed if any of the involved types are free - for (size_t i = 0; i < expected.size() && i + paramOffset < params.size(); ++i) - { - context.solver->unify(context.constraint, params[i + paramOffset], expected[i]); - } - - // if we know the argument count or if we have too many arguments for sure, we can issue an error - size_t numActualParams = params.size(); - size_t numExpectedParams = expected.size() + 1; // + 1 for the format string - - if (numExpectedParams != numActualParams && (!tail || numExpectedParams < numActualParams)) - context.solver->reportError(TypeError{context.callSite->location, CountMismatch{numExpectedParams, std::nullopt, numActualParams}}); - - // This is invoked at solve time, so we just need to provide a type for the result of :/.format - TypePackId resultPack = arena->addTypePack({context.solver->builtinTypes->stringType}); - asMutable(context.result)->ty.emplace(resultPack); - - return true; + if (auto stringSingleton = get(singleton)) + formatString = {stringSingleton->value}; } + + if (!formatString) + return false; + + std::vector expected = parseFormatString(context.solver->builtinTypes, formatString->data(), formatString->size()); + const auto& [params, tail] = flatten(context.arguments); + + size_t paramOffset = 1; + + // unify the prefix one argument at a time - needed if any of the involved types are free + for (size_t i = 0; i < expected.size() && i + paramOffset < params.size(); ++i) + { + context.solver->unify(context.constraint, params[i + paramOffset], expected[i]); + } + + // if we know the argument count or if we have too many arguments for sure, we can issue an error + size_t numActualParams = params.size(); + size_t numExpectedParams = expected.size() + 1; // + 1 for the format string + + if (numExpectedParams != numActualParams && (!tail || numExpectedParams < numActualParams)) + context.solver->reportError(TypeError{context.callSite->location, CountMismatch{numExpectedParams, std::nullopt, numActualParams}}); + + // This is invoked at solve time, so we just need to provide a type for the result of :/.format + TypePackId resultPack = arena->addTypePack({context.solver->builtinTypes->stringType}); + asMutable(context.result)->ty.emplace(resultPack); + + return true; } bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context) { - if (FFlag::LuauStringFormatImprovements) - { - auto iter = begin(context.arguments); + auto iter = begin(context.arguments); - if (iter == end(context.arguments)) - { - context.typechecker->reportError( - CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location + if (iter == end(context.arguments)) + { + context.typechecker->reportError( + CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location ); return true; } @@ -848,73 +784,6 @@ bool MagicFormat::typeCheck(const MagicFunctionTypeCheckContext& context) } return true; - } - else - { - AstExprConstantString* fmt = nullptr; - if (auto index = context.callSite->func->as(); index && context.callSite->self) - { - if (auto group = index->expr->as()) - fmt = group->expr->as(); - else - fmt = index->expr->as(); - } - - if (!context.callSite->self && context.callSite->args.size > 0) - fmt = context.callSite->args.data[0]->as(); - - if (!fmt) - { - context.typechecker->reportError( - CountMismatch{1, std::nullopt, 0, CountMismatch::Arg, true, "string.format"}, context.callSite->location - ); - return true; - } - - // CLI-150726: The block below effectively constructs a type pack and then type checks it by going parameter-by-parameter. - // This does _not_ handle cases like: - // - // local foo : () -> (...string) = (nil :: any) - // print(string.format("%s %d %s", foo())) - // - // ... which should be disallowed. - - std::vector expected = parseFormatString(context.builtinTypes, fmt->value.data, fmt->value.size); - - const auto& [params, tail] = flatten(context.arguments); - - size_t paramOffset = 1; - // Compare the expressions passed with the types the function expects to determine whether this function was called with : or . - bool calledWithSelf = expected.size() == context.callSite->args.size; - // unify the prefix one argument at a time - for (size_t i = 0; i < expected.size() && i + paramOffset < params.size(); ++i) - { - TypeId actualTy = params[i + paramOffset]; - TypeId expectedTy = expected[i]; - Location location = - context.callSite->args.data[std::min(context.callSite->args.size - 1, i + (calledWithSelf ? 0 : paramOffset))]->location; - // use subtyping instead here - SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope); - - if (!result.isSubtype) - { - switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy)) - { - case ErrorSuppression::Suppress: - break; - case ErrorSuppression::NormalizationFailed: - break; - case ErrorSuppression::DoNotSuppress: - Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result); - - if (!reasonings.suppressed) - context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location); - } - } - } - - return true; - } } static std::vector parsePatternString(NotNull builtinTypes, const char* data, size_t size) diff --git a/Analysis/src/BuiltinTypeFunctions.cpp b/Analysis/src/BuiltinTypeFunctions.cpp index 2883bdeb..008ae018 100644 --- a/Analysis/src/BuiltinTypeFunctions.cpp +++ b/Analysis/src/BuiltinTypeFunctions.cpp @@ -17,16 +17,14 @@ #include "Luau/UserDefinedTypeFunction.h" #include "Luau/VisitType.h" -LUAU_FASTFLAG(LuauNotAllBinaryTypeFunsHaveDefaults) LUAU_FASTFLAG(LuauEmptyStringInKeyOf) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying) LUAU_FASTFLAG(LuauOccursCheckForRefinement) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) +LUAU_FASTFLAGVARIABLE(LuauDoNotBlockOnStuckTypeFunctions) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) LUAU_FASTFLAGVARIABLE(LuauRefineNoRefineAlways) @@ -682,6 +680,16 @@ TypeFunctionReductionResult concatTypeFunction( return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; } +namespace +{ +bool isBlockedOrUnsolvedType(TypeId ty) +{ + if (auto tfit = get(ty); tfit && tfit->state == TypeFunctionInstanceState::Unsolved) + return true; + return is(ty); +} +} // namespace + TypeFunctionReductionResult andTypeFunction( TypeId instance, const std::vector& typeParams, @@ -748,10 +756,20 @@ TypeFunctionReductionResult orTypeFunction( // check to see if both operand types are resolved enough, and wait to reduce if not if (FFlag::LuauEagerGeneralization4) { - if (is(lhsTy)) - return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; - else if (is(rhsTy)) - return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}}; + if (FFlag::LuauDoNotBlockOnStuckTypeFunctions) + { + if (isBlockedOrUnsolvedType(lhsTy)) + return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; + else if (isBlockedOrUnsolvedType(rhsTy)) + return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}}; + } + else + { + if (is(lhsTy)) + return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; + else if (is(rhsTy)) + return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}}; + } } else { @@ -795,10 +813,20 @@ static TypeFunctionReductionResult comparisonTypeFunction( if (FFlag::LuauEagerGeneralization4) { - if (is(lhsTy)) - return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; - else if (is(rhsTy)) - return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}}; + if (FFlag::LuauDoNotBlockOnStuckTypeFunctions) + { + if (isBlockedOrUnsolvedType(lhsTy)) + return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; + else if (isBlockedOrUnsolvedType(rhsTy)) + return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}}; + } + else + { + if (is(lhsTy)) + return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; + else if (is(rhsTy)) + return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}}; + } } else { @@ -1302,8 +1330,18 @@ TypeFunctionReductionResult refineTypeFunction( return {targetTy, {}}; } - const bool targetIsPending = FFlag::LuauEagerGeneralization4 ? is(targetTy) - : isPending(targetTy, ctx->solver); + bool targetIsPending = false; + + if (FFlag::LuauEagerGeneralization4) + { + targetIsPending = FFlag::LuauDoNotBlockOnStuckTypeFunctions + ? isBlockedOrUnsolvedType(targetTy) + : is(targetTy); + } + else + { + targetIsPending = isPending(targetTy, ctx->solver); + } // check to see if both operand types are resolved enough, and wait to reduce if not if (targetIsPending) @@ -2133,20 +2171,15 @@ bool searchPropsAndIndexer( if (tblProps.find(stringSingleton->value) != tblProps.end()) { - TypeId propTy; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - Property& prop = tblProps.at(stringSingleton->value); + Property& prop = tblProps.at(stringSingleton->value); - if (prop.readTy) - propTy = follow(*prop.readTy); - else if (prop.writeTy) - propTy = follow(*prop.writeTy); - else // found the property, but there was no type associated with it - return false; - } - else - propTy = follow(tblProps.at(stringSingleton->value).type_DEPRECATED()); + TypeId propTy; + if (prop.readTy) + propTy = follow(*prop.readTy); + else if (prop.writeTy) + propTy = follow(*prop.writeTy); + else // found the property, but there was no type associated with it + return false; // property is a union type -> we need to extend our reduction type if (auto propUnionTy = get(propTy)) @@ -2804,21 +2837,10 @@ void BuiltinTypeFunctions::addToScope(NotNull arena, NotNull s scope->exportedTypeBindings[keyofFunc.name] = mkUnaryTypeFunction(&keyofFunc); scope->exportedTypeBindings[rawkeyofFunc.name] = mkUnaryTypeFunction(&rawkeyofFunc); - if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults) - { - scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunction(&indexFunc); - scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunction(&rawgetFunc); - } - else - { - scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunctionWithDefault(&indexFunc); - scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunctionWithDefault(&rawgetFunc); - } + scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunction(&indexFunc); + scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunction(&rawgetFunc); - if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults) - scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunction(&setmetatableFunc); - else - scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunctionWithDefault(&setmetatableFunc); + scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunction(&setmetatableFunc); scope->exportedTypeBindings[getmetatableFunc.name] = mkUnaryTypeFunction(&getmetatableFunc); } diff --git a/Analysis/src/Clone.cpp b/Analysis/src/Clone.cpp index d083c733..9c1403e3 100644 --- a/Analysis/src/Clone.cpp +++ b/Analysis/src/Clone.cpp @@ -13,7 +13,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(LuauSolverAgnosticClone) namespace Luau { @@ -203,37 +202,22 @@ public: private: Property shallowClone(const Property& p) { - if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone) - { - std::optional cloneReadTy; - if (auto ty = p.readTy) - cloneReadTy = shallowClone(*ty); + std::optional cloneReadTy; + if (auto ty = p.readTy) + cloneReadTy = shallowClone(*ty); - std::optional cloneWriteTy; - if (auto ty = p.writeTy) - cloneWriteTy = shallowClone(*ty); + std::optional cloneWriteTy; + if (auto ty = p.writeTy) + cloneWriteTy = shallowClone(*ty); - Property cloned = Property::create(cloneReadTy, cloneWriteTy); - cloned.deprecated = p.deprecated; - cloned.deprecatedSuggestion = p.deprecatedSuggestion; - cloned.location = p.location; - cloned.tags = p.tags; - cloned.documentationSymbol = p.documentationSymbol; - cloned.typeLocation = p.typeLocation; - return cloned; - } - else - { - return Property{ - shallowClone(p.type_DEPRECATED()), - p.deprecated, - p.deprecatedSuggestion, - p.location, - p.tags, - p.documentationSymbol, - p.typeLocation, - }; - } + Property cloned = Property::create(cloneReadTy, cloneWriteTy); + cloned.deprecated = p.deprecated; + cloned.deprecatedSuggestion = p.deprecatedSuggestion; + cloned.location = p.location; + cloned.tags = p.tags; + cloned.documentationSymbol = p.documentationSymbol; + cloned.typeLocation = p.typeLocation; + return cloned; } void cloneChildren(TypeId ty) diff --git a/Analysis/src/Constraint.cpp b/Analysis/src/Constraint.cpp index d75f5f76..877908d0 100644 --- a/Analysis/src/Constraint.cpp +++ b/Analysis/src/Constraint.cpp @@ -18,61 +18,6 @@ Constraint::Constraint(NotNull scope, const Location& location, Constrain { } -struct ReferenceCountInitializer_DEPRECATED : TypeOnceVisitor -{ - DenseHashSet* result; - bool traverseIntoTypeFunctions = true; - - explicit ReferenceCountInitializer_DEPRECATED(DenseHashSet* result) - : TypeOnceVisitor("ReferenceCountInitializer_DEPRECATED") - , result(result) - { - } - - bool visit(TypeId ty, const FreeType&) override - { - result->insert(ty); - return false; - } - - bool visit(TypeId ty, const BlockedType&) override - { - result->insert(ty); - return false; - } - - bool visit(TypeId ty, const PendingExpansionType&) override - { - result->insert(ty); - return false; - } - - bool visit(TypeId ty, const TableType& tt) override - { - if (FFlag::LuauEagerGeneralization4) - { - if (tt.state == TableState::Unsealed || tt.state == TableState::Free) - result->insert(ty); - } - - return true; - } - - bool visit(TypeId ty, const ExternType&) override - { - // ExternTypes never contain free types. - return false; - } - - bool visit(TypeId, const TypeFunctionInstanceType& tfit) override - { - if (FFlag::LuauForceSimplifyConstraint2) - return tfit.function->canReduceGenerics; - else - return FFlag::LuauEagerGeneralization4 && traverseIntoTypeFunctions; - } -}; - struct ReferenceCountInitializer : TypeOnceVisitor { NotNull result; @@ -140,110 +85,6 @@ bool isReferenceCountedType(const TypeId typ) return get(typ) || get(typ) || get(typ); } -DenseHashSet Constraint::getMaybeMutatedFreeTypes_DEPRECATED() const -{ - // For the purpose of this function and reference counting in general, we are only considering - // mutations that affect the _bounds_ of the free type, and not something that may bind the free - // type itself to a new type. As such, `ReduceConstraint` and `GeneralizationConstraint` have no - // contribution to the output set here. - - DenseHashSet types{{}}; - ReferenceCountInitializer_DEPRECATED rci{&types}; - - if (auto ec = get(*this)) - { - rci.traverse(ec->resultType); - // `EqualityConstraints` should not mutate `assignmentType`. - } - else if (auto sc = get(*this)) - { - rci.traverse(sc->subType); - rci.traverse(sc->superType); - } - else if (auto psc = get(*this)) - { - rci.traverse(psc->subPack); - rci.traverse(psc->superPack); - } - else if (auto itc = get(*this)) - { - for (TypeId ty : itc->variables) - rci.traverse(ty); - // `IterableConstraints` should not mutate `iterator`. - } - else if (auto nc = get(*this)) - { - rci.traverse(nc->namedType); - } - else if (auto taec = get(*this)) - { - rci.traverse(taec->target); - } - else if (auto fchc = get(*this)) - { - rci.traverse(fchc->argsPack); - } - else if (auto fcc = get(*this); fcc && FFlag::LuauEagerGeneralization4) - { - rci.traverseIntoTypeFunctions = false; - rci.traverse(fcc->fn); - rci.traverse(fcc->argsPack); - rci.traverseIntoTypeFunctions = true; - } - else if (auto ptc = get(*this)) - { - rci.traverse(ptc->freeType); - } - else if (auto hpc = get(*this)) - { - rci.traverse(hpc->resultType); - if (FFlag::LuauEagerGeneralization4) - rci.traverse(hpc->subjectType); - } - else if (auto hic = get(*this)) - { - if (FFlag::LuauEagerGeneralization4) - rci.traverse(hic->subjectType); - rci.traverse(hic->resultType); - // `HasIndexerConstraint` should not mutate `indexType`. - } - else if (auto apc = get(*this)) - { - rci.traverse(apc->lhsType); - rci.traverse(apc->rhsType); - } - else if (auto aic = get(*this)) - { - rci.traverse(aic->lhsType); - rci.traverse(aic->indexType); - rci.traverse(aic->rhsType); - } - else if (auto uc = get(*this)) - { - for (TypeId ty : uc->resultPack) - rci.traverse(ty); - // `UnpackConstraint` should not mutate `sourcePack`. - } - else if (auto rpc = get(*this)) - { - rci.traverse(rpc->tp); - } - else if (auto tcc = get(*this)) - { - rci.traverse(tcc->exprType); - } - - if (FFlag::LuauPushFunctionTypesInFunctionStatement) - { - if (auto pftc = get(*this)) - { - rci.traverse(pftc->functionType); - } - } - - return types; -} - TypeIds Constraint::getMaybeMutatedFreeTypes() const { // For the purpose of this function and reference counting in general, we are only considering diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 17c93999..cf369e18 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -36,21 +36,16 @@ LUAU_FASTINT(LuauCheckRecursionLimit) LUAU_FASTFLAG(DebugLuauLogSolverToJson) LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation) LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties) LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) -LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables) LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500) -LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauRefineTablesWithReadType) LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteTracksRValueRefinements) LUAU_FASTFLAGVARIABLE(LuauPushFunctionTypesInFunctionStatement) -LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType) +LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType2) LUAU_FASTFLAGVARIABLE(LuauDoNotPrototypeTableIndex) -LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving) +LUAU_FASTFLAGVARIABLE(LuauTrackFreeInteriorTypePacks) +LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3) namespace Luau { @@ -233,6 +228,11 @@ ConstraintGenerator::ConstraintGenerator( , logger(logger) { LUAU_ASSERT(module); + + if (FFlag::LuauEagerGeneralization4) + { + LUAU_ASSERT(FFlag::LuauTrackFreeInteriorTypePacks); + } } ConstraintSet ConstraintGenerator::run(AstStatBlock* block) @@ -261,7 +261,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) rootScope->location = block->location; module->astScopes[block] = NotNull{scope.get()}; - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.emplace_back(); @@ -297,7 +297,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) } ); - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) { scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -316,7 +316,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) } ); - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -354,13 +354,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::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.emplace_back(); visitBlockWithoutChildScope(resumeScope, block); // Post - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -390,9 +390,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity) { - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) { - auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity); + const TypeId ft = FFlag::LuauEagerGeneralization4 + ? Luau::freshType(arena, builtinTypes, scope.get(), polarity) + : Luau::freshType(arena, builtinTypes, scope.get()); + interiorFreeTypes.back().types.push_back(ft); if (FFlag::LuauEagerGeneralization4) @@ -412,7 +415,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po { FreeTypePack f{scope.get(), polarity}; TypePackId result = arena->addTypePack(TypePackVar{std::move(f)}); - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.back().typePacks.push_back(result); return result; } @@ -1036,6 +1039,11 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStat* stat) { RecursionLimiter limiter{"ConstraintGenerator", &recursionCount, FInt::LuauCheckRecursionLimit}; + if (FFlag::LuauTrackFreeInteriorTypePacks) + LUAU_ASSERT(DEPRECATED_interiorTypes.empty()); + else + LUAU_ASSERT(interiorFreeTypes.empty()); + if (auto s = stat->as()) return visit(scope, s); else if (auto i = stat->as()) @@ -1741,16 +1749,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatCompoundAss { TypeId resultTy = checkAstExprBinary(scope, assign->location, assign->op, assign->var, assign->value, std::nullopt).ty; module->astCompoundAssignResultTypes[assign] = resultTy; - - if (!FFlag::LuauSkipLvalueForCompoundAssignment) - { - TypeId lhsType = check(scope, assign->var).ty; - visitLValue(scope, assign->var, lhsType); - - follow(lhsType); - follow(resultTy); - } - + // NOTE: We do not update lvalues for compound assignments. This is + // intentional. return ControlFlow::None; } @@ -1870,7 +1870,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::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.push_back(std::vector{}); @@ -1888,7 +1888,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio } ); - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) { sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -1897,7 +1897,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back()); getMutable(generalizedTy)->setOwner(gc); - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -1993,7 +1993,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte } ); - if (FFlag::LuauLimitDynamicConstraintSolving) + if (FFlag::LuauLimitDynamicConstraintSolving3) { // If we don't emplace an error type here, then later we'll be // exposing a blocked type in this file's type interface. This @@ -2074,88 +2074,56 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte { Luau::Property& prop = props[propName]; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (auto readTy = prop.readTy) { - if (auto readTy = prop.readTy) - { - // We special-case this logic to keep the intersection flat; otherwise we - // would create a ton of nested intersection types. - if (const IntersectionType* itv = get(*readTy)) - { - std::vector options = itv->parts; - options.push_back(propTy); - TypeId newItv = arena->addType(IntersectionType{std::move(options)}); - - prop.readTy = newItv; - } - else if (get(*readTy)) - { - TypeId intersection = arena->addType(IntersectionType{{*readTy, propTy}}); - - prop.readTy = intersection; - } - else - { - reportError( - declaredExternType->location, - GenericError{format("Cannot overload read type of non-function class member '%s'", propName.c_str())} - ); - } - } - - if (auto writeTy = prop.writeTy) - { - // We special-case this logic to keep the intersection flat; otherwise we - // would create a ton of nested intersection types. - if (const IntersectionType* itv = get(*writeTy)) - { - std::vector options = itv->parts; - options.push_back(propTy); - TypeId newItv = arena->addType(IntersectionType{std::move(options)}); - - prop.writeTy = newItv; - } - else if (get(*writeTy)) - { - TypeId intersection = arena->addType(IntersectionType{{*writeTy, propTy}}); - - prop.writeTy = intersection; - } - else - { - reportError( - declaredExternType->location, - GenericError{format("Cannot overload write type of non-function class member '%s'", propName.c_str())} - ); - } - } - } - else - { - TypeId currentTy = prop.type_DEPRECATED(); - // We special-case this logic to keep the intersection flat; otherwise we // would create a ton of nested intersection types. - if (const IntersectionType* itv = get(currentTy)) + if (const IntersectionType* itv = get(*readTy)) { std::vector options = itv->parts; options.push_back(propTy); TypeId newItv = arena->addType(IntersectionType{std::move(options)}); prop.readTy = newItv; - prop.writeTy = newItv; } - else if (get(currentTy)) + else if (get(*readTy)) { - TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}}); + TypeId intersection = arena->addType(IntersectionType{{*readTy, propTy}}); prop.readTy = intersection; + } + else + { + reportError( + declaredExternType->location, + GenericError{format("Cannot overload read type of non-function class member '%s'", propName.c_str())} + ); + } + } + + if (auto writeTy = prop.writeTy) + { + // We special-case this logic to keep the intersection flat; otherwise we + // would create a ton of nested intersection types. + if (const IntersectionType* itv = get(*writeTy)) + { + std::vector options = itv->parts; + options.push_back(propTy); + TypeId newItv = arena->addType(IntersectionType{std::move(options)}); + + prop.writeTy = newItv; + } + else if (get(*writeTy)) + { + TypeId intersection = arena->addType(IntersectionType{{*writeTy, propTy}}); + prop.writeTy = intersection; } else { reportError( - declaredExternType->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())} + declaredExternType->location, + GenericError{format("Cannot overload write type of non-function class member '%s'", propName.c_str())} ); } } @@ -2653,7 +2621,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin // // 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) + if (largeTableDepth > 0) return Inference{builtinTypes->stringType}; TypeId freeTy = nullptr; @@ -2694,7 +2662,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool* // // 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) + if (largeTableDepth > 0) return Inference{builtinTypes->booleanType}; TypeId freeTy = nullptr; @@ -2866,7 +2834,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun Checkpoint startCheckpoint = checkpoint(this); FunctionSignature sig = checkFunctionSignature(scope, func, expectedType); - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.push_back(std::vector{}); @@ -2884,7 +2852,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun } ); - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) { sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -3114,7 +3082,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIfElse* ifEls applyRefinements(elseScope, ifElse->falseExpr->location, refinementArena.negation(refinement)); TypeId elseType = check(elseScope, ifElse->falseExpr, expectedType).ty; - if (FFlag::LuauInferActualIfElseExprType) + if (FFlag::LuauInferActualIfElseExprType2) return Inference{makeUnion(scope, ifElse->location, thenType, elseType)}; else return Inference{expectedType ? *expectedType : makeUnion(scope, ifElse->location, thenType, elseType)}; @@ -3406,11 +3374,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, ttv->definitionLocation = expr->location; ttv->scope = scope.get(); - if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && - expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit)) + if (FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit)) largeTableDepth++; - if (FFlag::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) interiorFreeTypes.back().types.push_back(ty); else DEPRECATED_interiorTypes.back().push_back(ty); @@ -3503,23 +3470,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, } } - if (expectedType && !FFlag::LuauTableLiteralSubtypeSpecificCheck2) - { - addConstraint( - scope, - expr->location, - TableCheckConstraint{ - *expectedType, - ty, - expr, - NotNull{&module->astTypes}, - NotNull{&module->astExpectedTypes}, - } - ); - } - - if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && - expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit)) + if (FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit)) largeTableDepth--; return Inference{ty}; @@ -3775,7 +3726,7 @@ TypeId ConstraintGenerator::resolveReferenceType( else return resolveType_(scope, ref->parameters.data[0].type, inTypeArguments); } - else if (FFlag::LuauLimitDynamicConstraintSolving && ref->name == "_luau_blocked_type") + else if (FFlag::LuauLimitDynamicConstraintSolving3 && ref->name == "_luau_blocked_type") { return arena->addType(BlockedType{}); } @@ -3871,11 +3822,6 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty, p.readTy = propTy; break; case AstTableAccess::Write: - if (!FFlag::LuauEnableWriteOnlyProperties) - { - reportError(*prop.accessLocation, GenericError{"write keyword is illegal here"}); - p.readTy = propTy; - } p.writeTy = propTy; break; default: @@ -4401,17 +4347,6 @@ struct GlobalPrepopulator : AstVisitor void ConstraintGenerator::prepopulateGlobalScopeForFragmentTypecheck(const ScopePtr& globalScope, const ScopePtr& resumeScope, AstStatBlock* program) { - if (!FFlag::LuauGlobalVariableModuleIsolation) - { - FragmentTypeCheckGlobalPrepopulator_DEPRECATED gp{NotNull{globalScope.get()}, NotNull{resumeScope.get()}, dfg, arena}; - - if (prepareModuleScope) - prepareModuleScope(module->name, resumeScope); - - program->visit(&gp); - } - - // Handle type function globals as well, without preparing a module scope since they have a separate environment GlobalPrepopulator tfgp{NotNull{typeFunctionRuntime->rootScope.get()}, arena, dfg}; program->visit(&tfgp); diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index fd589bc6..3c032a37 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -35,22 +35,40 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies) LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) -LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck) -LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint) -LUAU_FASTFLAGVARIABLE(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeCheckFunctionCalls) -LUAU_FASTFLAGVARIABLE(LuauUseOrderedTypeSetsInConstraints) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying) LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint2) LUAU_FASTFLAGVARIABLE(LuauCollapseShouldNotCrash) LUAU_FASTFLAGVARIABLE(LuauContainsAnyGenericFollowBeforeChecking) -LUAU_FASTFLAGVARIABLE(LuauLimitDynamicConstraintSolving) +LUAU_FASTFLAGVARIABLE(LuauLimitDynamicConstraintSolving3) +LUAU_FASTFLAGVARIABLE(LuauDontDynamicallyCreateRedundantSubtypeConstraints) namespace Luau { +static void hashCombine(size_t& seed, size_t hash) +{ + // Golden Ratio constant used for better hash scattering + // See https://softwareengineering.stackexchange.com/a/402543 + seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +bool SubtypeConstraintRecord::operator==(const SubtypeConstraintRecord& other) const +{ + return (subTy == other.subTy) && (superTy == other.superTy) && (variance == other.variance); +} + +size_t HashSubtypeConstraintRecord::operator()(const SubtypeConstraintRecord& c) const +{ + size_t result = 0; + hashCombine(result, intptr_t(c.subTy)); + hashCombine(result, intptr_t(c.superTy)); + hashCombine(result, intptr_t(c.variance)); + return result; +} + + static void dump(ConstraintSolver* cs, ToStringOptions& opts); size_t HashBlockedConstraintId::operator()(const BlockedConstraintId& bci) const @@ -427,22 +445,10 @@ void ConstraintSolver::run() // Free types that have no constraints at all can be generalized right away. if (FFlag::LuauEagerGeneralization4) { - if (FFlag::LuauUseOrderedTypeSetsInConstraints) + for (TypeId ty : constraintSet.freeTypes) { - for (TypeId ty : constraintSet.freeTypes) - { - if (auto it = mutatedFreeTypeToConstraint.find(ty); it == mutatedFreeTypeToConstraint.end() || it->second.empty()) - generalizeOneType(ty); - } - } - else - { - for (TypeId ty : constraintSet.freeTypes) - { - if (auto it = mutatedFreeTypeToConstraint_DEPRECATED.find(ty); - it == mutatedFreeTypeToConstraint_DEPRECATED.end() || it->second.empty()) - generalizeOneType(ty); - } + if (auto it = mutatedFreeTypeToConstraint.find(ty); it == mutatedFreeTypeToConstraint.end() || it->second.empty()) + generalizeOneType(ty); } } @@ -469,7 +475,7 @@ void ConstraintSolver::run() // If we were _given_ a limit, and the current limit has hit zero, ] // then early exit from constraint solving. - if (FFlag::LuauLimitDynamicConstraintSolving && FInt::LuauSolverConstraintLimit > 0 && solverConstraintLimit == 0) + if (FFlag::LuauLimitDynamicConstraintSolving3 && FInt::LuauSolverConstraintLimit > 0 && solverConstraintLimit == 0) break; std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{}; @@ -492,77 +498,37 @@ void ConstraintSolver::run() unblock(c); unsolvedConstraints.erase(unsolvedConstraints.begin() + ptrdiff_t(i)); - if (FFlag::LuauUseOrderedTypeSetsInConstraints) + if (const auto maybeMutated = maybeMutatedFreeTypes.find(c); maybeMutated != maybeMutatedFreeTypes.end()) { - if (const auto maybeMutated = maybeMutatedFreeTypes.find(c); maybeMutated != maybeMutatedFreeTypes.end()) + DenseHashSet seen{nullptr}; + for (auto ty : maybeMutated->second) { - DenseHashSet seen{nullptr}; - for (auto ty : maybeMutated->second) + // There is a high chance that this type has been rebound + // across blocked types, rebound free types, pending + // expansion types, etc, so we need to follow it. + ty = follow(ty); + + if (FFlag::LuauEagerGeneralization4) { - // There is a high chance that this type has been rebound - // across blocked types, rebound free types, pending - // expansion types, etc, so we need to follow it. - ty = follow(ty); - - if (FFlag::LuauEagerGeneralization4) - { - if (seen.contains(ty)) - continue; - seen.insert(ty); - } - - size_t& refCount = unresolvedConstraints[ty]; - if (refCount > 0) - refCount -= 1; - - // We have two constraints that are designed to wait for the - // refCount on a free type to be equal to 1: the - // PrimitiveTypeConstraint and ReduceConstraint. We - // therefore wake any constraint waiting for a free type's - // refcount to be 1 or 0. - if (refCount <= 1) - unblock(ty, Location{}); - - if (FFlag::LuauEagerGeneralization4 && refCount == 0) - generalizeOneType(ty); + if (seen.contains(ty)) + continue; + seen.insert(ty); } - } - } - else - { - const auto maybeMutated = maybeMutatedFreeTypes_DEPRECATED.find(c); - if (maybeMutated != maybeMutatedFreeTypes_DEPRECATED.end()) - { - DenseHashSet seen{nullptr}; - for (auto ty : maybeMutated->second) - { - // There is a high chance that this type has been rebound - // across blocked types, rebound free types, pending - // expansion types, etc, so we need to follow it. - ty = follow(ty); - if (FFlag::LuauEagerGeneralization4) - { - if (seen.contains(ty)) - continue; - seen.insert(ty); - } + size_t& refCount = unresolvedConstraints[ty]; + if (refCount > 0) + refCount -= 1; - size_t& refCount = unresolvedConstraints[ty]; - if (refCount > 0) - refCount -= 1; + // We have two constraints that are designed to wait for the + // refCount on a free type to be equal to 1: the + // PrimitiveTypeConstraint and ReduceConstraint. We + // therefore wake any constraint waiting for a free type's + // refcount to be 1 or 0. + if (refCount <= 1) + unblock(ty, Location{}); - // We have two constraints that are designed to wait for the - // refCount on a free type to be equal to 1: the - // PrimitiveTypeConstraint and ReduceConstraint. We - // therefore wake any constraint waiting for a free type's - // refcount to be 1 or 0. - if (refCount <= 1) - unblock(ty, Location{}); - - if (FFlag::LuauEagerGeneralization4 && refCount == 0) - generalizeOneType(ty); - } + if (FFlag::LuauEagerGeneralization4 && refCount == 0) + generalizeOneType(ty); } } @@ -731,57 +697,27 @@ struct TypeSearcher : TypeVisitor void ConstraintSolver::initFreeTypeTracking() { - if (FFlag::LuauUseOrderedTypeSetsInConstraints) + for (auto c : this->constraints) { - for (auto c : this->constraints) + unsolvedConstraints.emplace_back(c); + + auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes(); + for (auto ty : maybeMutatedTypesPerConstraint) { - unsolvedConstraints.emplace_back(c); + auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0); + refCount += 1; - auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes(); - for (auto ty : maybeMutatedTypesPerConstraint) + if (FFlag::LuauEagerGeneralization4) { - auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0); - refCount += 1; - - if (FFlag::LuauEagerGeneralization4) - { - auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty); - it->second.insert(c.get()); - } - } - maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint); - - for (NotNull dep : c->dependencies) - { - block(dep, c); + auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty); + it->second.insert(c.get()); } } - } - else - { + maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint); - for (NotNull c : this->constraints) + for (NotNull dep : c->dependencies) { - unsolvedConstraints.emplace_back(c); - - auto maybeMutatedTypesPerConstraint = c->getMaybeMutatedFreeTypes_DEPRECATED(); - for (auto ty : maybeMutatedTypesPerConstraint) - { - auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0); - refCount += 1; - - if (FFlag::LuauEagerGeneralization4) - { - auto [it, fresh] = mutatedFreeTypeToConstraint_DEPRECATED.try_emplace(ty, nullptr); - it->second.insert(c.get()); - } - } - maybeMutatedFreeTypes_DEPRECATED.emplace(c, maybeMutatedTypesPerConstraint); - - for (NotNull dep : c->dependencies) - { - block(dep, c); - } + block(dep, c); } } } @@ -1838,225 +1774,130 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull replacements{nullptr}; DenseHashMap replacementPacks{nullptr}; - if (FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck) + DenseHashSet genericTypesAndPacks{nullptr}; + + Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}}; + + for (auto generic : ftv->generics) { - - DenseHashSet genericTypesAndPacks{nullptr}; - - Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}}; - - for (auto generic : ftv->generics) + // We may see non-generic types here, for example when evaluating a + // recursive function call. + if (auto gty = get(follow(generic))) { - // We may see non-generic types here, for example when evaluating a - // recursive function call. - if (auto gty = get(follow(generic))) + replacements[generic] = gty->polarity == Polarity::Negative ? builtinTypes->neverType : builtinTypes->unknownType; + genericTypesAndPacks.insert(generic); + } + } + + for (auto genericPack : ftv->genericPacks) + { + replacementPacks[genericPack] = builtinTypes->unknownTypePack; + genericTypesAndPacks.insert(genericPack); + } + + const std::vector expectedArgs = flatten(ftv->argTypes).first; + const std::vector argPackHead = flatten(argsPack).first; + + // If this is a self call, the types will have more elements than the AST call. + // We don't attempt to perform bidirectional inference on the self type. + const size_t typeOffset = c.callSite->self ? 1 : 0; + + for (size_t i = 0; i < c.callSite->args.size && i + typeOffset < expectedArgs.size() && i + typeOffset < argPackHead.size(); ++i) + { + TypeId expectedArgTy = follow(expectedArgs[i + typeOffset]); + const TypeId actualArgTy = follow(argPackHead[i + typeOffset]); + AstExpr* expr = unwrapGroup(c.callSite->args.data[i]); + + (*c.astExpectedTypes)[expr] = expectedArgTy; + + const auto lambdaTy = get(actualArgTy); + const auto expectedLambdaTy = get(expectedArgTy); + const auto lambdaExpr = expr->as(); + + if (expectedLambdaTy && lambdaTy && lambdaExpr) + { + if (ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks})) + continue; + + const std::vector expectedLambdaArgTys = flatten(expectedLambdaTy->argTypes).first; + const std::vector lambdaArgTys = flatten(lambdaTy->argTypes).first; + + for (size_t j = 0; j < expectedLambdaArgTys.size() && j < lambdaArgTys.size() && j < lambdaExpr->args.size; ++j) { - replacements[generic] = gty->polarity == Polarity::Negative ? builtinTypes->neverType : builtinTypes->unknownType; - genericTypesAndPacks.insert(generic); + if (!lambdaExpr->args.data[j]->annotation && get(follow(lambdaArgTys[j]))) + { + shiftReferences(lambdaArgTys[j], expectedLambdaArgTys[j]); + bind(constraint, lambdaArgTys[j], expectedLambdaArgTys[j]); + } } } - - for (auto genericPack : ftv->genericPacks) + else if (expr->is() || expr->is() || expr->is() || + expr->is() || (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is())) { - replacementPacks[genericPack] = builtinTypes->unknownTypePack; - genericTypesAndPacks.insert(genericPack); - } - - const std::vector expectedArgs = flatten(ftv->argTypes).first; - const std::vector argPackHead = flatten(argsPack).first; - - // If this is a self call, the types will have more elements than the AST call. - // We don't attempt to perform bidirectional inference on the self type. - const size_t typeOffset = c.callSite->self ? 1 : 0; - - for (size_t i = 0; i < c.callSite->args.size && i + typeOffset < expectedArgs.size() && i + typeOffset < argPackHead.size(); ++i) - { - TypeId expectedArgTy = follow(expectedArgs[i + typeOffset]); - const TypeId actualArgTy = follow(argPackHead[i + typeOffset]); - AstExpr* expr = unwrapGroup(c.callSite->args.data[i]); - - (*c.astExpectedTypes)[expr] = expectedArgTy; - - const auto lambdaTy = get(actualArgTy); - const auto expectedLambdaTy = get(expectedArgTy); - const auto lambdaExpr = expr->as(); - - if (expectedLambdaTy && lambdaTy && lambdaExpr) + if (FFlag::LuauAvoidExcessiveTypeCopying) { if (ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks})) - continue; - - const std::vector expectedLambdaArgTys = flatten(expectedLambdaTy->argTypes).first; - const std::vector lambdaArgTys = flatten(lambdaTy->argTypes).first; - - for (size_t j = 0; j < expectedLambdaArgTys.size() && j < lambdaArgTys.size() && j < lambdaExpr->args.size; ++j) - { - if (!lambdaExpr->args.data[j]->annotation && get(follow(lambdaArgTys[j]))) - { - shiftReferences(lambdaArgTys[j], expectedLambdaArgTys[j]); - bind(constraint, lambdaArgTys[j], expectedLambdaArgTys[j]); - } - } - } - else if (expr->is() || expr->is() || expr->is() || - expr->is() || (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is())) - { - if (FFlag::LuauAvoidExcessiveTypeCopying) - { - if (ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks})) - { - ReferentialReplacer replacer{arena, NotNull{&replacements}, NotNull{&replacementPacks}}; - if (auto res = replacer.substitute(expectedArgTy)) - { - InstantiationQueuer queuer{constraint->scope, constraint->location, this}; - queuer.traverse(*res); - expectedArgTy = *res; - } - } - u2.unify(actualArgTy, expectedArgTy); - } - else { ReferentialReplacer replacer{arena, NotNull{&replacements}, NotNull{&replacementPacks}}; if (auto res = replacer.substitute(expectedArgTy)) { - if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls) - { - // If we do this replacement and there are type - // functions in the final type, then we need to - // ensure those get reduced. - InstantiationQueuer queuer{constraint->scope, constraint->location, this}; - queuer.traverse(*res); - } - u2.unify(actualArgTy, *res); - } - else - u2.unify(actualArgTy, expectedArgTy); - } - } - else if (!FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is() && - !ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks})) - { - Subtyping sp{builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, NotNull{&iceReporter}}; - std::vector toBlock; - (void)matchLiteralType( - c.astTypes, c.astExpectedTypes, builtinTypes, arena, NotNull{&u2}, NotNull{&sp}, expectedArgTy, actualArgTy, expr, toBlock - ); - LUAU_ASSERT(toBlock.empty()); - } - } - - if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls) - { - // Consider: - // - // local Direction = { Left = 1, Right = 2 } - // type Direction = keyof - // - // local function move(dirs: { Direction }) --[[...]] end - // - // move({ "Left", "Right", "Left", "Right" }) - // - // We need `keyof` to reduce prior to inferring that the - // arguments to `move` must generalize to their lower bounds. This - // is how we ensure that ordering. - for (auto& c : u2.incompleteSubtypes) - { - NotNull addition = pushConstraint(constraint->scope, constraint->location, std::move(c)); - inheritBlocks(constraint, addition); - } - } - } - else - { - ContainsGenerics_DEPRECATED containsGenerics; - - for (auto generic : ftv->generics) - { - replacements[generic] = builtinTypes->unknownType; - containsGenerics.generics.insert(generic); - } - - for (auto genericPack : ftv->genericPacks) - { - replacementPacks[genericPack] = builtinTypes->unknownTypePack; - containsGenerics.generics.insert(genericPack); - } - - const std::vector expectedArgs = flatten(ftv->argTypes).first; - const std::vector argPackHead = flatten(argsPack).first; - - // If this is a self call, the types will have more elements than the AST call. - // We don't attempt to perform bidirectional inference on the self type. - const size_t typeOffset = c.callSite->self ? 1 : 0; - - for (size_t i = 0; i < c.callSite->args.size && i + typeOffset < expectedArgs.size() && i + typeOffset < argPackHead.size(); ++i) - { - const TypeId expectedArgTy = follow(expectedArgs[i + typeOffset]); - const TypeId actualArgTy = follow(argPackHead[i + typeOffset]); - AstExpr* expr = unwrapGroup(c.callSite->args.data[i]); - - (*c.astExpectedTypes)[expr] = expectedArgTy; - - const FunctionType* lambdaTy = get(actualArgTy); - // Generic types are skipped over entirely, for now. - if (containsGenerics.hasGeneric(expectedArgTy)) - { - if (!lambdaTy || !lambdaTy->argTypes) - continue; - - const TypePack* argTp = get(follow(lambdaTy->argTypes)); - if (!argTp || !argTp->tail) - continue; - - if (const VariadicTypePack* argTpTail = get(follow(argTp->tail)); - argTpTail && argTpTail->hidden && argTpTail->ty == builtinTypes->anyType) - { - // Strip variadic any - const TypePackId anyLessArgTp = arena->addTypePack(TypePack{argTp->head}); - const TypeId newFuncTypeId = arena->addType(FunctionType{anyLessArgTp, lambdaTy->retTypes}); - FunctionType* newFunc = getMutable(newFuncTypeId); - newFunc->argNames = lambdaTy->argNames; - (*c.astTypes)[expr] = newFuncTypeId; - } - - continue; - } - - const FunctionType* expectedLambdaTy = get(expectedArgTy); - const AstExprFunction* lambdaExpr = expr->as(); - - if (expectedLambdaTy && lambdaTy && lambdaExpr) - { - const std::vector expectedLambdaArgTys = flatten(expectedLambdaTy->argTypes).first; - const std::vector lambdaArgTys = flatten(lambdaTy->argTypes).first; - - for (size_t j = 0; j < expectedLambdaArgTys.size() && j < lambdaArgTys.size() && j < lambdaExpr->args.size; ++j) - { - if (!lambdaExpr->args.data[j]->annotation && get(follow(lambdaArgTys[j]))) - { - shiftReferences(lambdaArgTys[j], expectedLambdaArgTys[j]); - bind(constraint, lambdaArgTys[j], expectedLambdaArgTys[j]); + InstantiationQueuer queuer{constraint->scope, constraint->location, this}; + queuer.traverse(*res); + expectedArgTy = *res; } } - } - else if (expr->is() || expr->is() || expr->is() || - expr->is()) - { - Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}}; u2.unify(actualArgTy, expectedArgTy); } - else if (expr->is()) + else { - Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}}; - Subtyping sp{builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, NotNull{&iceReporter}}; - std::vector toBlock; - (void)matchLiteralType( - c.astTypes, c.astExpectedTypes, builtinTypes, arena, NotNull{&u2}, NotNull{&sp}, expectedArgTy, actualArgTy, expr, toBlock - ); - LUAU_ASSERT(toBlock.empty()); + ReferentialReplacer replacer{arena, NotNull{&replacements}, NotNull{&replacementPacks}}; + if (auto res = replacer.substitute(expectedArgTy)) + { + if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls) + { + // If we do this replacement and there are type + // functions in the final type, then we need to + // ensure those get reduced. + InstantiationQueuer queuer{constraint->scope, constraint->location, this}; + queuer.traverse(*res); + } + u2.unify(actualArgTy, *res); + } + else + u2.unify(actualArgTy, expectedArgTy); } } + else if (!FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is() && + !ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks})) + { + Subtyping sp{builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, NotNull{&iceReporter}}; + std::vector toBlock; + (void)matchLiteralType( + c.astTypes, c.astExpectedTypes, builtinTypes, arena, NotNull{&u2}, NotNull{&sp}, expectedArgTy, actualArgTy, expr, toBlock + ); + LUAU_ASSERT(toBlock.empty()); + } + } + + if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls) + { + // Consider: + // + // local Direction = { Left = 1, Right = 2 } + // type Direction = keyof + // + // local function move(dirs: { Direction }) --[[...]] end + // + // move({ "Left", "Right", "Left", "Right" }) + // + // We need `keyof` to reduce prior to inferring that the + // arguments to `move` must generalize to their lower bounds. This + // is how we ensure that ordering. + for (auto& c : u2.incompleteSubtypes) + { + NotNull addition = pushConstraint(constraint->scope, constraint->location, std::move(c)); + inheritBlocks(constraint, addition); + } } return true; @@ -2621,21 +2462,10 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull(lhsType)) { - if (FFlag::LuauMissingFollowInAssignIndexConstraint) + if (auto lhsTable = getMutable(follow(lhsFree->upperBound))) { - if (auto lhsTable = getMutable(follow(lhsFree->upperBound))) - { - if (auto res = tableStuff(lhsTable)) - return *res; - } - } - else - { - if (auto lhsTable = getMutable(lhsFree->upperBound)) - { - if (auto res = tableStuff(lhsTable)) - return *res; - } + if (auto res = tableStuff(lhsTable)) + return *res; } TypeId newUpperBound = @@ -3400,17 +3230,12 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( return {{}, result.propType}; // TODO: __index can be an overloaded function. - // - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - // if the property is write-only, then surely we cannot read from it. - if (indexProp->second.isWriteOnly()) - return {{}, builtinTypes->errorType}; - } + // if the property is write-only, then surely we cannot read from it. + if (indexProp->second.isWriteOnly()) + return {{}, builtinTypes->errorType}; - TypeId indexType = - FFlag::LuauRemoveTypeCallsForReadWriteProps ? follow(*indexProp->second.readTy) : follow(indexProp->second.type_DEPRECATED()); + TypeId indexType = follow(*indexProp->second.readTy); if (auto ft = get(indexType)) { @@ -3448,16 +3273,11 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( if (indexProp == metatable->props.end()) return {{}, std::nullopt}; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - // if the property is write-only, then surely we cannot read from it. - if (indexProp->second.isWriteOnly()) - return {{}, builtinTypes->errorType}; + // if the property is write-only, then surely we cannot read from it. + if (indexProp->second.isWriteOnly()) + return {{}, builtinTypes->errorType}; - return lookupTableProp(constraint, *indexProp->second.readTy, propName, context, inConditional, suppressSimplification, seen); - } - else - return lookupTableProp(constraint, indexProp->second.type_DEPRECATED(), propName, context, inConditional, suppressSimplification, seen); + return lookupTableProp(constraint, *indexProp->second.readTy, propName, context, inConditional, suppressSimplification, seen); } else if (auto ft = get(subjectType)) { @@ -3826,7 +3646,7 @@ bool ConstraintSolver::isBlocked(TypeId ty) const if (auto tfit = get(ty)) { - if (FFlag::LuauStuckTypeFunctionsStillDispatch && tfit->state != TypeFunctionInstanceState::Unsolved) + if (FFlag::LuauEagerGeneralization4 && tfit->state != TypeFunctionInstanceState::Unsolved) return false; return uninhabitedTypeFunctions.contains(ty) == false; } @@ -3852,12 +3672,31 @@ bool ConstraintSolver::isBlocked(NotNull constraint) const NotNull ConstraintSolver::pushConstraint(NotNull scope, const Location& location, ConstraintV cv) { + std::optional scr; + if (FFlag::LuauDontDynamicallyCreateRedundantSubtypeConstraints) + { + if (auto sc = cv.get_if()) + scr.emplace(SubtypeConstraintRecord{sc->subType, sc->superType, SubtypingVariance::Covariant}); + else if (auto ec = cv.get_if()) + scr.emplace(SubtypeConstraintRecord{ec->assignmentType, ec->resultType, SubtypingVariance::Invariant}); + } + + if (scr) + { + if (auto f = seenConstraints.find(*scr)) + return NotNull{*f}; + } + std::unique_ptr c = std::make_unique(scope, location, std::move(cv)); NotNull borrow = NotNull(c.get()); + + if (scr) + seenConstraints[*scr] = borrow; + solverConstraints.push_back(std::move(c)); unsolvedConstraints.emplace_back(borrow); - if (FFlag::LuauLimitDynamicConstraintSolving) + if (FFlag::LuauLimitDynamicConstraintSolving3) { if (solverConstraintLimit > 0) { @@ -3962,42 +3801,18 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target) if (FFlag::LuauEagerGeneralization4) { - if (FFlag::LuauUseOrderedTypeSetsInConstraints) + if (auto it = mutatedFreeTypeToConstraint.find(source); it != mutatedFreeTypeToConstraint.end()) { + const OrderedSet& constraintsAffectedBySource = it->second; + auto [it2, fresh2] = mutatedFreeTypeToConstraint.try_emplace(target); - if (auto it = mutatedFreeTypeToConstraint.find(source); it != mutatedFreeTypeToConstraint.end()) + OrderedSet& constraintsAffectedByTarget = it2->second; + + for (const Constraint* constraint : constraintsAffectedBySource) { - const OrderedSet& constraintsAffectedBySource = it->second; - auto [it2, fresh2] = mutatedFreeTypeToConstraint.try_emplace(target); - - OrderedSet& constraintsAffectedByTarget = it2->second; - - for (const Constraint* constraint : constraintsAffectedBySource) - { - constraintsAffectedByTarget.insert(constraint); - auto [it3, fresh3] = maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, TypeIds{}); - it3->second.insert(target); - } - } - } - else - { - auto it = mutatedFreeTypeToConstraint_DEPRECATED.find(source); - if (it != mutatedFreeTypeToConstraint_DEPRECATED.end()) - { - const DenseHashSet& constraintsAffectedBySource = it->second; - - auto [it2, fresh2] = mutatedFreeTypeToConstraint_DEPRECATED.try_emplace(target, DenseHashSet{nullptr}); - DenseHashSet& constraintsAffectedByTarget = it2->second; - - // auto [it2, fresh] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet{nullptr}); - for (const Constraint* constraint : constraintsAffectedBySource) - { - constraintsAffectedByTarget.insert(constraint); - - auto [it3, fresh3] = maybeMutatedFreeTypes_DEPRECATED.try_emplace(NotNull{constraint}, DenseHashSet{nullptr}); - it3->second.insert(target); - } + constraintsAffectedByTarget.insert(constraint); + auto [it3, fresh3] = maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, TypeIds{}); + it3->second.insert(target); } } } diff --git a/Analysis/src/DataFlowGraph.cpp b/Analysis/src/DataFlowGraph.cpp index 5d821ef9..1455f033 100644 --- a/Analysis/src/DataFlowGraph.cpp +++ b/Analysis/src/DataFlowGraph.cpp @@ -13,7 +13,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull) LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements) LUAU_FASTFLAGVARIABLE(LuauDfgForwardNilFromAndOr) @@ -154,18 +153,6 @@ void DfgScope::inherit(const DfgScope* childScope) } } -bool DfgScope::canUpdateDefinition(Symbol symbol) const -{ - // NOTE: Vestigial as of clipping LuauDfgAllowUpdatesInLoops - return true; -} - -bool DfgScope::canUpdateDefinition(DefId def, const std::string& key) const -{ - // NOTE: Vestigial as of clipping LuauDfgAllowUpdatesInLoops - return true; -} - DataFlowGraphBuilder::DataFlowGraphBuilder(NotNull defArena, NotNull keyArena) : graph{defArena, keyArena} , defArena{defArena} @@ -185,14 +172,7 @@ DataFlowGraph DataFlowGraphBuilder::build( DataFlowGraphBuilder builder(defArena, keyArena); builder.handle = handle; - DfgScope* moduleScope; - // We're not explicitly calling makeChildScope here because that function relies on currentScope - // which guarantees that the scope being returned is NotNull - // This means that while the scope stack is empty, we'll have to manually initialize the global scope - if (FFlag::LuauDfgScopeStackNotNull) - moduleScope = builder.scopes.emplace_back(new DfgScope{nullptr, DfgScope::ScopeType::Linear}).get(); - else - moduleScope = builder.makeChildScope(); + DfgScope* moduleScope = builder.scopes.emplace_back(new DfgScope{nullptr, DfgScope::ScopeType::Linear}).get(); PushScope ps{builder.scopeStack, moduleScope}; builder.visitBlockWithoutChildScope(block); builder.resolveCaptures(); @@ -230,19 +210,9 @@ NotNull DataFlowGraphBuilder::currentScope() return NotNull{scopeStack.back()}; } -DfgScope* DataFlowGraphBuilder::currentScope_DEPRECATED() -{ - if (scopeStack.empty()) - return nullptr; // nullptr is the root DFG scope. - return scopeStack.back(); -} - DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType) { - if (FFlag::LuauDfgScopeStackNotNull) - return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get(); - else - return scopes.emplace_back(new DfgScope{currentScope_DEPRECATED(), scopeType}).get(); + return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get(); } void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b) @@ -319,7 +289,7 @@ void DataFlowGraphBuilder::joinProps(DfgScope* result, const DfgScope& a, const DefId DataFlowGraphBuilder::lookup(Symbol symbol, Location location) { - DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); + DfgScope* scope = currentScope(); // true if any of the considered scopes are a loop. bool outsideLoopScope = false; @@ -352,7 +322,7 @@ DefId DataFlowGraphBuilder::lookup(Symbol symbol, Location location) DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key, Location location) { - DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); + DfgScope* scope = currentScope(); for (DfgScope* current = scope; current; current = current->parent) { if (auto props = current->props.find(def)) @@ -398,10 +368,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatBlock* b) cf = visitBlockWithoutChildScope(b); } - if (FFlag::LuauDfgScopeStackNotNull) - currentScope()->inherit(child); - else - currentScope_DEPRECATED()->inherit(child); + currentScope()->inherit(child); return cf; } @@ -486,7 +453,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i) elsecf = visit(i->elsebody); } - DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); + DfgScope* scope = currentScope(); // If the control flow from the `if` or `else` block is non-linear, // then we should assume that the _other_ branch is the one taken. if (thencf != ControlFlow::None && elsecf == ControlFlow::None) @@ -615,10 +582,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l) } } graph.localDefs[local] = def; - if (FFlag::LuauDfgScopeStackNotNull) - currentScope()->bindings[local] = def; - else - currentScope_DEPRECATED()->bindings[local] = def; + currentScope()->bindings[local] = def; captures[local].allVersions.push_back(def); } @@ -675,10 +639,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f) DefId def = defArena->freshCell(local, local->location); graph.localDefs[local] = def; - if (FFlag::LuauDfgScopeStackNotNull) - currentScope()->bindings[local] = def; - else - currentScope_DEPRECATED()->bindings[local] = def; + currentScope()->bindings[local] = def; captures[local].allVersions.push_back(def); } @@ -757,10 +718,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l) { DefId def = defArena->freshCell(l->name, l->location); graph.localDefs[l->name] = def; - if (FFlag::LuauDfgScopeStackNotNull) - currentScope()->bindings[l->name] = def; - else - currentScope_DEPRECATED()->bindings[l->name] = def; + currentScope()->bindings[l->name] = def; captures[l->name].allVersions.push_back(def); visitExpr(l->func); @@ -793,10 +751,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareGlobal* d) { DefId def = defArena->freshCell(d->name, d->nameLocation); graph.declaredDefs[d] = def; - if (FFlag::LuauDfgScopeStackNotNull) - currentScope()->bindings[d->name] = def; - else - currentScope_DEPRECATED()->bindings[d->name] = def; + currentScope()->bindings[d->name] = def; captures[d->name].allVersions.push_back(def); visitType(d->type); @@ -808,10 +763,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d) { DefId def = defArena->freshCell(d->name, d->nameLocation); graph.declaredDefs[d] = def; - if (FFlag::LuauDfgScopeStackNotNull) - currentScope()->bindings[d->name] = def; - else - currentScope_DEPRECATED()->bindings[d->name] = def; + currentScope()->bindings[d->name] = def; captures[d->name].allVersions.push_back(def); DfgScope* unreachable = makeChildScope(); @@ -1058,10 +1010,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t) { DefId tableCell = defArena->freshCell(Symbol{}, t->location); - if (FFlag::LuauDfgScopeStackNotNull) - currentScope()->props[tableCell] = {}; - else - currentScope_DEPRECATED()->props[tableCell] = {}; + currentScope()->props[tableCell] = {}; for (AstExprTable::Item item : t->items) { DataFlowResult result = visitExpr(item.value); @@ -1070,10 +1019,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTable* t) visitExpr(item.key); if (auto string = item.key->as()) { - if (FFlag::LuauDfgScopeStackNotNull) - currentScope()->props[tableCell][string->value.data] = result.def; - else - currentScope_DEPRECATED()->props[tableCell][string->value.data] = result.def; + currentScope()->props[tableCell][string->value.data] = result.def; } } } @@ -1168,10 +1114,10 @@ void DataFlowGraphBuilder::visitLValue(AstExpr* e, DefId incomingDef) DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef) { - DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); + DfgScope* scope = currentScope(); // In order to avoid alias tracking, we need to clip the reference to the parent def. - if (scope->canUpdateDefinition(l->local) && !l->upvalue) + if (!l->upvalue) { DefId updated = defArena->freshCell(l->local, l->location, containsSubscriptedDefinition(incomingDef)); scope->bindings[l->local] = updated; @@ -1184,33 +1130,22 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef) DefId DataFlowGraphBuilder::visitLValue(AstExprGlobal* g, DefId incomingDef) { - DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); + DfgScope* scope = currentScope(); - // In order to avoid alias tracking, we need to clip the reference to the parent def. - if (scope->canUpdateDefinition(g->name)) - { - DefId updated = defArena->freshCell(g->name, g->location, containsSubscriptedDefinition(incomingDef)); - scope->bindings[g->name] = updated; - captures[g->name].allVersions.push_back(updated); - return updated; - } - else - return visitExpr(static_cast(g)).def; + DefId updated = defArena->freshCell(g->name, g->location, containsSubscriptedDefinition(incomingDef)); + scope->bindings[g->name] = updated; + captures[g->name].allVersions.push_back(updated); + return updated; } DefId DataFlowGraphBuilder::visitLValue(AstExprIndexName* i, DefId incomingDef) { DefId parentDef = visitExpr(i->expr).def; - DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); - if (scope->canUpdateDefinition(parentDef, i->index.value)) - { - DefId updated = defArena->freshCell(i->index, i->location, containsSubscriptedDefinition(incomingDef)); - scope->props[parentDef][i->index.value] = updated; - return updated; - } - else - return visitExpr(static_cast(i)).def; + DfgScope* scope = currentScope(); + DefId updated = defArena->freshCell(i->index, i->location, containsSubscriptedDefinition(incomingDef)); + scope->props[parentDef][i->index.value] = updated; + return updated; } DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef) @@ -1218,17 +1153,12 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprIndexExpr* i, DefId incomingDef) DefId parentDef = visitExpr(i->expr).def; visitExpr(i->index); - DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED(); + DfgScope* scope = currentScope(); if (auto string = i->index->as()) { - if (scope->canUpdateDefinition(parentDef, string->value.data)) - { - DefId updated = defArena->freshCell(Symbol{}, i->location, containsSubscriptedDefinition(incomingDef)); - scope->props[parentDef][string->value.data] = updated; - return updated; - } - else - return visitExpr(static_cast(i)).def; + DefId updated = defArena->freshCell(Symbol{}, i->location, containsSubscriptedDefinition(incomingDef)); + scope->props[parentDef][string->value.data] = updated; + return updated; } else return defArena->freshCell(Symbol{}, i->location, /*subscripted=*/true); diff --git a/Analysis/src/EmbeddedBuiltinDefinitions.cpp b/Analysis/src/EmbeddedBuiltinDefinitions.cpp index 8ae1d314..e255451e 100644 --- a/Analysis/src/EmbeddedBuiltinDefinitions.cpp +++ b/Analysis/src/EmbeddedBuiltinDefinitions.cpp @@ -1,8 +1,6 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/BuiltinDefinitions.h" -LUAU_FASTFLAG(LuauTypeFunOptional) - namespace Luau { @@ -364,29 +362,6 @@ export type type = { static constexpr const char* kBuiltinDefinitionTypesLibSrc = R"BUILTIN_SRC( -declare types: { - unknown: type, - never: type, - any: type, - boolean: type, - number: type, - string: type, - thread: type, - buffer: type, - - singleton: @checked (arg: string | boolean | nil) -> type, - generic: @checked (name: string, ispack: boolean?) -> type, - negationof: @checked (arg: type) -> type, - unionof: @checked (...type) -> type, - intersectionof: @checked (...type) -> type, - newtable: @checked (props: {[type]: type} | {[type]: { read: type, write: type } } | nil, indexer: { index: type, readresult: type, writeresult: type }?, metatable: type?) -> type, - newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type, - copy: @checked (arg: type) -> type, -} -)BUILTIN_SRC"; - -static constexpr const char* kBuiltinDefinitionTypesLibWithOptionalSrc = R"BUILTIN_SRC( - declare types: { unknown: type, never: type, @@ -415,10 +390,7 @@ std::string getTypeFunctionDefinitionSource() std::string result = kBuiltinDefinitionTypeMethodSrc; - if (FFlag::LuauTypeFunOptional) - result += kBuiltinDefinitionTypesLibWithOptionalSrc; - else - result += kBuiltinDefinitionTypesLibSrc; + result += kBuiltinDefinitionTypesLibSrc; return result; } diff --git a/Analysis/src/EqSatSimplification.cpp b/Analysis/src/EqSatSimplification.cpp index bc2725f3..914f8b3c 100644 --- a/Analysis/src/EqSatSimplification.cpp +++ b/Analysis/src/EqSatSimplification.cpp @@ -12,7 +12,6 @@ #include "Luau/Type.h" #include "Luau/TypeArena.h" #include "Luau/TypeFunction.h" -#include "Luau/VisitType.h" #include #include @@ -25,7 +24,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSimplification) LUAU_FASTFLAGVARIABLE(DebugLuauLogSimplificationToDot) LUAU_FASTFLAGVARIABLE(DebugLuauExtraEqSatSanityChecks) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau::EqSatSimplification { @@ -2371,15 +2369,10 @@ void Simplifier::intersectTableProperty(Id id) } Id newTableProp = - FFlag::LuauRemoveTypeCallsForReadWriteProps - ? egraph.add(Intersection{ - toId(egraph, builtinTypes, mappingIdToClass, stringCache, *it->second.readTy), - toId(egraph, builtinTypes, mappingIdToClass, stringCache, *table1Ty->props.begin()->second.readTy) - }) - : egraph.add(Intersection{ - toId(egraph, builtinTypes, mappingIdToClass, stringCache, it->second.type_DEPRECATED()), - toId(egraph, builtinTypes, mappingIdToClass, stringCache, table1Ty->props.begin()->second.type_DEPRECATED()) - }); + egraph.add(Intersection{ + toId(egraph, builtinTypes, mappingIdToClass, stringCache, *it->second.readTy), + toId(egraph, builtinTypes, mappingIdToClass, stringCache, *table1Ty->props.begin()->second.readTy) + }); newIntersectionParts.push_back(egraph.add(TTable{jId, {stringCache.add(it->first)}, {newTableProp}})); @@ -2451,10 +2444,7 @@ void Simplifier::unneededTableModification(Id id) StringId propName = tbl->propNames[i]; const Id propType = tbl->propTypes()[i]; - Id importedProp = - FFlag::LuauRemoveTypeCallsForReadWriteProps - ? toId(egraph, builtinTypes, mappingIdToClass, stringCache, *tt->props.at(stringCache.asString(propName)).readTy) - : toId(egraph, builtinTypes, mappingIdToClass, stringCache, tt->props.at(stringCache.asString(propName)).type_DEPRECATED()); + Id importedProp = toId(egraph, builtinTypes, mappingIdToClass, stringCache, *tt->props.at(stringCache.asString(propName)).readTy); if (find(importedProp) != find(propType)) { diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index be0fbdb8..a7a252de 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -20,9 +20,8 @@ LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) -LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive) LUAU_FASTFLAG(LuauSolverAgnosticStringification) +LUAU_FASTFLAGVARIABLE(LuauNewNonStrictReportsOneIndexedErrors) static std::string wrongNumberOfArgsString( size_t expectedCount, @@ -427,7 +426,7 @@ struct ErrorConverter } else { - if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (FFlag::LuauSolverV2) return it->second.readTy; else return it->second.type_DEPRECATED(); @@ -462,11 +461,8 @@ 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."; - } + 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); } @@ -785,8 +781,12 @@ struct ErrorConverter std::string operator()(const CheckedFunctionCallError& e) const { // TODO: What happens if checkedFunctionName cannot be found?? - return "Function '" + e.checkedFunctionName + "' expects '" + toString(e.expected) + "' at argument #" + std::to_string(e.argumentIndex) + - ", but got '" + Luau::toString(e.passed) + "'"; + if (FFlag::LuauNewNonStrictReportsOneIndexedErrors) + return "Function '" + e.checkedFunctionName + "' expects '" + toString(e.expected) + "' at argument #" + + std::to_string(e.argumentIndex + 1) + ", but got '" + Luau::toString(e.passed) + "'"; + else + return "Function '" + e.checkedFunctionName + "' expects '" + toString(e.expected) + "' at argument #" + std::to_string(e.argumentIndex) + + ", but got '" + Luau::toString(e.passed) + "'"; } std::string operator()(const NonStrictFunctionDefinitionError& e) const diff --git a/Analysis/src/FragmentAutocomplete.cpp b/Analysis/src/FragmentAutocomplete.cpp index 8e5f99d0..ee4dc438 100644 --- a/Analysis/src/FragmentAutocomplete.cpp +++ b/Analysis/src/FragmentAutocomplete.cpp @@ -31,17 +31,11 @@ LUAU_FASTINT(LuauTypeInferIterationLimit); LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete) -LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection) -LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection) -LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak) -LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation) -LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteIfRecommendations) -LUAU_FASTFLAG(LuauExpectedTypeVisitor) -LUAU_FASTFLAGVARIABLE(LuauPopulateRefinedTypesInFragmentFromOldSolver) LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) LUAU_FASTFLAGVARIABLE(LuauFragmentRequiresCanBeResolvedToAModule) LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements) LUAU_FASTFLAGVARIABLE(LuauPopulateSelfTypesInFragment) +LUAU_FASTFLAGVARIABLE(LuauForInProvidesRecommendations) namespace Luau { @@ -162,73 +156,80 @@ Location getFragmentLocation(AstStat* nearestStatement, const Position& cursorPo if (auto forStat = nearestStatement->as()) { + + if (FFlag::LuauForInProvidesRecommendations) + { + if (forStat->step && forStat->step->location.containsClosed(cursorPosition)) + return {forStat->step->location.begin, cursorPosition}; + if (forStat->to && forStat->to->location.containsClosed(cursorPosition)) + return {forStat->to->location.begin, cursorPosition}; + if (forStat->from && forStat->from->location.containsClosed(cursorPosition)) + return {forStat->from->location.begin, cursorPosition}; + } + if (!forStat->hasDo) return nonEmpty; else + { + if (FFlag::LuauForInProvidesRecommendations) + { + auto completeableExtents = Location{forStat->location.begin, forStat->doLocation.begin}; + if (completeableExtents.containsClosed(cursorPosition)) + return nonEmpty; + } + return empty; + } } if (auto forIn = nearestStatement->as()) { - // If we don't have a do statement if (!forIn->hasDo) return nonEmpty; else - return empty; - } - if (FFlag::LuauFragmentAutocompleteIfRecommendations) - { - if (auto ifS = getNearestIfToCursor(nearestStatement, cursorPosition)) { - auto conditionExtents = Location{ifS->condition->location.begin, ifS->condition->location.end}; - if (conditionExtents.containsClosed(cursorPosition) || !ifS->thenLocation) + if (FFlag::LuauForInProvidesRecommendations) { - // CLI-152249 - the condition parse location can sometimes be after the body of the if - // statement. This is a bug that results returning locations like {3,0 - 2,0} which is - // wrong. - if (ifS->condition->location.begin > cursorPosition) - return empty; - return Location{ifS->condition->location.begin, cursorPosition}; - } - - else if (ifS->thenbody->location.containsClosed(cursorPosition)) - return empty; - else if (auto elseS = ifS->elsebody) - { - if (auto elseIf = ifS->elsebody->as()) + auto completeableExtents = Location{forIn->location.begin, forIn->doLocation.begin}; + if (completeableExtents.containsClosed(cursorPosition)) { - auto elseIfConditionExtents = Location{elseIf->location.begin, elseIf->condition->location.end}; - if (elseIfConditionExtents.containsClosed(cursorPosition)) - return {elseIf->condition->location.begin, cursorPosition}; - if (elseIf->thenbody->hasEnd) - return empty; + if (!forIn->hasIn) + return nonEmpty; else - return {elseS->location.begin, cursorPosition}; + return Location{forIn->inLocation.begin, cursorPosition}; } - return empty; } + return empty; } } - else + if (auto ifS = getNearestIfToCursor(nearestStatement, cursorPosition)) { - if (auto ifS = nearestStatement->as()) + auto conditionExtents = Location{ifS->condition->location.begin, ifS->condition->location.end}; + if (conditionExtents.containsClosed(cursorPosition) || !ifS->thenLocation) { - auto conditionExtents = Location{ifS->location.begin, ifS->condition->location.end}; - if (conditionExtents.containsClosed(cursorPosition)) - return nonEmpty; - else if (ifS->thenbody->location.containsClosed(cursorPosition)) + // CLI-152249 - the condition parse location can sometimes be after the body of the if + // statement. This is a bug that results returning locations like {3,0 - 2,0} which is + // wrong. + if (ifS->condition->location.begin > cursorPosition) return empty; - else if (auto elseS = ifS->elsebody) + return Location{ifS->condition->location.begin, cursorPosition}; + } + + else if (ifS->thenbody->location.containsClosed(cursorPosition)) + return empty; + else if (auto elseS = ifS->elsebody) + { + if (auto elseIf = ifS->elsebody->as()) { - if (auto elseIf = ifS->elsebody->as()) - { - if (elseIf->thenbody->hasEnd) - return empty; - else - return {elseS->location.begin, cursorPosition}; - } - return empty; + auto elseIfConditionExtents = Location{elseIf->location.begin, elseIf->condition->location.end}; + if (elseIfConditionExtents.containsClosed(cursorPosition)) + return {elseIf->condition->location.begin, cursorPosition}; + if (elseIf->thenbody->hasEnd) + return empty; + else + return {elseS->location.begin, cursorPosition}; } + return empty; } } @@ -381,8 +382,7 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* st // the freshest ast can sometimes be null if the parse was bad. if (lastGoodParse == nullptr) return {}; - FragmentRegion region = FFlag::LuauBlockDiffFragmentSelection ? getFragmentRegionWithBlockDiff(stale, lastGoodParse, cursorPos) - : getFragmentRegion(lastGoodParse, cursorPos); + FragmentRegion region = getFragmentRegionWithBlockDiff(stale, lastGoodParse, cursorPos); std::vector ancestry = findAncestryAtPositionForAutocomplete(stale, cursorPos); LUAU_ASSERT(ancestry.size() >= 1); // We should only pick up locals that are before the region @@ -524,30 +524,19 @@ std::optional parseFragment( if (p.root == nullptr) return std::nullopt; - std::vector fabricatedAncestry; - // Moves cannot be on the rhs of a ? : statement, so the assignment is placed in an if else stmt - // Make sure to trim this comment after you remove FFlag::LuauFragmentAutocompleteIfRecommendations - if (FFlag::LuauFragmentAutocompleteIfRecommendations) - fabricatedAncestry = findAncestryAtPositionForAutocomplete(mostRecentParse, cursorPos); - else - fabricatedAncestry = std::move(result.ancestry); + std::vector fabricatedAncestry = findAncestryAtPositionForAutocomplete(mostRecentParse, cursorPos); std::vector fragmentAncestry = findAncestryAtPositionForAutocomplete(p.root, cursorPos); // Computes the accurate ancestry and then replaces the nodes that correspond to the fragment ancestry // Needed because we look up types by pointer identity - if (FFlag::LuauFragmentAutocompleteIfRecommendations) + LUAU_ASSERT(!fabricatedAncestry.empty()); + auto back = fabricatedAncestry.size() - 1; + for (auto it = fragmentAncestry.rbegin(); it != fragmentAncestry.rend(); ++it) { - LUAU_ASSERT(!fabricatedAncestry.empty()); - auto back = fabricatedAncestry.size() - 1; - for (auto it = fragmentAncestry.rbegin(); it != fragmentAncestry.rend(); ++it) - { - if (back >= 0 && back < fabricatedAncestry.size() && (*it)->classIndex == fabricatedAncestry[back]->classIndex) - fabricatedAncestry[back] = *it; - back--; - } + if (back >= 0 && back < fabricatedAncestry.size() && (*it)->classIndex == fabricatedAncestry[back]->classIndex) + fabricatedAncestry[back] = *it; + back--; } - else - fabricatedAncestry.insert(fabricatedAncestry.end(), fragmentAncestry.begin(), fragmentAncestry.end()); if (nearestStatement == nullptr) nearestStatement = p.root; @@ -629,8 +618,7 @@ struct UsageFinder : public AstVisitor bool visit(AstExprGlobal* global) override { - if (FFlag::LuauGlobalVariableModuleIsolation) - globalDefsToPrePopulate.emplace_back(global->name, dfg->getDef(global)); + globalDefsToPrePopulate.emplace_back(global->name, dfg->getDef(global)); if (FFlag::LuauFragmentAutocompleteTracksRValueRefinements) { auto def = dfg->getDef(global); @@ -641,11 +629,8 @@ struct UsageFinder : public AstVisitor bool visit(AstStatFunction* function) override { - if (FFlag::LuauGlobalVariableModuleIsolation) - { - if (AstExprGlobal* g = function->name->as()) - globalFunctionsReferenced.emplace_back(g->name); - } + if (AstExprGlobal* g = function->name->as()) + globalFunctionsReferenced.emplace_back(g->name); return true; } @@ -702,13 +687,6 @@ void cloneTypesFromFragment( destScope->lvalueTypes[d] = Luau::cloneIncremental(pair->second.typeId, *destArena, cloneState, destScope); destScope->bindings[pair->first] = Luau::cloneIncremental(pair->second, *destArena, cloneState, destScope); } - else if (FFlag::LuauBetterScopeSelection && !FFlag::LuauBlockDiffFragmentSelection) - { - destScope->lvalueTypes[d] = builtins->unknownType; - Binding b; - b.typeId = builtins->unknownType; - destScope->bindings[Symbol(loc)] = b; - } } if (FFlag::LuauFragmentAutocompleteTracksRValueRefinements) @@ -724,7 +702,7 @@ void cloneTypesFromFragment( } } } - else if (FFlag::LuauPopulateRefinedTypesInFragmentFromOldSolver && !staleModule->checkedInNewSolver) + else if (!staleModule->checkedInNewSolver) { for (const auto& [d, loc] : f.localBindingsReferenced) { @@ -761,41 +739,38 @@ void cloneTypesFromFragment( } } - if (FFlag::LuauGlobalVariableModuleIsolation) + // Fourth - prepopulate the global function types + for (const auto& name : f.globalFunctionsReferenced) { - // Fourth - prepopulate the global function types - for (const auto& name : f.globalFunctionsReferenced) + if (auto ty = staleModule->getModuleScope()->lookup(name)) { - if (auto ty = staleModule->getModuleScope()->lookup(name)) - { - destScope->bindings[name] = Binding{Luau::cloneIncremental(*ty, *destArena, cloneState, destScope)}; - } - else - { - TypeId bt = destArena->addType(BlockedType{}); - destScope->bindings[name] = Binding{bt}; - } + destScope->bindings[name] = Binding{Luau::cloneIncremental(*ty, *destArena, cloneState, destScope)}; } - - // Fifth - prepopulate the globals here - for (const auto& [name, def] : f.globalDefsToPrePopulate) + else { - if (auto ty = staleModule->getModuleScope()->lookup(name)) - { - destScope->lvalueTypes[def] = Luau::cloneIncremental(*ty, *destArena, cloneState, destScope); - } - else if (auto ty = destScope->lookup(name)) - { - // This branch is a little strange - we are looking up a symbol in the destScope - // This scope has no parent pointer, and only cloned types are written to it, so this is a - // safe operation to do without cloning. - // The reason we do this, is the usage finder will traverse the global functions referenced first - // If there is no name associated with this function at the global scope, it must appear first in the fragment and we must - // create a blocked type for it. We write this blocked type directly into the `destScope` bindings - // Then when we go to traverse the `AstExprGlobal` associated with this function, we need to ensure that we map the def -> blockedType - // in `lvalueTypes`, which was previously written into `destScope` - destScope->lvalueTypes[def] = *ty; - } + TypeId bt = destArena->addType(BlockedType{}); + destScope->bindings[name] = Binding{bt}; + } + } + + // Fifth - prepopulate the globals here + for (const auto& [name, def] : f.globalDefsToPrePopulate) + { + if (auto ty = staleModule->getModuleScope()->lookup(name)) + { + destScope->lvalueTypes[def] = Luau::cloneIncremental(*ty, *destArena, cloneState, destScope); + } + else if (auto ty = destScope->lookup(name)) + { + // This branch is a little strange - we are looking up a symbol in the destScope + // This scope has no parent pointer, and only cloned types are written to it, so this is a + // safe operation to do without cloning. + // The reason we do this, is the usage finder will traverse the global functions referenced first + // If there is no name associated with this function at the global scope, it must appear first in the fragment and we must + // create a blocked type for it. We write this blocked type directly into the `destScope` bindings + // Then when we go to traverse the `AstExprGlobal` associated with this function, we need to ensure that we map the def -> blockedType + // in `lvalueTypes`, which was previously written into `destScope` + destScope->lvalueTypes[def] = *ty; } } @@ -952,11 +927,6 @@ static std::pair getDocumentOffsets(std::string_view src, const if (endPos.line == lineCount && endPos.column == colCount) { endOffset = docOffset; - if (!FFlag::LuauFragmentAutocompleteIfRecommendations) - { - while (endOffset < src.size() && src[endOffset] != '\n') - endOffset++; - } foundEnd = true; } @@ -1006,8 +976,6 @@ ScopePtr findClosestScope_DEPRECATED(const ModulePtr& module, const AstStat* nea ScopePtr findClosestScope(const ModulePtr& module, const Position& scopePos) { LUAU_ASSERT(module->hasModuleScope()); - if (FFlag::LuauBlockDiffFragmentSelection) - { ScopePtr closest = module->getModuleScope(); // find the scope the nearest statement belonged to. for (const auto& [loc, sc] : module->scopes) @@ -1020,18 +988,6 @@ ScopePtr findClosestScope(const ModulePtr& module, const Position& scopePos) closest = sc; } return closest; - } - else - { - ScopePtr closest = module->getModuleScope(); - // find the scope the nearest statement belonged to. - for (const auto& [loc, sc] : module->scopes) - { - if (sc->location.contains(scopePos) && closest->location.begin < sc->location.begin) - closest = sc; - } - return closest; - } } std::optional parseFragment_DEPRECATED( @@ -1228,7 +1184,7 @@ FragmentTypeCheckResult typecheckFragment_( NotNull{&resolver}, frontend.builtinTypes, iceHandler, - FFlag::LuauGlobalVariableModuleIsolation ? freshChildOfNearestScope : stale->getModuleScope(), + freshChildOfNearestScope, frontend.globals.globalTypeFunctionScope, nullptr, nullptr, @@ -1299,18 +1255,15 @@ FragmentTypeCheckResult typecheckFragment_( reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverEnd); - if (FFlag::LuauExpectedTypeVisitor) - { - ExpectedTypeVisitor etv{ - NotNull{&incrementalModule->astTypes}, - NotNull{&incrementalModule->astExpectedTypes}, - NotNull{&incrementalModule->astResolvedTypes}, - NotNull{&incrementalModule->internalTypes}, - frontend.builtinTypes, - NotNull{freshChildOfNearestScope.get()} - }; - root->visit(&etv); - } + ExpectedTypeVisitor etv{ + NotNull{&incrementalModule->astTypes}, + NotNull{&incrementalModule->astExpectedTypes}, + NotNull{&incrementalModule->astResolvedTypes}, + NotNull{&incrementalModule->internalTypes}, + frontend.builtinTypes, + NotNull{freshChildOfNearestScope.get()} + }; + root->visit(&etv); // In frontend we would forbid internal types // because this is just for autocomplete, we don't actually care @@ -1386,7 +1339,7 @@ FragmentTypeCheckResult typecheckFragment__DEPRECATED( NotNull{&resolver}, frontend.builtinTypes, iceHandler, - FFlag::LuauGlobalVariableModuleIsolation ? freshChildOfNearestScope : stale->getModuleScope(), + freshChildOfNearestScope, frontend.globals.globalTypeFunctionScope, nullptr, nullptr, @@ -1457,18 +1410,15 @@ FragmentTypeCheckResult typecheckFragment__DEPRECATED( reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverEnd); - if (FFlag::LuauExpectedTypeVisitor) - { - ExpectedTypeVisitor etv{ - NotNull{&incrementalModule->astTypes}, - NotNull{&incrementalModule->astExpectedTypes}, - NotNull{&incrementalModule->astResolvedTypes}, - NotNull{&incrementalModule->internalTypes}, - frontend.builtinTypes, - NotNull{freshChildOfNearestScope.get()} - }; - root->visit(&etv); - } + ExpectedTypeVisitor etv{ + NotNull{&incrementalModule->astTypes}, + NotNull{&incrementalModule->astExpectedTypes}, + NotNull{&incrementalModule->astResolvedTypes}, + NotNull{&incrementalModule->internalTypes}, + frontend.builtinTypes, + NotNull{freshChildOfNearestScope.get()} + }; + root->visit(&etv); // In frontend we would forbid internal types // because this is just for autocomplete, we don't actually care @@ -1507,8 +1457,7 @@ std::pair typecheckFragment( } std::optional tryParse; - tryParse = FFlag::LuauBetterScopeSelection ? parseFragment(module->root, recentParse, module->names.get(), src, cursorPos, fragmentEndPosition) - : parseFragment_DEPRECATED(module->root, module->names.get(), src, cursorPos, fragmentEndPosition); + tryParse = parseFragment(module->root, recentParse, module->names.get(), src, cursorPos, fragmentEndPosition); if (!tryParse) @@ -1520,8 +1469,7 @@ std::pair typecheckFragment( return {FragmentTypeCheckStatus::SkipAutocomplete, {}}; FrontendOptions frontendOptions = opts.value_or(frontend.options); - const ScopePtr& closestScope = FFlag::LuauBetterScopeSelection ? findClosestScope(module, parseResult.scopePos) - : findClosestScope_DEPRECATED(module, parseResult.nearestStatement); + const ScopePtr& closestScope = findClosestScope(module, parseResult.scopePos); FragmentTypeCheckResult result = FFlag::LuauFragmentRequiresCanBeResolvedToAModule ? typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter) @@ -1590,13 +1538,11 @@ FragmentAutocompleteResult fragmentAutocomplete( auto globalScope = (opts && opts->forAutocomplete) ? frontend.globalsForAutocomplete.globalScope.get() : frontend.globals.globalScope.get(); if (FFlag::DebugLogFragmentsFromAutocomplete) logLuau("Fragment Autocomplete Source Script", src); - TypeArena arenaForAutocomplete_DEPRECATED; - if (FFlag::LuauFragmentAcMemoryLeak) - unfreeze(tcResult.incrementalModule->internalTypes); + unfreeze(tcResult.incrementalModule->internalTypes); auto result = Luau::autocomplete_( tcResult.incrementalModule, frontend.builtinTypes, - FFlag::LuauFragmentAcMemoryLeak ? &tcResult.incrementalModule->internalTypes : &arenaForAutocomplete_DEPRECATED, + &tcResult.incrementalModule->internalTypes, tcResult.ancestry, globalScope, tcResult.freshScope, @@ -1604,10 +1550,9 @@ FragmentAutocompleteResult fragmentAutocomplete( frontend.fileResolver, std::move(callback) ); - if (FFlag::LuauFragmentAcMemoryLeak) - freeze(tcResult.incrementalModule->internalTypes); + freeze(tcResult.incrementalModule->internalTypes); reportWaypoint(reporter, FragmentAutocompleteWaypoint::AutocompleteEnd); - return {std::move(tcResult.incrementalModule), tcResult.freshScope.get(), std::move(arenaForAutocomplete_DEPRECATED), std::move(result)}; + return {std::move(tcResult.incrementalModule), tcResult.freshScope.get(), std::move(result)}; } } // namespace Luau diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 5dfcdf5a..50b8db45 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -44,12 +44,11 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile) LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes) LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode) LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode) -LUAU_FASTFLAGVARIABLE(LuauExpectedTypeVisitor) LUAU_FASTFLAGVARIABLE(LuauTrackTypeAllocations) LUAU_FASTFLAGVARIABLE(LuauUseWorkspacePropToChooseSolver) LUAU_FASTFLAGVARIABLE(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete) LUAU_FASTFLAGVARIABLE(DebugLuauAlwaysShowConstraintSolvingIncomplete) -LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving) +LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3) namespace Luau { @@ -211,7 +210,8 @@ LoadDefinitionFileResult Frontend::loadDefinitionFile( if (parseResult.errors.size() > 0) return LoadDefinitionFileResult{false, std::move(parseResult), std::move(sourceModule), nullptr}; - ModulePtr checkedModule = check(sourceModule, Mode::Definition, {}, std::nullopt, /*forAutocomplete*/ false, /*recordJsonLog*/ false, {}); + Frontend::Stats dummyStats; + ModulePtr checkedModule = check(sourceModule, Mode::Definition, {}, std::nullopt, /*forAutocomplete*/ false, /*recordJsonLog*/ false, dummyStats, {}); if (checkedModule->errors.size() > 0) return LoadDefinitionFileResult{false, std::move(parseResult), std::move(sourceModule), std::move(checkedModule)}; @@ -988,6 +988,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) environmentScope, /*forAutocomplete*/ true, /*recordJsonLog*/ false, + item.stats, std::move(typeCheckLimits) ); @@ -1018,7 +1019,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) } ModulePtr module = - check(sourceModule, mode, requireCycles, environmentScope, /*forAutocomplete*/ false, item.recordJsonLog, std::move(typeCheckLimits)); + check(sourceModule, mode, requireCycles, environmentScope, /*forAutocomplete*/ false, item.recordJsonLog, item.stats, std::move(typeCheckLimits)); double duration = getTimestamp() - timestamp; @@ -1171,6 +1172,8 @@ void Frontend::recordItemResult(const BuildQueueItem& item) stats.strSingletonsMinted += item.stats.strSingletonsMinted; stats.uniqueStrSingletonsMinted += item.stats.uniqueStrSingletonsMinted; } + + stats.dynamicConstraintsCreated += item.stats.dynamicConstraintsCreated; } void Frontend::performQueueItemTask(std::shared_ptr state, size_t itemPos) @@ -1333,41 +1336,6 @@ const SourceModule* Frontend::getSourceModule(const ModuleName& moduleName) cons return const_cast(this)->getSourceModule(moduleName); } -ModulePtr check( - const SourceModule& sourceModule, - Mode mode, - const std::vector& requireCycles, - NotNull builtinTypes, - NotNull iceHandler, - NotNull moduleResolver, - NotNull fileResolver, - const ScopePtr& parentScope, - const ScopePtr& typeFunctionScope, - std::function prepareModuleScope, - FrontendOptions options, - TypeCheckLimits limits, - std::function writeJsonLog -) -{ - const bool recordJsonLog = FFlag::DebugLuauLogSolverToJson; - return check( - sourceModule, - mode, - requireCycles, - builtinTypes, - iceHandler, - moduleResolver, - fileResolver, - parentScope, - typeFunctionScope, - std::move(prepareModuleScope), - std::move(options), - std::move(limits), - recordJsonLog, - std::move(writeJsonLog) - ); -} - struct InternalTypeFinder : TypeOnceVisitor { InternalTypeFinder() @@ -1431,6 +1399,7 @@ ModulePtr check( FrontendOptions options, TypeCheckLimits limits, bool recordJsonLog, + Frontend::Stats& stats, std::function writeJsonLog ) { @@ -1555,6 +1524,8 @@ ModulePtr check( result->cancelled = true; } + stats.dynamicConstraintsCreated += cs->solverConstraints.size(); + if (recordJsonLog) { std::string output = logger->compileOutput(); @@ -1641,22 +1612,19 @@ ModulePtr check( result->errors.clear(); } - if (FFlag::LuauExpectedTypeVisitor) - { - ExpectedTypeVisitor etv{ - NotNull{&result->astTypes}, - NotNull{&result->astExpectedTypes}, - NotNull{&result->astResolvedTypes}, - NotNull{&result->internalTypes}, - builtinTypes, - NotNull{parentScope.get()} - }; - sourceModule.root->visit(&etv); - } + ExpectedTypeVisitor etv{ + NotNull{&result->astTypes}, + NotNull{&result->astExpectedTypes}, + NotNull{&result->astResolvedTypes}, + NotNull{&result->internalTypes}, + builtinTypes, + NotNull{parentScope.get()} + }; + sourceModule.root->visit(&etv); // NOTE: This used to be done prior to cloning the public interface, but // we now replace "internal" types with `*error-type*`. - if (FFlag::LuauLimitDynamicConstraintSolving) + if (FFlag::LuauLimitDynamicConstraintSolving3) { if (FFlag::DebugLuauForbidInternalTypes) { @@ -1696,7 +1664,7 @@ ModulePtr check( else result->clonePublicInterface_DEPRECATED(builtinTypes, *iceHandler); - if (!FFlag::LuauLimitDynamicConstraintSolving) + if (!FFlag::LuauLimitDynamicConstraintSolving3) { if (FFlag::DebugLuauForbidInternalTypes) { @@ -1751,6 +1719,7 @@ ModulePtr Frontend::check( std::optional environmentScope, bool forAutocomplete, bool recordJsonLog, + Frontend::Stats& stats, TypeCheckLimits typeCheckLimits ) { @@ -1778,6 +1747,7 @@ ModulePtr Frontend::check( options, std::move(typeCheckLimits), recordJsonLog, + stats, writeJsonLog ); } diff --git a/Analysis/src/Generalization.cpp b/Analysis/src/Generalization.cpp index c0c284a8..2d786715 100644 --- a/Analysis/src/Generalization.cpp +++ b/Analysis/src/Generalization.cpp @@ -15,11 +15,7 @@ #include "Luau/TypePack.h" #include "Luau/VisitType.h" -LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) - LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization4) -LUAU_FASTFLAGVARIABLE(LuauGeneralizationCannotMutateAcrossModules) namespace Luau { @@ -485,56 +481,33 @@ struct FreeTypeSearcher : TypeVisitor for (const auto& [_name, prop] : tt.props) { - if (FFlag::LuauEnableWriteOnlyProperties) + if (prop.isReadOnly()) { - 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; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - traverse(*prop.readTy); - else - traverse(prop.type_DEPRECATED()); - polarity = p; - } - else - { - LUAU_ASSERT(prop.isReadWrite() && !prop.isShared()); - - traverse(*prop.readTy); - Polarity p = polarity; - polarity = Polarity::Negative; - traverse(*prop.writeTy); - polarity = p; - } + 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.readTy); + polarity = p; } else { - if (prop.isReadOnly()) - traverse(*prop.readTy); - else - { - LUAU_ASSERT(prop.isShared()); + LUAU_ASSERT(prop.isReadWrite() && !prop.isShared()); - Polarity p = polarity; - polarity = Polarity::Mixed; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - traverse(*prop.readTy); - else - traverse(prop.type_DEPRECATED()); - polarity = p; - } + traverse(*prop.readTy); + Polarity p = polarity; + polarity = Polarity::Negative; + traverse(*prop.writeTy); + polarity = p; } } @@ -1447,56 +1420,33 @@ struct GenericCounter : TypeVisitor for (const auto& [_name, prop] : tt.props) { - if (FFlag::LuauEnableWriteOnlyProperties) + if (prop.isReadOnly()) { - 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; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - traverse(*prop.readTy); - else - traverse(prop.type_DEPRECATED()); - polarity = p; - } - else - { - LUAU_ASSERT(prop.isReadWrite() && !prop.isShared()); - - traverse(*prop.readTy); - Polarity p = polarity; - polarity = Polarity::Negative; - traverse(*prop.writeTy); - polarity = p; - } + 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.readTy); + polarity = p; } else { - if (prop.isReadOnly()) - traverse(*prop.readTy); - else - { - LUAU_ASSERT(prop.isShared()); + LUAU_ASSERT(prop.isReadWrite() && !prop.isShared()); - Polarity p = polarity; - polarity = Polarity::Mixed; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - traverse(*prop.readTy); - else - traverse(prop.type_DEPRECATED()); - polarity = p; - } + traverse(*prop.readTy); + Polarity p = polarity; + polarity = Polarity::Negative; + traverse(*prop.writeTy); + polarity = p; } } @@ -1596,7 +1546,7 @@ void pruneUnnecessaryGenerics( { if (state.count == 1 && state.polarity != Polarity::Mixed) { - if (FFlag::LuauGeneralizationCannotMutateAcrossModules && arena.get() != generic->owningArena) + if (arena.get() != generic->owningArena) continue; emplaceType(asMutable(generic), builtinTypes->unknownType); } diff --git a/Analysis/src/InferPolarity.cpp b/Analysis/src/InferPolarity.cpp index 1c0222c6..2eb650ae 100644 --- a/Analysis/src/InferPolarity.cpp +++ b/Analysis/src/InferPolarity.cpp @@ -6,7 +6,6 @@ #include "Luau/VisitType.h" LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauInferPolarityOfReadWriteProperties) namespace Luau @@ -51,51 +50,25 @@ struct InferPolarity : TypeVisitor return false; const Polarity p = polarity; - if (FFlag::LuauInferPolarityOfReadWriteProperties || FFlag::LuauRemoveTypeCallsForReadWriteProps) + for (const auto& [name, prop] : tt.props) { - for (const auto& [name, prop] : tt.props) + if (prop.isShared()) { - if (prop.isShared()) - { - polarity = Polarity::Mixed; - traverse(*prop.readTy); - continue; - } - - if (prop.readTy) - { - polarity = p; - traverse(*prop.readTy); - } - - if (prop.writeTy) - { - polarity = invert(p); - traverse(*prop.writeTy); - } + polarity = Polarity::Mixed; + traverse(*prop.readTy); + continue; } - } - else - { - for (const auto& [name, prop] : tt.props) + + if (prop.readTy) { - if (prop.isShared()) - { - polarity = Polarity::Mixed; - traverse(prop.type_DEPRECATED()); - } - else if (prop.isReadOnly()) - { - polarity = p; - traverse(*prop.readTy); - } - else if (prop.isWriteOnly()) - { - polarity = invert(p); - traverse(*prop.writeTy); - } - else - LUAU_ASSERT(!"Unreachable"); + polarity = p; + traverse(*prop.readTy); + } + + if (prop.writeTy) + { + polarity = invert(p); + traverse(*prop.writeTy); } } diff --git a/Analysis/src/Module.cpp b/Analysis/src/Module.cpp index 12c538e7..4ac40aef 100644 --- a/Analysis/src/Module.cpp +++ b/Analysis/src/Module.cpp @@ -16,7 +16,7 @@ LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) -LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving) +LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3) namespace Luau { @@ -100,6 +100,7 @@ struct ClonePublicInterface : Substitution // NOTE: This can be made non-optional after // LuauUseWorkspacePropToChooseSolver is clipped. std::optional solverMode{std::nullopt}; + bool internalTypeEscaped = false; ClonePublicInterface(const TxnLog* log, NotNull builtinTypes, Module* module) : Substitution(log, &module->interfaceTypes) @@ -177,21 +178,20 @@ struct ClonePublicInterface : Substitution { ttv->level = TypeLevel{0, 0}; if (isNewSolver()) + { ttv->scope = nullptr; + if (FFlag::LuauLimitDynamicConstraintSolving3) + ttv->state = TableState::Sealed; + } } if (isNewSolver()) { - if (FFlag::LuauLimitDynamicConstraintSolving) + if (FFlag::LuauLimitDynamicConstraintSolving3) { if (is(ty)) { - module->errors.emplace_back( - Location{}, // Not amazing but the best we can do. - module->name, - InternalError{"An internal type is escaping this module; please report this bug at " - "https://github.com/luau-lang/luau/issues"} - ); + internalTypeEscaped = true; result = builtinTypes->errorType; } else if (auto genericty = getMutable(result)) @@ -225,16 +225,11 @@ struct ClonePublicInterface : Substitution { if (isNewSolver()) { - if (FFlag::LuauLimitDynamicConstraintSolving) + if (FFlag::LuauLimitDynamicConstraintSolving3) { if (is(tp)) { - module->errors.emplace_back( - Location{}, - module->name, - InternalError{"An internal type pack is escaping this module; please report this bug at " - "https://github.com/luau-lang/luau/issues"} - ); + internalTypeEscaped = true; return builtinTypes->errorTypePack; } @@ -369,6 +364,16 @@ void Module::clonePublicInterface_DEPRECATED(NotNull builtinTypes, *tf = clonePublicInterface.cloneTypeFun(*tf); } + if (FFlag::LuauLimitDynamicConstraintSolving3 && clonePublicInterface.internalTypeEscaped) + { + errors.emplace_back( + Location{}, // Not amazing but the best we can do. + name, + InternalError{"An internal type is escaping this module; please report this bug at " + "https://github.com/luau-lang/luau/issues"} + ); + } + // Copy external stuff over to Module itself this->returnType = moduleScope->returnType; this->exportedTypeBindings = moduleScope->exportedTypeBindings; @@ -410,6 +415,16 @@ void Module::clonePublicInterface(NotNull builtinTypes, InternalEr *tf = clonePublicInterface.cloneTypeFun(*tf); } + if (FFlag::LuauLimitDynamicConstraintSolving3 && clonePublicInterface.internalTypeEscaped) + { + errors.emplace_back( + Location{}, // Not amazing but the best we can do. + name, + InternalError{"An internal type is escaping this module; please report this bug at " + "https://github.com/luau-lang/luau/issues"} + ); + } + // Copy external stuff over to Module itself this->returnType = moduleScope->returnType; this->exportedTypeBindings = moduleScope->exportedTypeBindings; diff --git a/Analysis/src/NonStrictTypeChecker.cpp b/Analysis/src/NonStrictTypeChecker.cpp index eae1dcfa..547a8003 100644 --- a/Analysis/src/NonStrictTypeChecker.cpp +++ b/Analysis/src/NonStrictTypeChecker.cpp @@ -22,7 +22,6 @@ LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAGVARIABLE(LuauNewNonStrictFixGenericTypePacks) LUAU_FASTFLAGVARIABLE(LuauNewNonStrictMoreUnknownSymbols) LUAU_FASTFLAGVARIABLE(LuauNewNonStrictNoErrorsPassingNever) LUAU_FASTFLAGVARIABLE(LuauNewNonStrictSuppressesDynamicRequireErrors) @@ -1095,43 +1094,19 @@ struct NonStrictTypeChecker Scope* scope = findInnermostScope(tp->location); LUAU_ASSERT(scope); - if (FFlag::LuauNewNonStrictFixGenericTypePacks) - { - if (std::optional alias = scope->lookupPack(tp->genericName.value)) - return; + if (std::optional alias = scope->lookupPack(tp->genericName.value)) + return; - if (scope->lookupType(tp->genericName.value)) - return reportError( - SwappedGenericTypeParameter{ - tp->genericName.value, - SwappedGenericTypeParameter::Kind::Pack, - }, - tp->location - ); + if (scope->lookupType(tp->genericName.value)) + return reportError( + SwappedGenericTypeParameter{ + tp->genericName.value, + SwappedGenericTypeParameter::Kind::Pack, + }, + tp->location + ); - reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location); - } - else - { - std::optional alias = scope->lookupPack(tp->genericName.value); - if (!alias.has_value()) - { - if (scope->lookupType(tp->genericName.value)) - { - reportError( - SwappedGenericTypeParameter{ - tp->genericName.value, - SwappedGenericTypeParameter::Kind::Pack, - }, - tp->location - ); - } - } - else - { - reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location); - } - } + reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location); } void visitGenerics(AstArray generics, AstArray genericPacks) diff --git a/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index e7a6e672..61314d87 100644 --- a/Analysis/src/Normalize.cpp +++ b/Analysis/src/Normalize.cpp @@ -21,7 +21,6 @@ LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000) LUAU_FASTINTVARIABLE(LuauNormalizeIntersectionLimit, 200) LUAU_FASTINTVARIABLE(LuauNormalizeUnionLimit, 100) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAGVARIABLE(LuauNormalizationIntersectTablesPreservesExternTypes) LUAU_FASTFLAGVARIABLE(LuauNormalizationReorderFreeTypeIntersect) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) @@ -3061,7 +3060,7 @@ NormalizationResult Normalizer::intersectNormalWithTy( } else if (get(there) || get(there)) { - if (useNewLuauSolver() && FFlag::LuauNormalizationIntersectTablesPreservesExternTypes) + if (useNewLuauSolver()) { NormalizedExternType externTypes = std::move(here.externTypes); TypeIds tables = std::move(here.tables); diff --git a/Analysis/src/OverloadResolution.cpp b/Analysis/src/OverloadResolution.cpp index d1e68f6a..dd6da046 100644 --- a/Analysis/src/OverloadResolution.cpp +++ b/Analysis/src/OverloadResolution.cpp @@ -10,7 +10,6 @@ #include "Luau/TypeUtils.h" #include "Luau/Unifier2.h" -LUAU_FASTFLAGVARIABLE(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) namespace Luau @@ -299,29 +298,16 @@ std::pair OverloadResolver::checkOverload_ // nil or are `unknown`, then this overload does not match. for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i) { - if (FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments) + if (get(follow(requiredHead[i])) || !subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype) { - if (get(follow(requiredHead[i])) || !subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype) - { - auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes); - for (auto arg : fn->argTypes) - if (get(follow(arg))) - minParams += 1; + auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes); + for (auto arg : fn->argTypes) + if (get(follow(arg))) + minParams += 1; - TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; + TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; - return {Analysis::ArityMismatch, {std::move(error)}}; - } - } - else - { - if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i], scope).isSubtype) - { - auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes); - TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; - - return {Analysis::ArityMismatch, {std::move(error)}}; - } + return {Analysis::ArityMismatch, {std::move(error)}}; } } diff --git a/Analysis/src/Simplify.cpp b/Analysis/src/Simplify.cpp index 19aee333..fc4c8532 100644 --- a/Analysis/src/Simplify.cpp +++ b/Analysis/src/Simplify.cpp @@ -18,8 +18,6 @@ LUAU_FASTINT(LuauTypeReductionRecursionLimit) LUAU_FASTFLAG(LuauSolverV2) LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8) -LUAU_FASTFLAGVARIABLE(LuauSimplificationTableExternType) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauRelateTablesAreNeverDisjoint) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAGVARIABLE(LuauMissingSeenSetRelate) @@ -307,11 +305,7 @@ Relation relateTables(TypeId left, TypeId right, SimplifierSeenSet& seen) if (!leftProp.isShared() || !rightProp.isShared()) return Relation::Intersects; - Relation r; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - r = relate(*leftProp.readTy, *rightProp.readTy, seen); - else - r = relate(leftProp.type_DEPRECATED(), rightProp.type_DEPRECATED(), seen); + Relation r = relate(*leftProp.readTy, *rightProp.readTy, seen); if (r == Relation::Coincident && 1 != leftTable->props.size()) { // eg {tag: "cat", prop: string} & {tag: "cat"} @@ -394,12 +388,9 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen) if (isTypeVariable(left) || isTypeVariable(right)) return Relation::Intersects; - if (FFlag::LuauSimplificationTableExternType) - { - // if either type is a type function, we cannot know if they'll be related. - if (get(left) || get(right)) - return Relation::Intersects; - } + // if either type is a type function, we cannot know if they'll be related. + if (get(left) || get(right)) + return Relation::Intersects; if (get(left)) { @@ -588,42 +579,37 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen) return Relation::Intersects; } - if (FFlag::LuauSimplificationTableExternType) + if (auto re = get(right)) { - if (auto re = get(right)) + Relation overall = Relation::Coincident; + + for (auto& [name, prop] : lt->props) { - Relation overall = Relation::Coincident; - - for (auto& [name, prop] : lt->props) + if (auto propInExternType = re->props.find(name); propInExternType != re->props.end()) { - if (auto propInExternType = re->props.find(name); propInExternType != re->props.end()) + Relation propRel; + if (FFlag::LuauMissingSeenSetRelate) { - Relation propRel; - if (FFlag::LuauMissingSeenSetRelate) - { - LUAU_ASSERT(prop.readTy && propInExternType->second.readTy); - propRel = relate(*prop.readTy, *propInExternType->second.readTy, seen); - } - else if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - LUAU_ASSERT(prop.readTy && propInExternType->second.readTy); - propRel = relate(*prop.readTy, *propInExternType->second.readTy); - } - else - propRel = relate(prop.type_DEPRECATED(), propInExternType->second.type_DEPRECATED()); - - if (propRel == Relation::Disjoint) - return Relation::Disjoint; - - if (propRel == Relation::Coincident) - continue; - - overall = Relation::Intersects; + LUAU_ASSERT(prop.readTy && propInExternType->second.readTy); + propRel = relate(*prop.readTy, *propInExternType->second.readTy, seen); + } + else + { + LUAU_ASSERT(prop.readTy && propInExternType->second.readTy); + propRel = relate(*prop.readTy, *propInExternType->second.readTy); } - } - return overall; + if (propRel == Relation::Disjoint) + return Relation::Disjoint; + + if (propRel == Relation::Coincident) + continue; + + overall = Relation::Intersects; + } } + + return overall; } // TODO metatables @@ -1319,11 +1305,7 @@ std::optional TypeSimplifier::basicIntersect(TypeId left, TypeId right) auto it = rt->props.find(propName); if (it != rt->props.end() && leftProp.isShared() && it->second.isShared()) { - Relation r; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - r = relate(*leftProp.readTy, *it->second.readTy); - else - r = relate(leftProp.type_DEPRECATED(), it->second.type_DEPRECATED()); + Relation r = relate(*leftProp.readTy, *it->second.readTy); switch (r) { diff --git a/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index c714decb..2553d1c2 100644 --- a/Analysis/src/Substitution.cpp +++ b/Analysis/src/Substitution.cpp @@ -10,8 +10,6 @@ LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) -LUAU_FASTFLAG(LuauSolverAgnosticClone) namespace Luau { @@ -191,13 +189,8 @@ void Tarjan::visitChildren(TypeId ty, int index) LUAU_ASSERT(!ttv->boundTo); for (const auto& [name, prop] : ttv->props) { - if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone) - { - visitChild(prop.readTy); - visitChild(prop.writeTy); - } - else - visitChild(prop.type_DEPRECATED()); + visitChild(prop.readTy); + visitChild(prop.writeTy); } if (ttv->indexer) @@ -247,7 +240,7 @@ void Tarjan::visitChildren(TypeId ty, int index) { for (const auto& [name, prop] : etv->props) { - if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (FFlag::LuauSolverV2) { visitChild(prop.readTy); visitChild(prop.writeTy); @@ -783,15 +776,10 @@ void Substitution::replaceChildren(TypeId ty) LUAU_ASSERT(!ttv->boundTo); for (auto& [name, prop] : ttv->props) { - if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone) - { - if (prop.readTy) - prop.readTy = replace(prop.readTy); - if (prop.writeTy) - prop.writeTy = replace(prop.writeTy); - } - else - prop.setType(replace(prop.type_DEPRECATED())); + if (prop.readTy) + prop.readTy = replace(prop.readTy); + if (prop.writeTy) + prop.writeTy = replace(prop.writeTy); } if (ttv->indexer) @@ -841,7 +829,7 @@ void Substitution::replaceChildren(TypeId ty) { for (auto& [name, prop] : etv->props) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2) { if (prop.readTy) prop.readTy = replace(prop.readTy); diff --git a/Analysis/src/Subtyping.cpp b/Analysis/src/Subtyping.cpp index 9dd214ad..43f5b564 100644 --- a/Analysis/src/Subtyping.cpp +++ b/Analysis/src/Subtyping.cpp @@ -21,9 +21,9 @@ LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity) LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100) LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAGVARIABLE(LuauMissingFollowMappedGenericPacks) +LUAU_FASTFLAGVARIABLE(LuauSubtypingNegationsChecksNormalizationComplexity) namespace Luau { @@ -606,11 +606,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub RecursionCounter rc(&counters.recursionCount); if (counters.recursionLimit > 0 && counters.recursionLimit < counters.recursionCount) - { - SubtypingResult result; - result.normalizationTooComplex = true; - return result; - } + return SubtypingResult{false, true}; subTy = follow(subTy); superTy = follow(superTy); @@ -672,10 +668,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub * For now, we do the conservative thing and refuse to cache anything * that touches a cycle. */ - SubtypingResult res; - res.isSubtype = true; - res.isCacheable = false; - return res; + return SubtypingResult{true, false, false}; } SeenSetPopper ssp{&seenTypes, typePair}; @@ -696,19 +689,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub { result = isCovariantWith(env, subTy, superUnion, scope); if (!result.isSubtype && !result.normalizationTooComplex) - { - SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); - - if (semantic.normalizationTooComplex) - { - result = semantic; - } - else if (semantic.isSubtype) - { - semantic.reasoning.clear(); - result = semantic; - } - } + result = trySemanticSubtyping(env, subTy, superTy, scope, result); } else if (auto superIntersection = get(superTy)) result = isCovariantWith(env, subTy, superIntersection, scope); @@ -716,21 +697,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub { result = isCovariantWith(env, subIntersection, superTy, scope); if (!result.isSubtype && !result.normalizationTooComplex) - { - SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); - - if (semantic.normalizationTooComplex) - { - result = semantic; - } - else if (semantic.isSubtype) - { - // Clear the semantic reasoning, as any reasonings within - // potentially contain invalid paths. - semantic.reasoning.clear(); - result = semantic; - } - } + result = trySemanticSubtyping(env, subTy, superTy, scope, result); } else if (get(superTy)) result = {true}; @@ -818,11 +785,16 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub result = isCovariantWith(env, subNegation, superTy, scope); if (!result.isSubtype && !result.normalizationTooComplex) { - SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); - if (semantic.isSubtype) + if (FFlag::LuauSubtypingNegationsChecksNormalizationComplexity) + result = trySemanticSubtyping(env, subTy, superTy, scope, result); + else { - semantic.reasoning.clear(); - result = semantic; + SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); + if (semantic.isSubtype) + { + semantic.reasoning.clear(); + result = semantic; + } } } } @@ -831,11 +803,16 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub result = isCovariantWith(env, subTy, superNegation, scope); if (!result.isSubtype && !result.normalizationTooComplex) { - SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); - if (semantic.isSubtype) + if (FFlag::LuauSubtypingNegationsChecksNormalizationComplexity) + result = trySemanticSubtyping(env, subTy, superTy, scope, result); + else { - semantic.reasoning.clear(); - result = semantic; + SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); + if (semantic.isSubtype) + { + semantic.reasoning.clear(); + result = semantic; + } } } } @@ -901,12 +878,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId { std::pair typePair = {subTp, superTp}; if (!seenPacks.insert(typePair)) - { - SubtypingResult res; - res.isSubtype = true; - res.isCacheable = false; - return res; - } + return SubtypingResult{true, false, false}; popper.emplace(&seenPacks, std::move(typePair)); } @@ -1659,18 +1631,9 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl { if (superProp.isShared()) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, *superProp.readTy, scope) - .withSubComponent(TypePath::TypeField::IndexResult) - .withSuperComponent(TypePath::Property::read(name))); - } - else - { - results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type_DEPRECATED(), scope) - .withSubComponent(TypePath::TypeField::IndexResult) - .withSuperComponent(TypePath::Property::read(name))); - } + results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, *superProp.readTy, scope) + .withSubComponent(TypePath::TypeField::IndexResult) + .withSuperComponent(TypePath::Property::read(name))); } else { @@ -1846,22 +1809,12 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim { if (auto it = mttv->props.find("__index"); it != mttv->props.end()) { + // the `string` metatable should not have any write-only types. + LUAU_ASSERT(*it->second.readTy); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - // the `string` metatable should not have any write-only types. - LUAU_ASSERT(*it->second.readTy); - - if (auto stringTable = get(*it->second.readTy)) - result.orElse(isCovariantWith(env, stringTable, superTable, scope) - .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); - } - else - { - if (auto stringTable = get(it->second.type_DEPRECATED())) - result.orElse(isCovariantWith(env, stringTable, superTable, scope) - .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); - } + if (auto stringTable = get(*it->second.readTy)) + result.orElse(isCovariantWith(env, stringTable, superTable, scope) + .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); } } } @@ -1891,21 +1844,12 @@ SubtypingResult Subtyping::isCovariantWith( { if (auto it = mttv->props.find("__index"); it != mttv->props.end()) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - // the `string` metatable should not have any write-only types. - LUAU_ASSERT(*it->second.readTy); + // the `string` metatable should not have any write-only types. + LUAU_ASSERT(*it->second.readTy); - if (auto stringTable = get(*it->second.readTy)) - result.orElse(isCovariantWith(env, stringTable, superTable, scope) - .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); - } - else - { - if (auto stringTable = get(it->second.type_DEPRECATED())) - result.orElse(isCovariantWith(env, stringTable, superTable, scope) - .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); - } + if (auto stringTable = get(*it->second.readTy)) + result.orElse(isCovariantWith(env, stringTable, superTable, scope) + .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); } } } @@ -1938,14 +1882,7 @@ SubtypingResult Subtyping::isCovariantWith( SubtypingResult res{true}; if (superProp.isShared() && subProp.isShared()) - { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - res.andAlso(isInvariantWith(env, *subProp.readTy, *superProp.readTy, scope).withBothComponent(TypePath::Property::read(name))); - else - res.andAlso( - isInvariantWith(env, subProp.type_DEPRECATED(), superProp.type_DEPRECATED(), scope).withBothComponent(TypePath::Property::read(name)) - ); - } + res.andAlso(isInvariantWith(env, *subProp.readTy, *superProp.readTy, scope).withBothComponent(TypePath::Property::read(name))); else { if (superProp.readTy.has_value() && subProp.readTy.has_value()) @@ -2252,4 +2189,25 @@ std::pair Subtyping::handleTypeFunctionReductionResult(const T return {builtinTypes->neverType, errors}; } +SubtypingResult Subtyping::trySemanticSubtyping(SubtypingEnvironment& env, + TypeId subTy, + TypeId superTy, + NotNull scope, + SubtypingResult& original) +{ + SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); + + if (semantic.normalizationTooComplex) + { + return semantic; + } + else if (semantic.isSubtype) + { + semantic.reasoning.clear(); + return semantic; + } + + return original; +} + } // namespace Luau diff --git a/Analysis/src/ToDot.cpp b/Analysis/src/ToDot.cpp index 30ce2aec..86f94b9e 100644 --- a/Analysis/src/ToDot.cpp +++ b/Analysis/src/ToDot.cpp @@ -11,7 +11,6 @@ #include LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau { @@ -190,27 +189,22 @@ void StateDot::visitChildren(TypeId ty, int index) for (const auto& [name, prop] : t.props) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (prop.isShared()) + visitChild(*prop.readTy, index, name.c_str()); + else { - if (prop.isShared()) - visitChild(*prop.readTy, index, name.c_str()); - else + if (prop.readTy) { - if (prop.readTy) - { - std::string readName = "read " + name; - visitChild(*prop.readTy, index, readName.c_str()); - } + std::string readName = "read " + name; + visitChild(*prop.readTy, index, readName.c_str()); + } - if (prop.writeTy) - { - std::string writeName = "write " + name; - visitChild(*prop.writeTy, index, writeName.c_str()); - } + if (prop.writeTy) + { + std::string writeName = "write " + name; + visitChild(*prop.writeTy, index, writeName.c_str()); } } - else - visitChild(prop.type_DEPRECATED(), index, name.c_str()); } if (t.indexer) { @@ -330,27 +324,22 @@ void StateDot::visitChildren(TypeId ty, int index) for (const auto& [name, prop] : t.props) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (prop.isShared()) + visitChild(*prop.readTy, index, name.c_str()); + else { - if (prop.isShared()) - visitChild(*prop.readTy, index, name.c_str()); - else + if (prop.readTy) { - if (prop.readTy) - { - std::string readName = "read " + name; - visitChild(*prop.readTy, index, readName.c_str()); - } + std::string readName = "read " + name; + visitChild(*prop.readTy, index, readName.c_str()); + } - if (prop.writeTy) - { - std::string writeName = "write " + name; - visitChild(*prop.writeTy, index, writeName.c_str()); - } + if (prop.writeTy) + { + std::string writeName = "write " + name; + visitChild(*prop.writeTy, index, writeName.c_str()); } } - else - visitChild(prop.type_DEPRECATED(), index, name.c_str()); } if (t.parent) diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index de024749..2f6a58e8 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -21,7 +21,6 @@ LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticStringification) /* @@ -42,7 +41,6 @@ LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticStringification) */ LUAU_FASTINTVARIABLE(DebugLuauVerboseTypeNames, 0) LUAU_FASTFLAGVARIABLE(DebugLuauToStringNoLexicalSort) -LUAU_FASTFLAGVARIABLE(LuauFixEmptyTypePackStringification) namespace Luau { @@ -420,10 +418,7 @@ struct TypeStringifier if (prop.isShared()) { emitKey(name); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - stringify(*prop.readTy); - else - stringify(prop.type_DEPRECATED()); + stringify(*prop.readTy); return; } @@ -493,8 +488,7 @@ struct TypeStringifier bool wrap = !singleTp && get(follow(tp)); - if (FFlag::LuauFixEmptyTypePackStringification) - wrap &= !isEmpty(tp); + wrap &= !isEmpty(tp); if (wrap) state.emit("("); @@ -742,7 +736,7 @@ struct TypeStringifier state.emit("("); - if (FFlag::LuauFixEmptyTypePackStringification && isEmpty(ftv.argTypes)) + if (isEmpty(ftv.argTypes)) { // if we've got an empty argument pack, we're done. } @@ -753,7 +747,7 @@ struct TypeStringifier state.emit(") -> "); - bool plural = FFlag::LuauFixEmptyTypePackStringification ? !isEmpty(ftv.retTypes) : true; + bool plural = !isEmpty(ftv.retTypes); auto retBegin = begin(ftv.retTypes); auto retEnd = end(ftv.retTypes); @@ -1270,14 +1264,11 @@ struct TypePackStringifier return; } - if (FFlag::LuauFixEmptyTypePackStringification) + if (tp.head.empty() && (!tp.tail || isEmpty(*tp.tail))) { - if (tp.head.empty() && (!tp.tail || isEmpty(*tp.tail))) - { - state.emit("()"); - state.unsee(&tp); - return; - } + state.emit("()"); + state.unsee(&tp); + return; } bool first = true; @@ -1827,34 +1818,18 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp state.emit("): "); - 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); + 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(")"); - } - 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(")"); - if (wrap) - state.emit("("); - - tvs.stringify(ftv.retTypes); - - if (wrap) - state.emit(")"); - } return result.name; } diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index bd82ff1b..397132fa 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -30,7 +30,6 @@ LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticVisitType) LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticSetType) @@ -714,9 +713,6 @@ Property Property::create(std::optional read, std::optional writ TypeId Property::type_DEPRECATED() const { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps && !FFlag::LuauUseWorkspacePropToChooseSolver) - LUAU_ASSERT(!FFlag::LuauSolverV2); - LUAU_ASSERT(readTy); return *readTy; } @@ -841,7 +837,7 @@ bool areEqual(SeenSet& seen, const TableType& lhs, const TableType& rhs) if (l->first != r->first) return false; - if (FFlag::LuauSolverV2 && (FFlag::LuauSubtypingCheckFunctionGenericCounts || FFlag::LuauRemoveTypeCallsForReadWriteProps)) + if (FFlag::LuauSolverV2) { if (l->second.readTy && r->second.readTy) { @@ -1090,7 +1086,7 @@ void persist(TypeId ty) for (const auto& [_name, prop] : ttv->props) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2) { if (prop.readTy) queue.push_back(*prop.readTy); @@ -1112,7 +1108,7 @@ void persist(TypeId ty) { for (const auto& [_name, prop] : etv->props) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2) { if (prop.readTy) queue.push_back(*prop.readTy); diff --git a/Analysis/src/TypeAttach.cpp b/Analysis/src/TypeAttach.cpp index 9a31edd5..7e970dca 100644 --- a/Analysis/src/TypeAttach.cpp +++ b/Analysis/src/TypeAttach.cpp @@ -2,7 +2,6 @@ #include "Luau/TypeAttach.h" #include "Luau/Ast.h" -#include "Luau/Error.h" #include "Luau/Module.h" #include "Luau/RecursionCounter.h" #include "Luau/Scope.h" @@ -14,8 +13,6 @@ #include -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) - static char* allocateString(Luau::Allocator& allocator, std::string_view contents) { char* result = (char*)allocator.allocate(contents.size() + 1); @@ -197,43 +194,33 @@ public: char* name = allocateString(*allocator, propName); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (prop.isShared()) { - if (prop.isShared()) - { - props.data[idx].name = AstName(name); - props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); - props.data[idx].access = AstTableAccess::ReadWrite; - props.data[idx].location = Location(); - idx++; - } - else - { - if (prop.readTy) - { - props.data[idx].name = AstName(name); - props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); - props.data[idx].access = AstTableAccess::Read; - props.data[idx].location = Location(); - idx++; - } - - if (prop.writeTy) - { - props.data[idx].name = AstName(name); - props.data[idx].type = Luau::visit(*this, (*prop.writeTy)->ty); - props.data[idx].access = AstTableAccess::Write; - props.data[idx].location = Location(); - idx++; - } - } + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); + props.data[idx].access = AstTableAccess::ReadWrite; + props.data[idx].location = Location(); + idx++; } else { - props.data[idx].name = AstName(name); - props.data[idx].type = Luau::visit(*this, prop.type_DEPRECATED()->ty); - props.data[idx].location = Location(); - idx++; + if (prop.readTy) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); + props.data[idx].access = AstTableAccess::Read; + props.data[idx].location = Location(); + idx++; + } + + if (prop.writeTy) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.writeTy)->ty); + props.data[idx].access = AstTableAccess::Write; + props.data[idx].location = Location(); + idx++; + } } } @@ -272,43 +259,33 @@ public: { char* name = allocateString(*allocator, propName); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (prop.isShared()) { - if (prop.isShared()) - { - props.data[idx].name = AstName(name); - props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); - props.data[idx].access = AstTableAccess::ReadWrite; - props.data[idx].location = Location(); - idx++; - } - else - { - if (prop.readTy) - { - props.data[idx].name = AstName(name); - props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); - props.data[idx].access = AstTableAccess::Read; - props.data[idx].location = Location(); - idx++; - } - - if (prop.writeTy) - { - props.data[idx].name = AstName(name); - props.data[idx].type = Luau::visit(*this, (*prop.writeTy)->ty); - props.data[idx].access = AstTableAccess::Write; - props.data[idx].location = Location(); - idx++; - } - } + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); + props.data[idx].access = AstTableAccess::ReadWrite; + props.data[idx].location = Location(); + idx++; } else { - props.data[idx].name = AstName{name}; - props.data[idx].type = Luau::visit(*this, prop.type_DEPRECATED()->ty); - props.data[idx].location = Location(); - idx++; + if (prop.readTy) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); + props.data[idx].access = AstTableAccess::Read; + props.data[idx].location = Location(); + idx++; + } + + if (prop.writeTy) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.writeTy)->ty); + props.data[idx].access = AstTableAccess::Write; + props.data[idx].location = Location(); + idx++; + } } } diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 49b109ff..7500b891 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -30,16 +30,13 @@ LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) -LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks) -LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck2) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) LUAU_FASTFLAGVARIABLE(LuauSuppressErrorsForMultipleNonviableOverloads) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) -LUAU_FASTFLAG(LuauInferActualIfElseExprType) +LUAU_FASTFLAG(LuauInferActualIfElseExprType2) LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(LuauIceLess) @@ -722,72 +719,69 @@ void TypeChecker2::visit(AstStatReturn* ret) { Scope* scope = findInnermostScope(ret->location); TypePackId expectedRetType = scope->returnType; - if (FFlag::LuauTableLiteralSubtypeSpecificCheck2) + if (ret->list.size == 0) { - if (ret->list.size == 0) - { - testIsSubtype(builtinTypes->emptyTypePack, expectedRetType, ret->location); - return; - } + testIsSubtype(builtinTypes->emptyTypePack, expectedRetType, ret->location); + return; + } - auto [head, _] = extendTypePack(module->internalTypes, builtinTypes, expectedRetType, ret->list.size); - bool isSubtype = true; - std::vector actualHead; - std::optional actualTail; - for (size_t idx = 0; idx < ret->list.size - 1; idx++) + auto [head, _] = extendTypePack(module->internalTypes, builtinTypes, expectedRetType, ret->list.size); + bool isSubtype = true; + std::vector actualHead; + std::optional actualTail; + for (size_t idx = 0; idx < ret->list.size - 1; idx++) + { + if (idx < head.size()) { - if (idx < head.size()) - { - isSubtype &= testPotentialLiteralIsSubtype(ret->list.data[idx], head[idx]); - actualHead.push_back(head[idx]); - } + if (FFlag::LuauInferActualIfElseExprType2) + isSubtype &= testLiteralOrAstTypeIsSubtype(ret->list.data[idx], head[idx]); else - { - actualHead.push_back(lookupType(ret->list.data[idx])); - } - } - - // This stanza is deconstructing what constraint generation does to - // return statements. If we have some statement like: - // - // return E0, E1, E2, ... , EN - // - // All expressions *except* the last will be types, and the last can - // potentially be a pack. However, if the last expression is a function - // call or varargs (`...`), then we _could_ have a pack in the final - // position. Additionally, if we have an argument overflow, then we can't - // do anything interesting with subtyping. - // - // _If_ the last argument is not a function call or varargs and we have - // at least an argument underflow, then we grab the last type out of - // the type pack head and use that to check the subtype of - auto lastExpr = ret->list.data[ret->list.size - 1]; - if (head.size() < ret->list.size || lastExpr->is() || lastExpr->is()) - { - actualTail = lookupPack(lastExpr); + isSubtype &= testPotentialLiteralIsSubtype(ret->list.data[idx], head[idx]); + actualHead.push_back(head[idx]); } else { - auto lastType = head[ret->list.size - 1]; - isSubtype &= testPotentialLiteralIsSubtype(lastExpr, lastType); - actualHead.push_back(lastType); + actualHead.push_back(lookupType(ret->list.data[idx])); } + } - // After all that, we still fire a pack subtype test to determine - // whether we have a well-formed return statement. We only fire - // this if all the previous subtype tests have succeeded, lest - // we double error. - if (isSubtype) - { - auto reconstructedRetType = module->internalTypes.addTypePack(TypePack{std::move(actualHead), std::move(actualTail)}); - testIsSubtype(reconstructedRetType, expectedRetType, ret->location); - } + // This stanza is deconstructing what constraint generation does to + // return statements. If we have some statement like: + // + // return E0, E1, E2, ... , EN + // + // All expressions *except* the last will be types, and the last can + // potentially be a pack. However, if the last expression is a function + // call or varargs (`...`), then we _could_ have a pack in the final + // position. Additionally, if we have an argument overflow, then we can't + // do anything interesting with subtyping. + // + // _If_ the last argument is not a function call or varargs and we have + // at least an argument underflow, then we grab the last type out of + // the type pack head and use that to check the subtype of + auto lastExpr = ret->list.data[ret->list.size - 1]; + if (head.size() < ret->list.size || lastExpr->is() || lastExpr->is()) + { + actualTail = lookupPack(lastExpr); } else { - TypeArena* arena = &module->internalTypes; - TypePackId actualRetType = reconstructPack(ret->list, *arena); - testIsSubtype(actualRetType, expectedRetType, ret->location); + auto lastType = head[ret->list.size - 1]; + if (FFlag::LuauInferActualIfElseExprType2) + isSubtype &= testLiteralOrAstTypeIsSubtype(lastExpr, lastType); + else + isSubtype &= testPotentialLiteralIsSubtype(lastExpr, lastType); + actualHead.push_back(lastType); + } + + // After all that, we still fire a pack subtype test to determine + // whether we have a well-formed return statement. We only fire + // this if all the previous subtype tests have succeeded, lest + // we double error. + if (isSubtype) + { + auto reconstructedRetType = module->internalTypes.addTypePack(TypePack{std::move(actualHead), std::move(actualTail)}); + testIsSubtype(reconstructedRetType, expectedRetType, ret->location); } for (AstExpr* expr : ret->list) @@ -819,12 +813,7 @@ void TypeChecker2::visit(AstStatLocal* local) TypeId annotationType = lookupAnnotation(var->annotation); TypeId valueType = value ? lookupType(value) : nullptr; if (valueType) - { - if (FFlag::LuauTableLiteralSubtypeSpecificCheck2) - testPotentialLiteralIsSubtype(value, annotationType); - else - testIsSubtype(valueType, annotationType, value->location); - } + testPotentialLiteralIsSubtype(value, annotationType); visit(var->annotation); } @@ -1233,33 +1222,19 @@ void TypeChecker2::visit(AstStatAssign* assign) continue; } - if (FFlag::LuauTableLiteralSubtypeSpecificCheck2) + // FIXME CLI-142462: Due to the fact that we do not type state + // tables properly, table types "time travel." We can take + // advantage of this for the specific code pattern of: + // + // local t = {} + // t.foo = {} -- Type of the RHS gets time warped to `{ bar: {} }` + // t.foo.bar = {} + // + if (testLiteralOrAstTypeIsSubtype(rhs, lhsType)) { - // FIXME CLI-142462: Due to the fact that we do not type state - // tables properly, table types "time travel." We can take - // advantage of this for the specific code pattern of: - // - // local t = {} - // t.foo = {} -- Type of the RHS gets time warped to `{ bar: {} }` - // t.foo.bar = {} - // - if (testLiteralOrAstTypeIsSubtype(rhs, lhsType)) - { - if (std::optional bindingType = getBindingType(lhs)) - testLiteralOrAstTypeIsSubtype(rhs, *bindingType); - } - } - else - { - bool ok = testIsSubtype(rhsType, lhsType, rhs->location); - // If rhsType bindingType = getBindingType(lhs); - if (bindingType) - testIsSubtype(rhsType, *bindingType, rhs->location); - } + if (std::optional bindingType = getBindingType(lhs)) + testLiteralOrAstTypeIsSubtype(rhs, *bindingType); } } } @@ -2067,7 +2042,7 @@ void TypeChecker2::visit(AstExprFunction* fn) // If the function type has a function annotation, we need to see if we can suggest an annotation if (normalizedFnTy) { - if (FFlag::LuauStuckTypeFunctionsStillDispatch) + if (FFlag::LuauEagerGeneralization4) suggestAnnotations(fn, normalizedFnTy->functions.parts.front()); else { @@ -2905,43 +2880,19 @@ void TypeChecker2::visit(AstTypePackGeneric* tp) Scope* scope = findInnermostScope(tp->location); LUAU_ASSERT(scope); - if (FFlag::LuauNewNonStrictFixGenericTypePacks) - { - if (std::optional alias = scope->lookupPack(tp->genericName.value)) - return; + if (std::optional alias = scope->lookupPack(tp->genericName.value)) + return; - if (scope->lookupType(tp->genericName.value)) - return reportError( - SwappedGenericTypeParameter{ - tp->genericName.value, - SwappedGenericTypeParameter::Kind::Pack, - }, - tp->location - ); + if (scope->lookupType(tp->genericName.value)) + return reportError( + SwappedGenericTypeParameter{ + tp->genericName.value, + SwappedGenericTypeParameter::Kind::Pack, + }, + tp->location + ); - reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location); - } - else - { - std::optional alias = scope->lookupPack(tp->genericName.value); - if (!alias.has_value()) - { - if (scope->lookupType(tp->genericName.value)) - { - reportError( - SwappedGenericTypeParameter{ - tp->genericName.value, - SwappedGenericTypeParameter::Kind::Pack, - }, - tp->location - ); - } - else - { - reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location); - } - } - } + reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location); } template @@ -3110,7 +3061,7 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT auto exprType = follow(lookupType(expr)); expectedType = follow(expectedType); - if (FFlag::LuauInferActualIfElseExprType) + if (FFlag::LuauInferActualIfElseExprType2) { if (auto group = expr->as()) { @@ -3162,20 +3113,9 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT Set> missingKeys{{}}; for (const auto& [name, prop] : expectedTableType->props) { - if (FFlag::LuauEnableWriteOnlyProperties) + if (prop.readTy) { - if (prop.readTy) - { - if (!isOptional(*prop.readTy)) - missingKeys.insert(name); - } - } - else - { - LUAU_ASSERT(!prop.isWriteOnly()); - - auto readTy = *prop.readTy; - if (!isOptional(readTy)) + if (!isOptional(*prop.readTy)) missingKeys.insert(name); } } @@ -3213,22 +3153,11 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT } else { - 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 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 + if (expectedIt->second.readTy) { - // 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); } diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index e3bf1012..4cabcc74 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -33,14 +33,10 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1); LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies) -LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauOccursCheckForRefinement) -LUAU_FASTFLAGVARIABLE(LuauStuckTypeFunctionsStillDispatch) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAGVARIABLE(LuauEmptyStringInKeyOf) LUAU_FASTFLAGVARIABLE(LuauAvoidExcessiveTypeCopying) @@ -315,7 +311,7 @@ struct TypeFunctionReducer if (auto tfit = get(t)) { - if (FFlag::LuauStuckTypeFunctionsStillDispatch) + if (FFlag::LuauEagerGeneralization4) { if (tfit->state == TypeFunctionInstanceState::Stuck) return SkipTestResult::Stuck; @@ -438,7 +434,7 @@ struct TypeFunctionReducer if (FFlag::DebugLuauLogTypeFamilies) printf("%s is uninhabited\n", toString(subject, {true}).c_str()); - if (FFlag::LuauStuckTypeFunctionsStillDispatch) + if (FFlag::LuauEagerGeneralization4) { if (getState(subject) == TypeFunctionInstanceState::Unsolved) { @@ -500,7 +496,7 @@ struct TypeFunctionReducer if (skip == SkipTestResult::Stuck) { // SkipTestResult::Stuck cannot happen when this flag is unset. - LUAU_ASSERT(FFlag::LuauStuckTypeFunctionsStillDispatch); + LUAU_ASSERT(FFlag::LuauEagerGeneralization4); if (FFlag::DebugLuauLogTypeFamilies) printf("%s is stuck!\n", toString(subject, {true}).c_str()); @@ -779,7 +775,7 @@ FunctionGraphReductionResult reduceTypeFunctions(TypePackId entrypoint, Location bool isPending(TypeId ty, ConstraintSolver* solver) { - if (FFlag::LuauStuckTypeFunctionsStillDispatch) + if (FFlag::LuauEagerGeneralization4) { if (auto tfit = get(ty); tfit && tfit->state == TypeFunctionInstanceState::Unsolved) return true; diff --git a/Analysis/src/TypeFunctionRuntime.cpp b/Analysis/src/TypeFunctionRuntime.cpp index 5a6657e0..2dfd21d5 100644 --- a/Analysis/src/TypeFunctionRuntime.cpp +++ b/Analysis/src/TypeFunctionRuntime.cpp @@ -21,7 +21,6 @@ #include LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit) -LUAU_FASTFLAGVARIABLE(LuauTypeFunOptional) namespace Luau { @@ -455,8 +454,6 @@ static int getSingletonValue(lua_State* L) // Otherwise, makes a union of the two things. static int createOptional(lua_State* L) { - LUAU_ASSERT(FFlag::LuauTypeFunOptional); - int argumentCount = lua_gettop(L); if (argumentCount != 1) luaL_error(L, "types.optional: expected 1 argument, but got %d", argumentCount); @@ -1663,12 +1660,12 @@ void registerTypesLibrary(lua_State* L) {"negationof", createNegation}, {"unionof", createUnion}, {"intersectionof", createIntersection}, + {"optional", createOptional}, {"newtable", createTable}, {"newfunction", createFunction}, {"copy", deepCopy}, {"generic", createGeneric}, - {(FFlag::LuauTypeFunOptional) ? "optional" : nullptr, (FFlag::LuauTypeFunOptional) ? createOptional : nullptr}, {nullptr, nullptr} }; diff --git a/Analysis/src/TypeFunctionRuntimeBuilder.cpp b/Analysis/src/TypeFunctionRuntimeBuilder.cpp index 68f61246..79d9ff35 100644 --- a/Analysis/src/TypeFunctionRuntimeBuilder.cpp +++ b/Analysis/src/TypeFunctionRuntimeBuilder.cpp @@ -20,8 +20,6 @@ // currently, controls serialization, deserialization, and `type.copy` LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000); -LUAU_FASTFLAGVARIABLE(LuauTypeFunctionSerializeFollowMetatable) - namespace Luau { @@ -390,7 +388,7 @@ private: void serializeChildren(const MetatableType* m1, TypeFunctionTableType* m2) { // Serialize main part of the metatable immediately - if (auto tableTy = get(FFlag::LuauTypeFunctionSerializeFollowMetatable ? follow(m1->table) : m1->table)) + if (auto tableTy = get(follow(m1->table))) serializeChildren(tableTy, m2); m2->metatable = shallowSerialize(m1->metatable); diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 6475ffd0..accd037d 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -34,8 +34,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver) -LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure) - namespace Luau { @@ -1912,7 +1910,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp else if (auto a = expr.as()) result = checkExpr(scope, *a); else if (auto a = expr.as()) - result = FFlag::LuauReduceCheckBinaryExprStackPressure ? checkExpr(scope, *a, expectedType) : checkExpr_DEPRECATED(scope, *a, expectedType); + result = checkExpr(scope, *a, expectedType); else if (auto a = expr.as()) result = checkExpr(scope, *a); else if (auto a = expr.as()) @@ -3212,63 +3210,6 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp } } -WithPredicate TypeChecker::checkExpr_DEPRECATED(const ScopePtr& scope, const AstExprBinary& expr, std::optional expectedType) -{ - if (expr.op == AstExprBinary::And) - { - auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType); - - ScopePtr innerScope = childScope(scope, expr.location); - resolve(lhsPredicates, innerScope, true); - - auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType); - - return {checkBinaryOperation(scope, expr, lhsTy, rhsTy), {AndPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}}; - } - else if (expr.op == AstExprBinary::Or) - { - auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType); - - ScopePtr innerScope = childScope(scope, expr.location); - resolve(lhsPredicates, innerScope, false); - - auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType); - - // Because of C++, I'm not sure if lhsPredicates was not moved out by the time we call checkBinaryOperation. - TypeId result = checkBinaryOperation(scope, expr, lhsTy, rhsTy, lhsPredicates); - return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}}; - } - else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe) - { - // For these, passing expectedType is worse than simply forcing them, because their implementation - // may inadvertently check if expectedTypes exist first and use it, instead of forceSingleton first. - WithPredicate lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/true); - WithPredicate rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/true); - if (auto predicate = tryGetTypeGuardPredicate(expr)) - return {booleanType, {std::move(*predicate)}}; - - PredicateVec predicates; - if (auto lvalue = tryGetLValue(*expr.left)) - predicates.push_back(EqPredicate{std::move(*lvalue), rhs.type, expr.location}); - if (auto lvalue = tryGetLValue(*expr.right)) - predicates.push_back(EqPredicate{std::move(*lvalue), lhs.type, expr.location}); - - if (!predicates.empty() && expr.op == AstExprBinary::CompareNe) - predicates = {NotPredicate{std::move(predicates)}}; - - return {checkBinaryOperation(scope, expr, lhs.type, rhs.type), std::move(predicates)}; - } - else - { - // Expected types are not useful for other binary operators. - WithPredicate lhs = checkExpr(scope, *expr.left); - WithPredicate rhs = checkExpr(scope, *expr.right); - - // Intentionally discarding predicates with other operators. - return WithPredicate{checkBinaryOperation(scope, expr, lhs.type, rhs.type, lhs.predicates)}; - } -} - WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr) { TypeId annotationType = resolveType(scope, *expr.annotation); diff --git a/Analysis/src/TypeUtils.cpp b/Analysis/src/TypeUtils.cpp index 1ca0971d..fdba2483 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/Analysis/src/TypeUtils.cpp @@ -13,8 +13,6 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau { @@ -77,7 +75,7 @@ std::optional findTableProperty(NotNull builtinTypes, Er const auto& fit = itt->props.find(name); if (fit != itt->props.end()) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2) { if (fit->second.readTy) return fit->second.readTy; @@ -136,7 +134,7 @@ std::optional findMetatableEntry( auto it = mtt->props.find(entry); if (it != mtt->props.end()) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2) { if (it->second.readTy) return it->second.readTy; @@ -209,7 +207,7 @@ std::optional findTablePropertyRespectingMeta( const auto& fit = itt->props.find(name); if (fit != itt->props.end()) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2) { switch (context) { @@ -469,23 +467,20 @@ TypeId stripNil(NotNull builtinTypes, TypeArena& arena, TypeId ty) ErrorSuppression shouldSuppressErrors(NotNull normalizer, TypeId ty) { - if (FFlag::LuauErrorSuppressionTypeFunctionArgs) + if (auto tfit = get(follow(ty))) { - if (auto tfit = get(follow(ty))) + for (auto ty : tfit->typeArguments) { - for (auto ty : tfit->typeArguments) - { - std::shared_ptr normType = normalizer->normalize(ty); + std::shared_ptr normType = normalizer->normalize(ty); - if (!normType) - return ErrorSuppression::NormalizationFailed; + if (!normType) + return ErrorSuppression::NormalizationFailed; - if (normType->shouldSuppressErrors()) - return ErrorSuppression::Suppress; - } - - return ErrorSuppression::DoNotSuppress; + if (normType->shouldSuppressErrors()) + return ErrorSuppression::Suppress; } + + return ErrorSuppression::DoNotSuppress; } std::shared_ptr normType = normalizer->normalize(ty); diff --git a/Analysis/src/Unifier2.cpp b/Analysis/src/Unifier2.cpp index c5400953..6c6b901a 100644 --- a/Analysis/src/Unifier2.cpp +++ b/Analysis/src/Unifier2.cpp @@ -18,10 +18,7 @@ #include LUAU_FASTINT(LuauTypeInferRecursionLimit) -LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(LuauRefineTablesWithReadType) namespace Luau @@ -52,32 +49,17 @@ static bool areCompatible(TypeId left, TypeId right) // the right table is free (and therefore potentially has an indexer or // a compatible property) - if (FFlag::LuauRemoveTypeCallsForReadWriteProps || FFlag::LuauRefineTablesWithReadType) + if (rightTable->state == TableState::Free || rightTable->indexer.has_value()) + return true; + + if (leftProp.isReadOnly() || leftProp.isShared()) { - if (rightTable->state == TableState::Free || rightTable->indexer.has_value()) + if (isOptionalOrFree(*leftProp.readTy)) return true; - - if (leftProp.isReadOnly() || leftProp.isShared()) - { - if (isOptionalOrFree(*leftProp.readTy)) - return true; - } - - // FIXME: Could this create an issue for write only / divergent properties? - return false; - } - else - { - LUAU_ASSERT(leftProp.isReadOnly() || leftProp.isShared()); - - const TypeId leftType = follow(leftProp.isReadOnly() ? *leftProp.readTy : leftProp.type_DEPRECATED()); - - if (isOptional(leftType) || get(leftType) || rightTable->state == TableState::Free || rightTable->indexer.has_value()) - return true; - - return false; } + // FIXME: Could this create an issue for write only / divergent properties? + return false; }; for (const auto& [name, leftProp] : leftTable->props) @@ -106,7 +88,7 @@ static bool areCompatible(TypeId left, TypeId right) // returns `true` if `ty` is irressolvable and should be added to `incompleteSubtypes`. static bool isIrresolvable(TypeId ty) { - if (FFlag::LuauStuckTypeFunctionsStillDispatch) + if (FFlag::LuauEagerGeneralization4) { if (auto tfit = get(ty); tfit && tfit->state != TypeFunctionInstanceState::Unsolved) return false; @@ -446,28 +428,11 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable) { const Property& superProp = superPropOpt->second; - if (FFlag::LuauEnableWriteOnlyProperties) - { - if (subProp.readTy && superProp.readTy) - result &= unify(*subProp.readTy, *superProp.readTy); + if (subProp.readTy && superProp.readTy) + result &= unify(*subProp.readTy, *superProp.readTy); - if (subProp.writeTy && superProp.writeTy) - result &= unify(*superProp.writeTy, *subProp.writeTy); - } - else - { - if (subProp.isReadOnly() && superProp.isReadOnly()) - result &= unify(*subProp.readTy, *superPropOpt->second.readTy); - else if (subProp.isReadOnly()) - result &= unify(*subProp.readTy, superProp.type_DEPRECATED()); - else if (superProp.isReadOnly()) - result &= unify(subProp.type_DEPRECATED(), *superProp.readTy); - else - { - result &= unify(subProp.type_DEPRECATED(), superProp.type_DEPRECATED()); - result &= unify(superProp.type_DEPRECATED(), subProp.type_DEPRECATED()); - } - } + if (subProp.writeTy && superProp.writeTy) + result &= unify(*superProp.writeTy, *subProp.writeTy); } } diff --git a/Ast/include/Luau/Lexer.h b/Ast/include/Luau/Lexer.h index 3570a35c..fd1c8f32 100644 --- a/Ast/include/Luau/Lexer.h +++ b/Ast/include/Luau/Lexer.h @@ -192,6 +192,14 @@ public: return offset; } + enum class BraceType + { + InterpolatedString, + Normal + }; + + std::optional peekBraceStackTop(); + private: char peekch() const; char peekch(unsigned int lookahead) const; @@ -243,12 +251,6 @@ private: bool skipComments; bool readNames; - enum class BraceType - { - InterpolatedString, - Normal - }; - std::vector braceStack; }; diff --git a/Ast/include/Luau/Parser.h b/Ast/include/Luau/Parser.h index 1c277cf8..b3206e25 100644 --- a/Ast/include/Luau/Parser.h +++ b/Ast/include/Luau/Parser.h @@ -130,7 +130,6 @@ private: // function funcname funcbody LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray& attributes = {nullptr, 0}); - std::pair validateAttribute_DEPRECATED(const char* attributeName, const TempVector& attributes); std::optional validateAttribute(const char* attributeName, const TempVector& attributes); // attribute ::= '@' NAME diff --git a/Ast/src/Lexer.cpp b/Ast/src/Lexer.cpp index c2743640..578bb7b4 100644 --- a/Ast/src/Lexer.cpp +++ b/Ast/src/Lexer.cpp @@ -1009,6 +1009,14 @@ Lexeme Lexer::readNext() } } +std::optional Lexer::peekBraceStackTop() +{ + if (braceStack.empty()) + return std::nullopt; + else + return {braceStack.back()}; +} + LUAU_NOINLINE Lexeme Lexer::readUtf8Error() { Position start = position(); diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 6b3db051..21adf68f 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -18,9 +18,8 @@ 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(LuauParseStringIndexer) -LUAU_FASTFLAGVARIABLE(LuauParseAttributeFixUninit) LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false) +LUAU_FASTFLAGVARIABLE(LuauParseIncompleteInterpStringsWithLocation) // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false; @@ -769,53 +768,8 @@ AstStat* Parser::parseFunctionStat(const AstArray& attributes) return node; } - -std::pair Parser::validateAttribute_DEPRECATED(const char* attributeName, const TempVector& attributes) -{ - LUAU_ASSERT(!FFlag::LuauParseAttributeFixUninit); - - AstAttr::Type type; - - // check if the attribute name is valid - - bool found = false; - - for (int i = 0; kAttributeEntries[i].name; ++i) - { - found = !strcmp(attributeName, kAttributeEntries[i].name); - if (found) - { - type = kAttributeEntries[i].type; - break; - } - } - - if (!found) - { - if (strlen(attributeName) == 1) - report(lexer.current().location, "Attribute name is missing"); - else - report(lexer.current().location, "Invalid attribute '%s'", attributeName); - } - else - { - // check that attribute is not duplicated - for (const AstAttr* attr : attributes) - { - if (attr->type == type) - { - report(lexer.current().location, "Cannot duplicate attribute '%s'", attributeName); - } - } - } - - return {found, type}; -} - std::optional Parser::validateAttribute(const char* attributeName, const TempVector& attributes) { - LUAU_ASSERT(FFlag::LuauParseAttributeFixUninit); - // check if the attribute name is valid std::optional type; @@ -855,26 +809,13 @@ void Parser::parseAttribute(TempVector& attributes) Location loc = lexer.current().location; - if (FFlag::LuauParseAttributeFixUninit) - { - const char* name = lexer.current().name; - std::optional type = validateAttribute(name, attributes); + const char* name = lexer.current().name; + std::optional type = validateAttribute(name, attributes); - nextLexeme(); + nextLexeme(); - if (type) - attributes.push_back(allocator.alloc(loc, *type)); - } - else - { - const char* name = lexer.current().name; - const auto [found, type] = validateAttribute_DEPRECATED(name, attributes); - - nextLexeme(); - - if (found) - attributes.push_back(allocator.alloc(loc, type)); - } + if (type) + attributes.push_back(allocator.alloc(loc, *type)); } // attributes ::= {attribute} @@ -1258,8 +1199,8 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray> chars = parseCharArray(); - - const Location nameEnd = lexer.previousLocation(); - - expectMatchAndConsume(']', begin); - expectAndConsume(':', "property type annotation"); - AstType* type = parseType(); - - // since AstName contains a char*, it can't contain null - bool containsNull = chars && (memchr(chars->data, 0, chars->size) != nullptr); - - if (chars && !containsNull) - { - props.push_back(AstDeclaredExternTypeProperty{ - AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation()) - }); - } - else - { - report(begin.location, "String literal contains malformed escape sequence or \\0"); - } - } - else if (indexer) - { - // 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(AstTableAccess::ReadWrite, std::nullopt, begin).node; - - // we lose all additional indexer expressions from the AST after error recovery here - report(badIndexer->location, "Cannot have more than one indexer on an extern type"); - } - else - { - indexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt, begin).node; - } - } - else - { - Location propStart = lexer.current().location; - std::optional propName = parseNameOpt("property name"); - - if (!propName) - break; - - expectAndConsume(':', "property type annotation"); - AstType* propType = parseType(); - props.push_back(AstDeclaredExternTypeProperty{ - propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation()) - }); - } + props.push_back(parseDeclaredExternTypeMethod(attributes)); } - else + else if (lexer.current().type == '[') { - // There are two possibilities: Either it's a property or a function. - if (lexer.current().type == Lexeme::ReservedFunction) - { - props.push_back(parseDeclaredExternTypeMethod(attributes)); - } - else if (lexer.current().type == '[' && - (lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString)) - { - const Lexeme begin = lexer.current(); - nextLexeme(); // [ + const Lexeme begin = lexer.current(); + nextLexeme(); // [ + if ((lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) && lexer.lookahead().type == ']') + { const Location nameBegin = lexer.current().location; std::optional> chars = parseCharArray(); @@ -1410,37 +1284,34 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArraylocation, "Cannot have more than one indexer on an extern type"); - } - else - { - indexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt, lexer.current()).node; - } + // we lose all additional indexer expressions from the AST after error recovery here + report(badIndexer->location, "Cannot have more than one indexer on an extern type"); } else { - Location propStart = lexer.current().location; - std::optional propName = parseNameOpt("property name"); - - if (!propName) - break; - - expectAndConsume(':', "property type annotation"); - AstType* propType = parseType(); - props.push_back(AstDeclaredExternTypeProperty{ - propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation()) - }); + indexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt, begin).node; } } + else + { + Location propStart = lexer.current().location; + std::optional propName = parseNameOpt("property name"); + + if (!propName) + break; + + expectAndConsume(':', "property type annotation"); + AstType* propType = parseType(); + props.push_back(AstDeclaredExternTypeProperty{ + propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation()) + }); + } } Location classEnd = lexer.current().location; @@ -1982,12 +1853,6 @@ std::pair Parser::extractString // TableIndexer ::= `[' Type `]' `:' Type Parser::TableIndexerResult Parser::parseTableIndexer(AstTableAccess access, std::optional accessLocation, Lexeme begin) { - if (!FFlag::LuauParseStringIndexer) - { - begin = lexer.current(); - nextLexeme(); // [ - } - AstType* index = parseType(); Position indexerClosePosition = lexer.current().location.begin; @@ -2046,121 +1911,13 @@ AstType* Parser::parseTableType(bool inDeclarationContext) } } - if (FFlag::LuauParseStringIndexer) + if (lexer.current().type == '[') { - if (lexer.current().type == '[') + const Lexeme begin = lexer.current(); + nextLexeme(); // [ + + if ((lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) && lexer.lookahead().type == ']') { - const Lexeme begin = lexer.current(); - nextLexeme(); // [ - - if ((lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) && lexer.lookahead().type == ']') - { - CstExprConstantString::QuoteStyle style; - unsigned int blockDepth = 0; - if (options.storeCstData) - std::tie(style, blockDepth) = extractStringDetails(); - - Position stringPosition = lexer.current().location.begin; - AstArray sourceString; - std::optional> chars = parseCharArray(options.storeCstData ? &sourceString : nullptr); - - Position indexerClosePosition = lexer.current().location.begin; - expectMatchAndConsume(']', begin); - Position colonPosition = lexer.current().location.begin; - expectAndConsume(':', "table field"); - - AstType* type = parseType(); - - // since AstName contains a char*, it can't contain null - bool containsNull = chars && (memchr(chars->data, 0, chars->size) != nullptr); - - if (chars && !containsNull) - { - props.push_back(AstTableProp{AstName(chars->data), begin.location, type, access, accessLocation}); - if (options.storeCstData) - cstItems.push_back(CstTypeTable::Item{ - CstTypeTable::Item::Kind::StringProperty, - begin.location.begin, - indexerClosePosition, - colonPosition, - tableSeparator(), - lexer.current().location.begin, - allocator.alloc(sourceString, style, blockDepth), - stringPosition - }); - } - else - report(begin.location, "String literal contains malformed escape sequence or \\0"); - } - else - { - if (indexer) - { - // 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; - - // 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, - }); - } - } - } - else if (props.empty() && !indexer && !(lexer.current().type == Lexeme::Name && lexer.lookahead().type == ':')) - { - AstType* type = parseType(); - - // array-like table type: {T} desugars into {[number]: T} - isArray = true; - AstType* index = allocator.alloc(type->location, std::nullopt, nameNumber, std::nullopt, type->location); - indexer = allocator.alloc(AstTableIndexer{index, type, type->location, access, accessLocation}); - - break; - } - else - { - std::optional name = parseNameOpt("table field"); - - if (!name) - break; - - Position colonPosition = lexer.current().location.begin; - expectAndConsume(':', "table field"); - - AstType* type = parseType(inDeclarationContext); - - props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation}); - if (options.storeCstData) - cstItems.push_back(CstTypeTable::Item{ - CstTypeTable::Item::Kind::Property, - Position{0, 0}, - Position{0, 0}, - colonPosition, - tableSeparator(), - lexer.current().location.begin - }); - } - } - else - { - if (lexer.current().type == '[' && (lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString)) - { - const Lexeme begin = lexer.current(); - nextLexeme(); // [ - CstExprConstantString::QuoteStyle style; unsigned int blockDepth = 0; if (options.storeCstData) @@ -2198,21 +1955,20 @@ AstType* Parser::parseTableType(bool inDeclarationContext) else report(begin.location, "String literal contains malformed escape sequence or \\0"); } - else if (lexer.current().type == '[') + else { if (indexer) { // 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, lexer.current()).node; + AstTableIndexer* badIndexer = parseTableIndexer(access, accessLocation, begin).node; // 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()); + auto tableIndexerResult = parseTableIndexer(access, accessLocation, begin); indexer = tableIndexerResult.node; if (options.storeCstData) cstItems.push_back(CstTypeTable::Item{ @@ -2225,40 +1981,40 @@ AstType* Parser::parseTableType(bool inDeclarationContext) }); } } - else if (props.empty() && !indexer && !(lexer.current().type == Lexeme::Name && lexer.lookahead().type == ':')) - { - AstType* type = parseType(); + } + else if (props.empty() && !indexer && !(lexer.current().type == Lexeme::Name && lexer.lookahead().type == ':')) + { + AstType* type = parseType(); - // array-like table type: {T} desugars into {[number]: T} - isArray = true; - AstType* index = allocator.alloc(type->location, std::nullopt, nameNumber, std::nullopt, type->location); - indexer = allocator.alloc(AstTableIndexer{index, type, type->location, access, accessLocation}); + // array-like table type: {T} desugars into {[number]: T} + isArray = true; + AstType* index = allocator.alloc(type->location, std::nullopt, nameNumber, std::nullopt, type->location); + indexer = allocator.alloc(AstTableIndexer{index, type, type->location, access, accessLocation}); + break; + } + else + { + std::optional name = parseNameOpt("table field"); + + if (!name) break; - } - else - { - std::optional name = parseNameOpt("table field"); - if (!name) - break; + Position colonPosition = lexer.current().location.begin; + expectAndConsume(':', "table field"); - Position colonPosition = lexer.current().location.begin; - expectAndConsume(':', "table field"); + AstType* type = parseType(inDeclarationContext); - AstType* type = parseType(inDeclarationContext); - - props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation}); - if (options.storeCstData) - cstItems.push_back(CstTypeTable::Item{ - CstTypeTable::Item::Kind::Property, - Position{0, 0}, - Position{0, 0}, - colonPosition, - tableSeparator(), - lexer.current().location.begin - }); - } + props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation}); + if (options.storeCstData) + cstItems.push_back(CstTypeTable::Item{ + CstTypeTable::Item::Kind::Property, + Position{0, 0}, + Position{0, 0}, + colonPosition, + tableSeparator(), + lexer.current().location.begin + }); } if (lexer.current().type == ',' || lexer.current().type == ';') @@ -3988,7 +3744,34 @@ AstExpr* Parser::parseInterpString() return reportExprError(endLocation, {}, "Double braces are not permitted within interpolated strings; did you mean '\\{'?"); case Lexeme::BrokenString: nextLexeme(); - return reportExprError(endLocation, {}, "Malformed interpolated string; did you forget to add a '}'?"); + if (!FFlag::LuauParseIncompleteInterpStringsWithLocation) + return reportExprError(endLocation, {}, "Malformed interpolated string; did you forget to add a '}'?"); + LUAU_FALLTHROUGH; + case Lexeme::Eof: + { + if (FFlag::LuauParseIncompleteInterpStringsWithLocation) + { + AstArray> stringsArray = copy(strings); + AstArray exprs = copy(expressions); + AstExprInterpString* node = + allocator.alloc(Location{startLocation, lexer.previousLocation()}, stringsArray, exprs); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(sourceStrings), copy(stringPositions)); + if (auto top = lexer.peekBraceStackTop()) + { + // We are in a broken interpolated string, the top of the stack is non empty, we are missing '}' + if (*top == Lexer::BraceType::InterpolatedString) + report(lexer.previousLocation(), "Malformed interpolated string; did you forget to add a '}'?"); + } + else + { + // We are in a broken interpolated string, the top of the stack is empty, we are missing '`'. + report(lexer.previousLocation(), "Malformed interpolated string; did you forget to add a '`'?"); + } + return node; + } + LUAU_FALLTHROUGH; + } default: return reportExprError(endLocation, {}, "Malformed interpolated string, got %s", lexer.current().toString().c_str()); } diff --git a/CodeGen/src/AssemblyBuilderX64.cpp b/CodeGen/src/AssemblyBuilderX64.cpp index b48627cc..80982fa5 100644 --- a/CodeGen/src/AssemblyBuilderX64.cpp +++ b/CodeGen/src/AssemblyBuilderX64.cpp @@ -6,6 +6,8 @@ #include #include +LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauCodeGenFixRexw, false) + namespace Luau { namespace CodeGen @@ -37,7 +39,10 @@ static_assert(sizeof(cmovTextForCondition) / sizeof(cmovTextForCondition[0]) == #define OP_PLUS_CC(op, cc) ((op) + uint8_t(cc)) #define REX_W_BIT(value) (value ? 0x8 : 0x0) -#define REX_W(reg) REX_W_BIT((reg).size == SizeX64::qword || ((reg).size == SizeX64::byte && (reg).index >= 4)) +// TODO: remove with DFFlagLuauCodeGenFixRexw +#define REX_W_DEPRECATED(reg) REX_W_BIT((reg).size == SizeX64::qword || ((reg).size == SizeX64::byte && (reg).index >= 4)) +#define REX_W(reg) REX_W_BIT((reg).size == SizeX64::qword) +#define REX_FORCE(reg) (((reg).size == SizeX64::byte && (reg).index >= 4) ? 0x40 : 0x00) #define REX_R(reg) (((reg).index & 0x8) >> 1) #define REX_X(reg) (((reg).index & 0x8) >> 2) #define REX_B(reg) (((reg).index & 0x8) >> 3) @@ -1390,25 +1395,52 @@ void AssemblyBuilderX64:: void AssemblyBuilderX64::placeRex(RegisterX64 op) { - uint8_t code = REX_W(op) | REX_B(op); + if (DFFlag::LuauCodeGenFixRexw) + { + uint8_t code = REX_W(op) | REX_B(op) | REX_FORCE(op); - if (code != 0) - place(code | 0x40); + if (code != 0) + place(code | 0x40); + } + else + { + uint8_t code = REX_W_DEPRECATED(op) | REX_B(op); + + if (code != 0) + place(code | 0x40); + } } void AssemblyBuilderX64::placeRex(OperandX64 op) { - uint8_t code = 0; + if (DFFlag::LuauCodeGenFixRexw) + { + uint8_t code = 0; - if (op.cat == CategoryX64::reg) - code = REX_W(op.base) | REX_B(op.base); - else if (op.cat == CategoryX64::mem) - code = REX_W_BIT(op.memSize == SizeX64::qword) | REX_X(op.index) | REX_B(op.base); + if (op.cat == CategoryX64::reg) + code = REX_W(op.base) | REX_B(op.base) | REX_FORCE(op.base); + else if (op.cat == CategoryX64::mem) + code = REX_W_BIT(op.memSize == SizeX64::qword) | REX_X(op.index) | REX_B(op.base); + else + CODEGEN_ASSERT(!"No encoding for left operand of this category"); + + if (code != 0) + place(code | 0x40); + } else - CODEGEN_ASSERT(!"No encoding for left operand of this category"); + { + uint8_t code = 0; - if (code != 0) - place(code | 0x40); + if (op.cat == CategoryX64::reg) + code = REX_W_DEPRECATED(op.base) | REX_B(op.base); + else if (op.cat == CategoryX64::mem) + code = REX_W_BIT(op.memSize == SizeX64::qword) | REX_X(op.index) | REX_B(op.base); + else + CODEGEN_ASSERT(!"No encoding for left operand of this category"); + + if (code != 0) + place(code | 0x40); + } } void AssemblyBuilderX64::placeRexNoW(OperandX64 op) @@ -1428,15 +1460,30 @@ void AssemblyBuilderX64::placeRexNoW(OperandX64 op) void AssemblyBuilderX64::placeRex(RegisterX64 lhs, OperandX64 rhs) { - uint8_t code = REX_W(lhs); + if (DFFlag::LuauCodeGenFixRexw) + { + uint8_t code = REX_W(lhs) | REX_FORCE(lhs); - if (rhs.cat == CategoryX64::imm) - code |= REX_B(lhs); + if (rhs.cat == CategoryX64::imm) + code |= REX_B(lhs); + else + code |= REX_R(lhs) | REX_X(rhs.index) | REX_B(rhs.base) | REX_FORCE(lhs) | REX_FORCE(rhs.base); + + if (code != 0) + place(code | 0x40); + } else - code |= REX_R(lhs) | REX_X(rhs.index) | REX_B(rhs.base); + { + uint8_t code = REX_W_DEPRECATED(lhs); - if (code != 0) - place(code | 0x40); + if (rhs.cat == CategoryX64::imm) + code |= REX_B(lhs); + else + code |= REX_R(lhs) | REX_X(rhs.index) | REX_B(rhs.base); + + if (code != 0) + place(code | 0x40); + } } void AssemblyBuilderX64::placeVex(OperandX64 dst, OperandX64 src1, OperandX64 src2, bool setW, uint8_t mode, uint8_t prefix) diff --git a/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index 8d2b32de..307dbe00 100644 --- a/Compiler/src/Compiler.cpp +++ b/Compiler/src/Compiler.cpp @@ -28,6 +28,7 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo) +LUAU_FASTFLAGVARIABLE(LuauCompileCli162537) namespace Luau { @@ -1928,7 +1929,11 @@ struct Compiler CompileError::raise(ckey->location, "Exceeded constant limit; simplify the code to compile"); LUAU_ASSERT(shape.length < BytecodeBuilder::TableShape::kMaxLength); - shape.keys[shape.length++] = int16_t(cid); + + if (FFlag::LuauCompileCli162537) + shape.keys[shape.length++] = cid; + else + shape.keys[shape.length++] = int16_t(cid); } int32_t tid = bytecode.addConstantTable(shape); diff --git a/Require/Runtime/src/RequireImpl.cpp b/Require/Runtime/src/RequireImpl.cpp index 7106b375..133a54dc 100644 --- a/Require/Runtime/src/RequireImpl.cpp +++ b/Require/Runtime/src/RequireImpl.cpp @@ -106,7 +106,15 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State* static int checkRegisteredModules(lua_State* L, const char* path) { luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1); - lua_getfield(L, -1, path); + + std::string pathLower = std::string(path); + for (char& c : pathLower) + { + if (c >= 'A' && c <= 'Z') + c -= ('A' - 'a'); + } + + lua_getfield(L, -1, pathLower.c_str()); if (lua_isnil(L, -1)) { lua_pop(L, 2); @@ -243,6 +251,16 @@ int registerModuleImpl(lua_State* L) if (pathView.empty() || pathView[0] != '@') luaL_argerrorL(L, 1, "path must begin with '@'"); + // Make path lowercase to ensure case-insensitive matching. + std::string pathLower = std::string(path, len); + for (char& c : pathLower) + { + if (c >= 'A' && c <= 'Z') + c -= ('A' - 'a'); + } + lua_pushstring(L, pathLower.c_str()); + lua_replace(L, 1); + luaL_findtable(L, LUA_REGISTRYINDEX, registeredCacheTableKey, 1); // (1) path, (2) result, (3) cache table diff --git a/Sources.cmake b/Sources.cmake index 990326b5..0c5a956e 100644 --- a/Sources.cmake +++ b/Sources.cmake @@ -220,6 +220,7 @@ target_sources(Luau.Analysis PRIVATE Analysis/include/Luau/Simplify.h Analysis/include/Luau/Substitution.h Analysis/include/Luau/Subtyping.h + Analysis/include/Luau/SubtypingVariance.h Analysis/include/Luau/Symbol.h Analysis/include/Luau/TableLiteralInference.h Analysis/include/Luau/ToDot.h diff --git a/tests/AssemblyBuilderX64.test.cpp b/tests/AssemblyBuilderX64.test.cpp index a727ed54..a3100366 100644 --- a/tests/AssemblyBuilderX64.test.cpp +++ b/tests/AssemblyBuilderX64.test.cpp @@ -7,6 +7,8 @@ #include +LUAU_DYNAMIC_FASTFLAG(LuauCodeGenFixRexw) + using namespace Luau::CodeGen; using namespace Luau::CodeGen::X64; @@ -60,6 +62,8 @@ TEST_SUITE_BEGIN("x64Assembly"); TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseBinaryInstructionForms") { + ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true}; + // reg, reg SINGLE_COMPARE(add(rax, rcx), 0x48, 0x03, 0xc1); SINGLE_COMPARE(add(rsp, r12), 0x49, 0x03, 0xe4); @@ -71,8 +75,8 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseBinaryInstructionForms") SINGLE_COMPARE(add(rax, 0x80), 0x48, 0x81, 0xc0, 0x80, 0x00, 0x00, 0x00); SINGLE_COMPARE(add(r10, 0x7fffffff), 0x49, 0x81, 0xc2, 0xff, 0xff, 0xff, 0x7f); SINGLE_COMPARE(add(al, 3), 0x80, 0xc0, 0x03); - SINGLE_COMPARE(add(sil, 3), 0x48, 0x80, 0xc6, 0x03); - SINGLE_COMPARE(add(r11b, 3), 0x49, 0x80, 0xc3, 0x03); + SINGLE_COMPARE(add(sil, 3), 0x40, 0x80, 0xc6, 0x03); + SINGLE_COMPARE(add(r11b, 3), 0x41, 0x80, 0xc3, 0x03); // reg, [reg] SINGLE_COMPARE(add(rax, qword[rax]), 0x48, 0x03, 0x00); @@ -193,12 +197,14 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseUnaryInstructionForms") TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfMov") { + ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true}; + SINGLE_COMPARE(mov(rcx, 1), 0x48, 0xb9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); SINGLE_COMPARE(mov64(rcx, 0x1234567812345678ll), 0x48, 0xb9, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12); SINGLE_COMPARE(mov(ecx, 2), 0xb9, 0x02, 0x00, 0x00, 0x00); SINGLE_COMPARE(mov(cl, 2), 0xb1, 0x02); - SINGLE_COMPARE(mov(sil, 2), 0x48, 0xb6, 0x02); - SINGLE_COMPARE(mov(r9b, 2), 0x49, 0xb1, 0x02); + SINGLE_COMPARE(mov(sil, 2), 0x40, 0xb6, 0x02); + SINGLE_COMPARE(mov(r9b, 2), 0x41, 0xb1, 0x02); SINGLE_COMPARE(mov(rcx, qword[rdi]), 0x48, 0x8b, 0x0f); SINGLE_COMPARE(mov(dword[rax], 0xabcd), 0xc7, 0x00, 0xcd, 0xab, 0x00, 0x00); SINGLE_COMPARE(mov(r13, 1), 0x49, 0xbd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); @@ -209,8 +215,8 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfMov") SINGLE_COMPARE(mov(qword[rdx], r9), 0x4c, 0x89, 0x0a); SINGLE_COMPARE(mov(byte[rsi], 0x3), 0xc6, 0x06, 0x03); SINGLE_COMPARE(mov(byte[rsi], al), 0x88, 0x06); - SINGLE_COMPARE(mov(byte[rsi], dil), 0x48, 0x88, 0x3e); - SINGLE_COMPARE(mov(byte[rsi], r10b), 0x4c, 0x88, 0x16); + SINGLE_COMPARE(mov(byte[rsi], dil), 0x40, 0x88, 0x3e); + SINGLE_COMPARE(mov(byte[rsi], r10b), 0x44, 0x88, 0x16); SINGLE_COMPARE(mov(wordReg(ebx), 0x3a3d), 0x66, 0xbb, 0x3d, 0x3a); SINGLE_COMPARE(mov(word[rsi], 0x3a3d), 0x66, 0xc7, 0x06, 0x3d, 0x3a); SINGLE_COMPARE(mov(word[rsi], wordReg(eax)), 0x66, 0x89, 0x06); @@ -232,20 +238,28 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfMovExtended") TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfTest") { + ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true}; + SINGLE_COMPARE(test(al, 8), 0xf6, 0xc0, 0x08); SINGLE_COMPARE(test(eax, 8), 0xf7, 0xc0, 0x08, 0x00, 0x00, 0x00); SINGLE_COMPARE(test(rax, 8), 0x48, 0xf7, 0xc0, 0x08, 0x00, 0x00, 0x00); SINGLE_COMPARE(test(rcx, 0xabab), 0x48, 0xf7, 0xc1, 0xab, 0xab, 0x00, 0x00); SINGLE_COMPARE(test(rcx, rax), 0x48, 0x85, 0xc8); SINGLE_COMPARE(test(rax, qword[rcx]), 0x48, 0x85, 0x01); + SINGLE_COMPARE(test(al, cl), 0x84, 0xc1); + SINGLE_COMPARE(test(al, sil), 0x40, 0x84, 0xc6); + SINGLE_COMPARE(test(cl, r12b), 0x41, 0x84, 0xcc); + SINGLE_COMPARE(test(sil, dil), 0x40, 0x84, 0xf7); } TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfShift") { + ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true}; + SINGLE_COMPARE(shl(al, 1), 0xd0, 0xe0); SINGLE_COMPARE(shl(al, cl), 0xd2, 0xe0); - SINGLE_COMPARE(shl(sil, cl), 0x48, 0xd2, 0xe6); - SINGLE_COMPARE(shl(r10b, cl), 0x49, 0xd2, 0xe2); + SINGLE_COMPARE(shl(sil, cl), 0x40, 0xd2, 0xe6); + SINGLE_COMPARE(shl(r10b, cl), 0x41, 0xd2, 0xe2); SINGLE_COMPARE(shr(al, 4), 0xc0, 0xe8, 0x04); SINGLE_COMPARE(shr(eax, 1), 0xd1, 0xe8); SINGLE_COMPARE(sal(eax, cl), 0xd3, 0xe0); @@ -267,8 +281,10 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfLea") TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "FormsOfSetcc") { + ScopedFastFlag luauCodeGenFixRexw{DFFlag::LuauCodeGenFixRexw, true}; + SINGLE_COMPARE(setcc(ConditionX64::NotEqual, bl), 0x0f, 0x95, 0xc3); - SINGLE_COMPARE(setcc(ConditionX64::NotEqual, dil), 0x48, 0x0f, 0x95, 0xc7); + SINGLE_COMPARE(setcc(ConditionX64::NotEqual, dil), 0x40, 0x0f, 0x95, 0xc7); SINGLE_COMPARE(setcc(ConditionX64::BelowEqual, byte[rcx]), 0x0f, 0x96, 0x01); } diff --git a/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index 20a7ee08..7f628abe 100644 --- a/tests/Autocomplete.test.cpp +++ b/tests/Autocomplete.test.cpp @@ -20,7 +20,6 @@ LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2) LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauExpectedTypeVisitor) LUAU_FASTFLAG(LuauImplicitTableIndexerKeys3) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) @@ -4560,8 +4559,6 @@ end TEST_CASE_FIXTURE(ACFixture, "autocomplete_for_assignment") { - ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true}; - check(R"( local function foobar(tbl: { tag: "left" | "right" }) tbl.tag = "@1" @@ -4575,8 +4572,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_for_assignment") TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_local_table") { - ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true}; - check(R"( type Entry = { field: number, prop: string } local x : {Entry} = {} @@ -4603,8 +4598,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_local_table") TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_type_assertion") { - ScopedFastFlag _{FFlag::LuauExpectedTypeVisitor, true}; - check(R"( type Entry = { field: number, prop: string } return ( { f@1, p@2 } :: Entry ) @@ -4621,7 +4614,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr") ScopedFastFlag sffs[] = { // Somewhat surprisingly, the old solver didn't cover this case. {FFlag::LuauSolverV2, true}, - {FFlag::LuauExpectedTypeVisitor, true}, {FFlag::LuauImplicitTableIndexerKeys3, true}, }; @@ -4648,7 +4640,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr_witho { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauExpectedTypeVisitor, true}, {FFlag::LuauImplicitTableIndexerKeys3, true}, }; @@ -4679,10 +4670,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr_witho TEST_CASE_FIXTURE(ACFixture, "bidirectional_autocomplete_in_function_call") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauExpectedTypeVisitor, true}, - }; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; check(R"( local function take(_: { choice: "left" | "right" }) end diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index df8b74b9..411d7133 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -37,10 +37,9 @@ void luau_callhook(lua_State* L, lua_Hook hook, void* userdata); LUAU_FASTFLAG(LuauHeapDumpStringSizeOverhead) LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_DYNAMIC_FASTFLAG(LuauErrorYield) LUAU_DYNAMIC_FASTFLAG(LuauSafeStackCheck) - +LUAU_FASTFLAG(LuauCompileCli162537) static lua_CompileOptions defaultOptions() { @@ -1211,15 +1210,10 @@ static void populateRTTI(lua_State* L, Luau::TypeId type) for (const auto& [name, prop] : t->props) { - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - if (prop.readTy) - populateRTTI(L, *prop.readTy); - else if (prop.writeTy) - populateRTTI(L, *prop.writeTy); - } - else - populateRTTI(L, prop.type_DEPRECATED()); + if (prop.readTy) + populateRTTI(L, *prop.readTy); + else if (prop.writeTy) + populateRTTI(L, *prop.writeTy); lua_setfield(L, -2, name.c_str()); } @@ -3333,6 +3327,60 @@ TEST_CASE("HugeFunctionLoadFailure") REQUIRE_EQ(largeAllocationToFail, expectedTotalLargeAllocations); } +TEST_CASE("HugeConstantTable") +{ + ScopedFastFlag luauCompileCli162537{FFlag::LuauCompileCli162537, true}; + + std::string source = "function foo(...)\n"; + + source += " local args = ...\n"; + source += " local t = args and {\n"; + + for (int i = 0; i < 400; i++) + { + for (int k = 0; k < 100; k++) + { + source += "call("; + source += std::to_string(i * 100 + k); + source += ".125), "; + } + + source += "\n "; + } + + source += " }\n"; + source += " return { a = 1, b = 2, c = 3 }\n"; + source += "end\n"; + source += "return foo().a + foo().b\n"; + + StateRef globalState(luaL_newstate(), lua_close); + lua_State* L = globalState.get(); + + if (codegen && luau_codegen_supported()) + luau_codegen_create(L); + + luaL_openlibs(L); + luaL_sandbox(L); + luaL_sandboxthread(L); + + size_t bytecodeSize = 0; + char* bytecode = luau_compile(source.data(), source.size(), nullptr, &bytecodeSize); + int result = luau_load(L, "=HugeConstantTable", bytecode, bytecodeSize, 0); + free(bytecode); + + REQUIRE(result == 0); + + if (codegen && luau_codegen_supported()) + { + Luau::CodeGen::CompilationOptions nativeOptions{Luau::CodeGen::CodeGen_ColdFunctions}; + Luau::CodeGen::compile(L, -1, nativeOptions); + } + + int status = lua_resume(L, nullptr, 0); + REQUIRE(status == 0); + + CHECK(lua_tonumber(L, -1) == 3); +} TEST_CASE("IrInstructionLimit") { diff --git a/tests/Fixture.cpp b/tests/Fixture.cpp index a0a35bdd..247d2982 100644 --- a/tests/Fixture.cpp +++ b/tests/Fixture.cpp @@ -285,6 +285,7 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars if (FFlag::LuauSolverV2) { Mode mode = sourceModule->mode ? *sourceModule->mode : Mode::Strict; + Frontend::Stats stats; ModulePtr module = Luau::check( *sourceModule, mode, @@ -299,6 +300,7 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars getFrontend().options, {}, false, + stats, {} ); diff --git a/tests/Fixture.h b/tests/Fixture.h index f7dcddf5..13ee7a0e 100644 --- a/tests/Fixture.h +++ b/tests/Fixture.h @@ -29,7 +29,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests) -LUAU_FASTFLAG(LuauTypeFunOptional) LUAU_FASTFLAG(LuauUpdateSetMetatableTypeSignature) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) @@ -203,7 +202,6 @@ struct BuiltinsFixture : Fixture explicit BuiltinsFixture(bool prepareAutocomplete = false); // For the purpose of our tests, we're always the latest version of type functions. - ScopedFastFlag sff_optionalInTypeFunctionLib{FFlag::LuauTypeFunOptional, true}; Frontend& getFrontend() override; }; diff --git a/tests/FragmentAutocomplete.test.cpp b/tests/FragmentAutocomplete.test.cpp index 401f4107..68512cc3 100644 --- a/tests/FragmentAutocomplete.test.cpp +++ b/tests/FragmentAutocomplete.test.cpp @@ -27,17 +27,13 @@ using namespace Luau; LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTFLAG(LuauBetterReverseDependencyTracking) -LUAU_FASTFLAG(LuauBetterScopeSelection) -LUAU_FASTFLAG(LuauBlockDiffFragmentSelection) -LUAU_FASTFLAG(LuauFragmentAcMemoryLeak) -LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation) -LUAU_FASTFLAG(LuauFragmentAutocompleteIfRecommendations) -LUAU_FASTFLAG(LuauPopulateRefinedTypesInFragmentFromOldSolver) LUAU_FASTFLAG(LuauSolverAgnosticStringification) LUAU_FASTFLAG(LuauFragmentRequiresCanBeResolvedToAModule) LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements) LUAU_FASTFLAG(LuauPopulateSelfTypesInFragment) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) +LUAU_FASTFLAG(LuauParseIncompleteInterpStringsWithLocation) +LUAU_FASTFLAG(LuauForInProvidesRecommendations) static std::optional nullCallback(std::string tag, std::optional ptr, std::optional contents) { @@ -67,14 +63,9 @@ struct FragmentAutocompleteFixtureImpl : BaseType { static_assert(std::is_base_of_v, "BaseType must be a descendant of Fixture"); - ScopedFastFlag luauBetterScopeSelection{FFlag::LuauBetterScopeSelection, true}; - ScopedFastFlag luauBlockDiffFragmentSelection{FFlag::LuauBlockDiffFragmentSelection, true}; - ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true}; - ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true}; - ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true}; - ScopedFastFlag luauPopulateRefinedTypesInFragmentFromOldSolver{FFlag::LuauPopulateRefinedTypesInFragmentFromOldSolver, true}; ScopedFastFlag sffLuauFragmentAutocompleteTracksRValueRefinement{FFlag::LuauFragmentAutocompleteTracksRValueRefinements, true}; ScopedFastFlag sffLuauPopulateSelfTypesInFragment{FFlag::LuauPopulateSelfTypesInFragment, true}; + ScopedFastFlag luauParseIncompleteInterpStringsWithLocation{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; FragmentAutocompleteFixtureImpl() : BaseType(true) @@ -703,6 +694,7 @@ end TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "partial_for_numeric_in_condition") { + ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true}; auto region = getAutocompleteRegion( R"( for c = 1,3 @@ -710,7 +702,7 @@ for c = 1,3 Position{1, 11} ); - CHECK_EQ(Location{{1, 0}, {1, 11}}, region.fragmentLocation); + CHECK_EQ(Location{{1, 10}, {1, 11}}, region.fragmentLocation); REQUIRE(region.parentBlock); CHECK(region.nearestStatement->as()); } @@ -4110,6 +4102,198 @@ end ); } +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "string_interpolation_format_provides_autocomplete_results") +{ + const std::string source = R"( +type Foo = {x : number, x1 : string, x2 : boolean} +local e: Foo = {x = 1, x1 = "1", x2 = true} +local s = +)"; + + const std::string dest = R"( +type Foo = {x : number, x1 : string, x2 : boolean} +local e : Foo = {x = 1, x1 = "1", x2 = true} +local s = `{e. }` +)"; + autocompleteFragmentInBothSolvers( + source, + dest, + Position{3, 14}, + [](auto& result) + { + CHECK(!result.result->acResults.entryMap.empty()); + CHECK(result.result->acResults.entryMap.count("x")); + CHECK(result.result->acResults.entryMap.count("x1")); + CHECK(result.result->acResults.entryMap.count("x2")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "string_interpolation_format_provides_results_inside_of_function_call") +{ + ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; + const std::string source = R"( +type T = {x : number, y : number, z : number} +local e = {x = 1, y = 2, z = 3} +print(`{e.x}`) +)"; + const std::string dest = R"( +type T = {x : number, y : number, z : number} +local e = {x = 1, y = 2, z = 3} +print(`{e.x} {e.}`) +)"; + autocompleteFragmentInBothSolvers( + source, + dest, + Position{3, 16}, + [](auto& result) + { + CHECK(!result.result->acResults.entryMap.empty()); + CHECK(result.result->acResults.entryMap.count("x")); + CHECK(result.result->acResults.entryMap.count("y")); + CHECK(result.result->acResults.entryMap.count("z")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_in_should_rec") +{ + ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true}; + const std::string source = R"( +type T = { x : {[number] : number}, y: number} +local x : T = ({} :: T) +for _,n in pairs(x.) do +end +)"; + autocompleteFragmentInBothSolvers( + source, + source, + Position{3, 19}, + [](auto& result) + { + CHECK(!result.result->acResults.entryMap.empty()); + CHECK(result.result->acResults.entryMap.count("x")); + CHECK(result.result->acResults.entryMap.count("y")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_expr_in_should_rec_no_do") +{ + ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true}; + const std::string source = R"( +type T = { x : {[number] : number}, y: number, z: number} +local x : T = ({} :: T) +for i = +end +)"; + const std::string dest = R"( +type T = { x : {[number] : number}, y: number, z : number} +local x : T = ({} :: T) +for i = x. +end +)"; + autocompleteFragmentInBothSolvers( + source, + dest, + Position{3, 10}, + [](auto& result) + { + CHECK(!result.result->acResults.entryMap.empty()); + CHECK(result.result->acResults.entryMap.count("x")); + CHECK(result.result->acResults.entryMap.count("y")); + CHECK(result.result->acResults.entryMap.count("z")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_expr_in_should_rec_with_do_in_step") +{ + ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true}; + const std::string source = R"( +type T = { x : {[number] : number}, y: number, z: number} +local x : T = ({} :: T) +for i = x.y, 100 do +end +)"; + const std::string dest = R"( +type T = { x : {[number] : number}, y: number, z : number} +local x : T = ({} :: T) +for i = x.y, 100, x. do +end +)"; + autocompleteFragmentInBothSolvers( + source, + dest, + Position{3, 20}, + [](auto& result) + { + CHECK(!result.result->acResults.entryMap.empty()); + CHECK(result.result->acResults.entryMap.count("x")); + CHECK(result.result->acResults.entryMap.count("y")); + CHECK(result.result->acResults.entryMap.count("z")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_expr_in_should_rec_with_do_in_max_delete") +{ + ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true}; + const std::string source = R"( +type T = { x : {[number] : number}, y: number, z: number} +local x : T = ({} :: T) +for i = x.y, x.z do +end +)"; + const std::string dest = R"( +type T = { x : {[number] : number}, y: number, z : number} +local x : T = ({} :: T) +for i = x.y, x. do +end +)"; + autocompleteFragmentInBothSolvers( + source, + dest, + Position{3, 15}, + [](auto& result) + { + CHECK(!result.result->acResults.entryMap.empty()); + CHECK(result.result->acResults.entryMap.count("x")); + CHECK(result.result->acResults.entryMap.count("y")); + CHECK(result.result->acResults.entryMap.count("z")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "for_expr_in_should_rec_with_do_in_max_add") +{ + ScopedFastFlag sff{FFlag::LuauForInProvidesRecommendations, true}; + const std::string source = R"( +type T = { x : {[number] : number}, y: number, z: number} +local x : T = ({} :: T) +for i = x.y do +end +)"; + const std::string dest = R"( +type T = { x : {[number] : number}, y: number, z : number} +local x : T = ({} :: T) +for i = x.y, x. do +end +)"; + autocompleteFragmentInBothSolvers( + source, + dest, + Position{3, 15}, + [](auto& result) + { + CHECK(!result.result->acResults.entryMap.empty()); + CHECK(result.result->acResults.entryMap.count("x")); + CHECK(result.result->acResults.entryMap.count("y")); + CHECK(result.result->acResults.entryMap.count("z")); + } + ); +} + // NOLINTEND(bugprone-unchecked-optional-access) TEST_SUITE_END(); diff --git a/tests/Frontend.test.cpp b/tests/Frontend.test.cpp index afa5aa3b..e6ea2bcd 100644 --- a/tests/Frontend.test.cpp +++ b/tests/Frontend.test.cpp @@ -16,7 +16,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) namespace { @@ -877,8 +876,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "discard_type_graphs") TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - Frontend fe{&fileResolver, &configResolver, {false}}; fileResolver.source["Module/A"] = R"( diff --git a/tests/Generalization.test.cpp b/tests/Generalization.test.cpp index 7b3b1f37..77650b6f 100644 --- a/tests/Generalization.test.cpp +++ b/tests/Generalization.test.cpp @@ -16,8 +16,8 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) LUAU_FASTFLAG(DebugLuauForbidInternalTypes) -LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) TEST_SUITE_BEGIN("Generalization"); @@ -227,7 +227,10 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "('a) -> 'a") TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; TableType tt; tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType}; @@ -261,7 +264,10 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: number | string)) -> string?") TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; auto [aTy, aFree] = freshType(); auto [bTy, bFree] = freshType(); @@ -376,10 +382,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_should_not_leak_free_type") TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true}, - }; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; LUAU_REQUIRE_NO_ERRORS(check(R"( local func: (T, (T) -> ()) -> () = nil :: any @@ -398,10 +401,7 @@ TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback") TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback_2") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true}, - }; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; // FIXME: CLI-156389: this is clearly wrong, but also predates this PR. LUAU_REQUIRE_NO_ERRORS(check(R"( @@ -415,7 +415,6 @@ TEST_CASE_FIXTURE(Fixture, "generics_dont_leak_into_callback_2") TEST_CASE_FIXTURE(Fixture, "generic_argument_with_singleton_oss_1808") { - ScopedFastFlag _{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true}; // All we care about here is that this has no errors, and we correctly // infer that the `false` literal should be typed as `false`. LUAU_REQUIRE_NO_ERRORS(check(R"( @@ -428,9 +427,9 @@ TEST_CASE_FIXTURE(Fixture, "generic_argument_with_singleton_oss_1808") TEST_CASE_FIXTURE(BuiltinsFixture, "avoid_cross_module_mutation_in_bidirectional_inference") { - ScopedFastFlag sffs[] = { - {FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true}, + ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} }; fileResolver.source["Module/ListFns"] = R"( diff --git a/tests/InferPolarity.test.cpp b/tests/InferPolarity.test.cpp index e09c2190..944ca934 100644 --- a/tests/InferPolarity.test.cpp +++ b/tests/InferPolarity.test.cpp @@ -10,12 +10,16 @@ using namespace Luau; LUAU_FASTFLAG(LuauEagerGeneralization4); LUAU_FASTFLAG(LuauInferPolarityOfReadWriteProperties) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) TEST_SUITE_BEGIN("InferPolarity"); TEST_CASE_FIXTURE(Fixture, "T where T = { m: (a) -> T }") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; TypeArena arena; ScopePtr globalScope = std::make_shared(getBuiltins()->anyTypePack); @@ -53,6 +57,7 @@ TEST_CASE_FIXTURE(Fixture, "({ read x: a, write x: b }) -> ()") { ScopedFastFlag sffs[] = { {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, {FFlag::LuauInferPolarityOfReadWriteProperties, true}, }; diff --git a/tests/Module.test.cpp b/tests/Module.test.cpp index b94b79b7..4a0c61de 100644 --- a/tests/Module.test.cpp +++ b/tests/Module.test.cpp @@ -14,7 +14,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTINT(LuauTypeCloneIterationLimit) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) TEST_SUITE_BEGIN("ModuleTests"); @@ -138,8 +137,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table") CHECK_EQ(std::optional{"Cyclic"}, ttv->syntheticName); - - TypeId methodType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *ttv->props["get"].readTy : ttv->props["get"].type_DEPRECATED(); + TypeId methodType = *ttv->props["get"].readTy; REQUIRE(methodType != nullptr); const FunctionType* ftv = get(methodType); @@ -172,7 +170,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table_2") TableType* ctt = getMutable(cloneTy); REQUIRE(ctt); - TypeId clonedMethodType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *ctt->props["get"].readTy : ctt->props["get"].type_DEPRECATED(); + TypeId clonedMethodType = *ctt->props["get"].readTy; REQUIRE(clonedMethodType); const FunctionType* cmf = get(clonedMethodType); @@ -201,8 +199,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena") TableType* exportsTable = getMutable(*exports); REQUIRE(exportsTable != nullptr); - TypeId signType = - FFlag::LuauRemoveTypeCallsForReadWriteProps ? *exportsTable->props["sign"].readTy : exportsTable->props["sign"].type_DEPRECATED(); + TypeId signType = *exportsTable->props["sign"].readTy; REQUIRE(signType != nullptr); CHECK(!isInArena(signType, module->interfaceTypes)); @@ -356,17 +353,9 @@ TEST_CASE_FIXTURE(Fixture, "clone_iteration_limit") for (int i = 0; i < nesting; i++) { TableType* ttv = getMutable(nested); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - ttv->props["a"].readTy = src.addType(TableType{}); - ttv->props["a"].writeTy = ttv->props["a"].readTy; - nested = *ttv->props["a"].readTy; - } - else - { - ttv->props["a"].setType(src.addType(TableType{})); - nested = ttv->props["a"].type_DEPRECATED(); - } + ttv->props["a"].readTy = src.addType(TableType{}); + ttv->props["a"].writeTy = ttv->props["a"].readTy; + nested = *ttv->props["a"].readTy; } TypeArena dest; @@ -457,10 +446,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_reexports") TypeId typeB = modBiter->second.type; TableType* tableB = getMutable(typeB); REQUIRE(tableB); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - CHECK(typeA == tableB->props["q"].readTy); - else - CHECK(typeA == tableB->props["q"].type_DEPRECATED()); + CHECK(typeA == tableB->props["q"].readTy); } TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values") @@ -494,13 +480,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values") TableType* tableB = getMutable(*typeB); REQUIRE_MESSAGE(tableB, "Expected a table, but got " << toString(*typeB)); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - CHECK(tableA->props["a"].readTy == tableB->props["b"].readTy); - CHECK(tableA->props["a"].writeTy == tableB->props["b"].writeTy); - } - else - CHECK(tableA->props["a"].type_DEPRECATED() == tableB->props["b"].type_DEPRECATED()); + CHECK(tableA->props["a"].readTy == tableB->props["b"].readTy); + CHECK(tableA->props["a"].writeTy == tableB->props["b"].writeTy); } TEST_CASE_FIXTURE(BuiltinsFixture, "clone_table_bound_to_table_bound_to_table") diff --git a/tests/NonStrictTypeChecker.test.cpp b/tests/NonStrictTypeChecker.test.cpp index 9b2e2a9e..8d44acc7 100644 --- a/tests/NonStrictTypeChecker.test.cpp +++ b/tests/NonStrictTypeChecker.test.cpp @@ -15,10 +15,10 @@ #include "doctest.h" #include -LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks) LUAU_FASTFLAG(LuauNewNonStrictMoreUnknownSymbols) LUAU_FASTFLAG(LuauNewNonStrictNoErrorsPassingNever) LUAU_FASTFLAG(LuauNewNonStrictSuppressesDynamicRequireErrors) +LUAU_FASTFLAG(LuauNewNonStrictReportsOneIndexedErrors) using namespace Luau; @@ -125,6 +125,7 @@ declare os : { } @checked declare function require(target : any) : any +@checked declare function getAllTheArgsWrong(one: string, two: number, three: boolean) : any )BUILTIN_SRC"; }; @@ -629,8 +630,6 @@ optionalArgsAtTheEnd1("a", nil, 3) TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "generic_type_packs_in_non_strict") { - ScopedFastFlag sff{FFlag::LuauNewNonStrictFixGenericTypePacks, true}; - CheckResult result = checkNonStrict(R"( --!nonstrict local test: (T...) -> () -- TypeError: Unknown type 'T' @@ -862,4 +861,22 @@ require("@self/NonExistent") CHECK(req2 != nullptr); } +TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "new_non_strict_stringifies_checked_function_errors_as_one_indexed") +{ + ScopedFastFlag sff = {FFlag::LuauNewNonStrictReportsOneIndexedErrors, true}; + CheckResult result = checkNonStrict(R"( +getAllTheArgsWrong(3, true, "what") +)"); + LUAU_REQUIRE_ERROR_COUNT(3, result); + const CheckedFunctionCallError* err1 = get(result.errors[0]); + const CheckedFunctionCallError* err2 = get(result.errors[1]); + const CheckedFunctionCallError* err3 = get(result.errors[2]); + CHECK(err1 != nullptr); + CHECK(err2 != nullptr); + CHECK(err3 != nullptr); + CHECK_EQ("Function 'getAllTheArgsWrong' expects 'string' at argument #1, but got 'number'", toString(result.errors[0])); + CHECK_EQ("Function 'getAllTheArgsWrong' expects 'number' at argument #2, but got 'boolean'", toString(result.errors[1])); + CHECK_EQ("Function 'getAllTheArgsWrong' expects 'boolean' at argument #3, but got 'string'", toString(result.errors[2])); +} + TEST_SUITE_END(); diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index ee2de442..da307ec2 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -17,8 +17,8 @@ LUAU_FASTINT(LuauRecursionLimit) LUAU_FASTINT(LuauTypeLengthLimit) LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauParseStringIndexer) LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix) +LUAU_FASTFLAG(LuauParseIncompleteInterpStringsWithLocation) // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix extern bool luau_telemetry_parsed_return_type_variadic_with_type_suffix; @@ -951,6 +951,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_mid") TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace") { + ScopedFastFlag sff{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; auto columnOfEndBraceError = [this](const char* code) { try @@ -969,9 +970,10 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace") } }; - // This makes sure that the error is coming from the brace itself - CHECK_EQ(columnOfEndBraceError("_ = `{a`"), columnOfEndBraceError("_ = `{abcdefg`")); - CHECK_NE(columnOfEndBraceError("_ = `{a`"), columnOfEndBraceError("_ = `{a`")); + // This makes sure that the error is coming from the closing brace itself + CHECK_EQ(columnOfEndBraceError("_ = `{a`"), 7); + CHECK_EQ(columnOfEndBraceError("_ = `{abcdefg`"), 13); + CHECK_EQ(columnOfEndBraceError("_ = `{a`"), columnOfEndBraceError("_ = `{abcdefg`")); } TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace_in_table") @@ -4194,8 +4196,136 @@ TEST_CASE_FIXTURE(Fixture, "parsing_type_suffix_for_return_type_with_variadic") TEST_CASE_FIXTURE(Fixture, "parsing_string_union_indexers") { - ScopedFastFlag _{FFlag::LuauParseStringIndexer, true}; parse(R"(type foo = { ["bar" | "baz"]: number })"); } +TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_at_eof") +{ + ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; + auto parseResult = tryParse(R"(print(`{e.x} {e.a)"); + const auto first = parseResult.root->body.data[0]; + auto expr = first->as(); + CHECK(expr != nullptr); + auto call = expr->expr->as(); + CHECK(call != nullptr); + auto interpString = call->args.data[0]->as(); + CHECK(interpString != nullptr); + CHECK(interpString->expressions.size == 2); + CHECK(interpString->location.begin == Position(0, 6)); + CHECK(interpString->location.end == Position(0, 17)); + CHECK_EQ(parseResult.errors.size(), 2); + + auto err = parseResult.errors[0]; + CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?"); + CHECK_EQ(err.getLocation(), Location({0, 16}, {0, 17})); +} + +TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_backtick_at_eof") +{ + ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; + auto parseResult = tryParse(R"(print(`{e.x} {e.a})"); + const auto first = parseResult.root->body.data[0]; + auto expr = first->as(); + CHECK(expr != nullptr); + auto call = expr->expr->as(); + CHECK(call != nullptr); + auto interpString = call->args.data[0]->as(); + CHECK(interpString != nullptr); + CHECK(interpString->expressions.size == 2); + CHECK(interpString->location.begin == Position(0, 6)); + CHECK(interpString->location.end == Position(0, 18)); + CHECK_EQ(parseResult.errors.size(), 2); + + auto err = parseResult.errors[0]; + CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '`'?"); + CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18})); +} + +TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_with_backtick_at_eof") +{ + ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; + auto parseResult = tryParse(R"(print(`{e.x} {e.a`)"); + const auto first = parseResult.root->body.data[0]; + auto expr = first->as(); + CHECK(expr != nullptr); + auto call = expr->expr->as(); + CHECK(call != nullptr); + auto interpString = call->args.data[0]->as(); + CHECK(interpString != nullptr); + CHECK(interpString->expressions.size == 2); + CHECK(interpString->location.begin == Position(0, 6)); + CHECK(interpString->location.end == Position(0, 18)); + CHECK_EQ(parseResult.errors.size(), 2); + + auto err = parseResult.errors[0]; + CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?"); + CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18})); +} + +TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_broken_string") +{ + ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; + auto parseResult = tryParse(R"(print(`{e.x} {e.a +)"); + const auto first = parseResult.root->body.data[0]; + auto expr = first->as(); + CHECK(expr != nullptr); + auto call = expr->expr->as(); + CHECK(call != nullptr); + auto interpString = call->args.data[0]->as(); + CHECK(interpString != nullptr); + CHECK(interpString->expressions.size == 2); + CHECK(interpString->location.begin == Position(0, 6)); + CHECK(interpString->location.end == Position(0, 17)); + CHECK_EQ(parseResult.errors.size(), 2); + + auto err = parseResult.errors[0]; + CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?"); + CHECK_EQ(err.getLocation(), Location({0, 16}, {0, 17})); +} + +TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_backtick_broken_string") +{ + ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; + auto parseResult = tryParse(R"(print(`{e.x} {e.a} +)"); + const auto first = parseResult.root->body.data[0]; + auto expr = first->as(); + CHECK(expr != nullptr); + auto call = expr->expr->as(); + CHECK(call != nullptr); + auto interpString = call->args.data[0]->as(); + CHECK(interpString != nullptr); + CHECK(interpString->expressions.size == 2); + CHECK(interpString->location.begin == Position(0, 6)); + CHECK(interpString->location.end == Position(0, 18)); + CHECK_EQ(parseResult.errors.size(), 2); + + auto err = parseResult.errors[0]; + CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '`'?"); + CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18})); +} + +TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_with_backtick_broken_string") +{ + ScopedFastFlag _{FFlag::LuauParseIncompleteInterpStringsWithLocation, true}; + auto parseResult = tryParse(R"(print(`{e.x} {e.a` +)"); + const auto first = parseResult.root->body.data[0]; + auto expr = first->as(); + CHECK(expr != nullptr); + auto call = expr->expr->as(); + CHECK(call != nullptr); + auto interpString = call->args.data[0]->as(); + CHECK(interpString != nullptr); + CHECK(interpString->expressions.size == 2); + CHECK(interpString->location.begin == Position(0, 6)); + CHECK(interpString->location.end == Position(0, 18)); + CHECK_EQ(parseResult.errors.size(), 2); + + auto err = parseResult.errors[0]; + CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?"); + CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18})); +} + TEST_SUITE_END(); diff --git a/tests/RequireByString.test.cpp b/tests/RequireByString.test.cpp index ad6b9fe2..b4bff43c 100644 --- a/tests/RequireByString.test.cpp +++ b/tests/RequireByString.test.cpp @@ -568,6 +568,20 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RegisterRuntimeModule") assertOutputContainsAll({"true"}); } +TEST_CASE_FIXTURE(ReplWithPathFixture, "RegisterRuntimeModuleCaseInsensitive") +{ + lua_pushcfunction(L, luarequire_registermodule, nullptr); + lua_pushstring(L, "@test/helloworld"); + lua_newtable(L); + lua_pushstring(L, "hello"); + lua_pushstring(L, "world"); + lua_settable(L, -3); + lua_call(L, 2, 0); + + runCode(L, "return require('@TeSt/heLLoWoRld').hello == 'world'"); + assertOutputContainsAll({"true"}); +} + TEST_CASE_FIXTURE(ReplWithPathFixture, "ProxyRequire") { luarequire_pushproxyrequire(L, requireConfigInit, createCliRequireContext(L)); diff --git a/tests/RuntimeLimits.test.cpp b/tests/RuntimeLimits.test.cpp index ed3553ec..05e33a56 100644 --- a/tests/RuntimeLimits.test.cpp +++ b/tests/RuntimeLimits.test.cpp @@ -25,7 +25,9 @@ LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauIceLess) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauSimplifyAnyAndUnion) -LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving) +LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3) +LUAU_FASTFLAG(LuauDontDynamicallyCreateRedundantSubtypeConstraints) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) struct LimitFixture : BuiltinsFixture { @@ -292,7 +294,9 @@ TEST_CASE_FIXTURE(LimitFixture, "Signal_exerpt" * doctest::timeout(0.5)) // These flags are required to surface the problem. {FFlag::LuauSolverV2, true}, {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, {FFlag::LuauPushFunctionTypesInFunctionStatement, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, // And this flag is the one that fixes it. {FFlag::LuauSimplifyAnyAndUnion, true}, @@ -341,7 +345,7 @@ TEST_CASE_FIXTURE(Fixture, "limit_number_of_dynamically_created_constraints") { ScopedFastFlag sff[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauLimitDynamicConstraintSolving, true}, + {FFlag::LuauLimitDynamicConstraintSolving3, true}, }; constexpr const char* src = R"( @@ -369,4 +373,65 @@ TEST_CASE_FIXTURE(Fixture, "limit_number_of_dynamically_created_constraints") } } +TEST_CASE_FIXTURE(BuiltinsFixture, "limit_number_of_dynamically_created_constraints_2") +{ + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauLimitDynamicConstraintSolving3, true}, + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, + {FFlag::LuauPushFunctionTypesInFunctionStatement, true}, + {FFlag::LuauDontDynamicallyCreateRedundantSubtypeConstraints, true}, + }; + + ScopedFastInt sfi{FInt::LuauSolverConstraintLimit, 50}; + + CheckResult result = check(R"( + local T = {} + + export type T = typeof(setmetatable( + {}, + {} :: typeof(T) + )) + + function T.One(): T + return nil :: any + end + + function T.Two(self: T) end + + function T.Three(self: T, x) + self.Prop[x] = true + end + + function T.Four(self: T, x) + print("", x) + end + + function T.Five(self: T) end + + function T.Six(self: T) end + + function T.Seven(self: T) end + + function T.Eight(self: T) end + + function T.Nine(self: T) end + + function T.Ten(self: T) end + + function T.Eleven(self: T) end + + function T.Twelve(self: T) end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + LUAU_REQUIRE_ERROR(result, UnknownProperty); + + // A sanity check to ensure that this statistic is being recorded at all. + CHECK(frontend->stats.dynamicConstraintsCreated > 10); + + CHECK(frontend->stats.dynamicConstraintsCreated < 40); +} + TEST_SUITE_END(); diff --git a/tests/Subtyping.test.cpp b/tests/Subtyping.test.cpp index 7655acef..9f6e2166 100644 --- a/tests/Subtyping.test.cpp +++ b/tests/Subtyping.test.cpp @@ -17,6 +17,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) using namespace Luau; @@ -1654,7 +1655,10 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation") TEST_CASE_FIXTURE(SubtypeFixture, "free_types_might_be_subtypes") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; TypeId argTy = arena.freshType(getBuiltins(), moduleScope.get()); FreeType* freeArg = getMutable(argTy); diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp index 6ddfaf5b..ee2d14bf 100644 --- a/tests/ToString.test.cpp +++ b/tests/ToString.test.cpp @@ -14,8 +14,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauFixEmptyTypePackStringification) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauSolverAgnosticStringification) TEST_SUITE_BEGIN("ToString"); @@ -507,8 +505,6 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_array_uses_array_syntax") TEST_CASE_FIXTURE(Fixture, "the_empty_type_pack_should_be_parenthesized") { - ScopedFastFlag sff{FFlag::LuauFixEmptyTypePackStringification, true}; - TypePackVar emptyTypePack{TypePack{}}; CHECK_EQ(toString(&emptyTypePack), "()"); @@ -889,8 +885,6 @@ TEST_CASE_FIXTURE(Fixture, "tostring_unsee_ttv_if_array") TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( --!strict function f1(t: {a : number, b: string, c: {d: string}}) : {a : number, b : string, c : { d : number}} diff --git a/tests/TypeFunction.test.cpp b/tests/TypeFunction.test.cpp index 020e5f30..e6a3b6c9 100644 --- a/tests/TypeFunction.test.cpp +++ b/tests/TypeFunction.test.cpp @@ -16,10 +16,11 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) -LUAU_FASTFLAG(LuauErrorSuppressionTypeFunctionArgs) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) LUAU_FASTFLAG(LuauEmptyStringInKeyOf) +LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) +LUAU_FASTFLAG(LuauDoNotBlockOnStuckTypeFunctions) +LUAU_FASTFLAG(LuauForceSimplifyConstraint2) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) struct TypeFunctionFixture : Fixture { @@ -351,8 +352,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type MyObject = { x: number, y: number, z: number } type KeysOfMyObject = keyof @@ -374,8 +373,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( local metatable = { __index = {w = 1} } local obj = setmetatable({x = 1, y = 2, z = 3}, metatable) @@ -433,8 +430,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_errors_if_it_has_nontabl TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - if (!FFlag::LuauSolverV2) return; @@ -466,8 +461,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_common_subset_if_union_o if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type MyObject = { x: number, y: number, z: number } type MyOtherObject = { w: number, y: number, z: number } @@ -504,8 +497,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_works") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type MyObject = { x: number, y: number, z: number } type KeysOfMyObject = rawkeyof @@ -527,8 +518,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_ignores_metatables") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( local metatable = { __index = {w = 1} } local obj = setmetatable({x = 1, y = 2, z = 3}, metatable) @@ -570,8 +559,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_common_subset_if_unio if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type MyObject = { x: number, y: number, z: number } type MyOtherObject = { w: number, y: number, z: number } @@ -608,8 +595,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "keyof_type_function_works_on_extern_types" if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type KeysOfMyObject = keyof @@ -744,7 +729,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_oss_crash_gh1161") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag sff[] = {{FFlag::LuauEagerGeneralization4, true}, {FFlag::LuauStuckTypeFunctionsStillDispatch, true}}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, + }; CheckResult result = check(R"( local EnumVariants = { @@ -1008,8 +996,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type MyObject = {a: string, b: number, c: boolean} type IdxAType = index @@ -1182,17 +1168,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_errors_w_var_indexer") type errType1 = index )"); - if (FFlag::LuauErrorSuppressionTypeFunctionArgs) - { - LUAU_REQUIRE_ERROR_COUNT(1, result); - CHECK(toString(result.errors[0]) == "Unknown type 'key'"); - } - else - { - LUAU_REQUIRE_ERROR_COUNT(2, result); - CHECK(toString(result.errors[0]) == "Second argument to index is not a valid index type"); - CHECK(toString(result.errors[1]) == "Unknown type 'key'"); - } + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(toString(result.errors[0]) == "Unknown type 'key'"); } TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_union_type_indexer") @@ -1309,8 +1286,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type MyObject = {a: string, b: number, c: boolean} type RawAType = rawget @@ -1354,17 +1329,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_errors_w_var_indexer") )"); - if (FFlag::LuauErrorSuppressionTypeFunctionArgs) - { - LUAU_REQUIRE_ERROR_COUNT(1, result); - CHECK(toString(result.errors[0]) == "Unknown type 'key'"); - } - else - { - LUAU_REQUIRE_ERROR_COUNT(2, result); - CHECK(toString(result.errors[0]) == "Second argument to rawget is not a valid index type"); - CHECK(toString(result.errors[1]) == "Unknown type 'key'"); - } + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(toString(result.errors[0]) == "Unknown type 'key'"); } TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_union_type_indexer") @@ -1691,8 +1657,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "error_suppression_should_work_on_type_functi if (!FFlag::LuauSolverV2) return; - ScopedFastFlag errorSuppressionTypeFunctionArgs{FFlag::LuauErrorSuppressionTypeFunctionArgs, true}; - CheckResult result = check(R"( local Colours = { Red = 1, @@ -1720,7 +1684,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fully_dispatch_type_function_that_is_paramet ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, - {FFlag::LuauStuckTypeFunctionsStillDispatch, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} }; CheckResult result = check(R"( @@ -1747,7 +1711,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "undefined_add_application") ScopedFastFlag sff[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauEagerGeneralization4, true}, - {FFlag::LuauStuckTypeFunctionsStillDispatch, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; CheckResult result = check(R"( @@ -1806,8 +1770,9 @@ struct TFFixture TypeCheckLimits limits; TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}}; - const ScopedFastFlag sff[1] = { + const ScopedFastFlag sff[2] = { {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} }; BuiltinTypeFunctions builtinTypeFunctions; @@ -1870,7 +1835,10 @@ TEST_CASE_FIXTURE(TFFixture, "a_type_function_parameterized_on_generics_is_solve TEST_CASE_FIXTURE(TFFixture, "a_tf_parameterized_on_a_solved_tf_is_solved") { - ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; TypeId a = arena->addType(GenericType{"A"}); TypeId b = arena->addType(GenericType{"B"}); @@ -1889,7 +1857,10 @@ TEST_CASE_FIXTURE(TFFixture, "a_tf_parameterized_on_a_solved_tf_is_solved") TEST_CASE_FIXTURE(TFFixture, "a_tf_parameterized_on_a_stuck_tf_is_stuck") { - ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; TypeId innerAddTy = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.addFunc, {builtinTypes_.bufferType, builtinTypes_.booleanType}}); @@ -1903,4 +1874,23 @@ TEST_CASE_FIXTURE(TFFixture, "a_tf_parameterized_on_a_stuck_tf_is_stuck") CHECK(tfit->state == TypeFunctionInstanceState::Stuck); } +TEST_CASE_FIXTURE(Fixture, "generic_type_functions_should_not_get_stuck_or") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, + {FFlag::LuauForceSimplifyConstraint2, true}, + {FFlag::LuauDoNotBlockOnStuckTypeFunctions, true}, + }; + + CheckResult result = check(R"( + local function init(data) + return not data or data == '' + end + )"); + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(get(result.errors[0])); +} + TEST_SUITE_END(); diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index 86439b66..626589f3 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -10,9 +10,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) -LUAU_FASTFLAG(LuauTypeFunctionSerializeFollowMetatable) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests"); @@ -352,7 +350,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function serialize_union(arg) @@ -372,7 +369,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function numberhuh() @@ -391,7 +387,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works_on_unions") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function foobar() @@ -413,8 +408,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type function getunion() local ty = types.unionof(types.string, types.number, types.boolean) @@ -442,7 +435,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function serialize_intersection(arg) @@ -464,8 +456,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type function getintersection() local tbl1 = types.newtable(nil, nil, nil) @@ -499,7 +489,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getnegation() @@ -548,7 +537,6 @@ local function notok(idx: fail): never return idx end TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function serialize_table(arg) @@ -568,7 +556,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function gettable() @@ -607,7 +594,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getmetatable() @@ -657,8 +643,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type function getfunction() local ty = types.newfunction(nil, nil) -- () -> () @@ -718,7 +702,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_serialization_works2") TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_methods_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getclass(arg) @@ -740,7 +723,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_methods_works") TEST_CASE_FIXTURE(ExternTypeFixture, "write_of_readonly_is_nil") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getclass(arg) @@ -767,7 +749,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "write_of_readonly_is_nil") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function checkmut() @@ -799,7 +780,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getcopy() @@ -985,7 +965,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_type_cant_call_get_props") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function foo() @@ -1006,7 +985,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_2") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function first(arg) @@ -1059,7 +1037,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_3") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_unordered") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function bar() @@ -1125,8 +1102,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type function optionify(tbl) if not tbl:is("table") then @@ -1179,8 +1154,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type function foo(tbl) local count = 0 @@ -1244,8 +1217,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strip_indexer") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type function stripindexer(tbl) if not tbl:is("table") then @@ -1331,10 +1302,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_eq_field") TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type function test(x) @@ -2403,7 +2371,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "type_alias_reduction_errors") ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, - {FFlag::LuauStuckTypeFunctionsStillDispatch, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; CheckResult result = check(R"( @@ -2456,8 +2424,6 @@ end TEST_CASE_FIXTURE(Fixture, "udtf_metatable_serialization_follows") { - ScopedFastFlag luauTypeFunctionSerializeFollowMetatable{FFlag::LuauTypeFunctionSerializeFollowMetatable, true}; - LUAU_REQUIRE_ERRORS(check(R"( _ = setmetatable(_(),_),(_) or _ == _ or f while _() do diff --git a/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index 81d97dc8..746c9ed3 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/tests/TypeInfer.aliases.test.cpp @@ -10,7 +10,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("TypeAliases"); @@ -205,10 +204,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_aliases") TEST_CASE_FIXTURE(Fixture, "generic_aliases") { - ScopedFastFlag sff[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type T = { v: a } @@ -224,10 +220,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_aliases") TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases") { - ScopedFastFlag sff[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type T = { v: a } diff --git a/tests/TypeInfer.annotations.test.cpp b/tests/TypeInfer.annotations.test.cpp index b8747d04..13fcda76 100644 --- a/tests/TypeInfer.annotations.test.cpp +++ b/tests/TypeInfer.annotations.test.cpp @@ -9,7 +9,6 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) using namespace Luau; @@ -445,11 +444,9 @@ TEST_CASE_FIXTURE(Fixture, "self_referential_type_alias") std::optional incr = get(oTable->props, "incr"); REQUIRE(incr); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - REQUIRE(incr->readTy); + REQUIRE(incr->readTy); - const FunctionType* incrFunc = - FFlag::LuauRemoveTypeCallsForReadWriteProps ? get(*incr->readTy) : get(incr->type_DEPRECATED()); + const FunctionType* incrFunc = get(*incr->readTy); REQUIRE(incrFunc); std::optional firstArg = first(incrFunc->argTypes); @@ -608,14 +605,8 @@ TEST_CASE_FIXTURE(Fixture, "interface_types_belong_to_interface_arena") TableType* exportsTable = getMutable(*exportsType); REQUIRE(exportsTable != nullptr); - TypeId n; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(exportsTable->props["n"].readTy); - n = *exportsTable->props["n"].readTy; - } - else - n = exportsTable->props["n"].type_DEPRECATED(); + REQUIRE(exportsTable->props["n"].readTy); + TypeId n = *exportsTable->props["n"].readTy; REQUIRE(n != nullptr); CHECK(isInArena(n, mod.interfaceTypes)); @@ -670,24 +661,12 @@ TEST_CASE_FIXTURE(Fixture, "cloned_interface_maintains_pointers_between_definiti TableType* exportsTable = getMutable(*exportsType); REQUIRE(exportsTable != nullptr); - TypeId aType; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(exportsTable->props["a"].readTy); - aType = *exportsTable->props["a"].readTy; - } - else - aType = exportsTable->props["a"].type_DEPRECATED(); + REQUIRE(exportsTable->props["a"].readTy); + TypeId aType = *exportsTable->props["a"].readTy; REQUIRE(aType); - TypeId bType; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(exportsTable->props["b"].readTy); - bType = *exportsTable->props["b"].readTy; - } - else - bType = exportsTable->props["b"].type_DEPRECATED(); + REQUIRE(exportsTable->props["b"].readTy); + TypeId bType = *exportsTable->props["b"].readTy; REQUIRE(bType); CHECK(isInArena(recordType, mod.interfaceTypes)); diff --git a/tests/TypeInfer.anyerror.test.cpp b/tests/TypeInfer.anyerror.test.cpp index 4388cdc7..b75d70eb 100644 --- a/tests/TypeInfer.anyerror.test.cpp +++ b/tests/TypeInfer.anyerror.test.cpp @@ -14,7 +14,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) TEST_SUITE_BEGIN("TypeInferAnyError"); @@ -248,13 +247,8 @@ TEST_CASE_FIXTURE(Fixture, "assign_prop_to_table_by_calling_any_yields_any") REQUIRE(ttv); REQUIRE(ttv->props.count("prop")); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(ttv->props["prop"].readTy); - CHECK_EQ("any", toString(*ttv->props["prop"].readTy)); - } - else - REQUIRE_EQ("any", toString(ttv->props["prop"].type_DEPRECATED())); + REQUIRE(ttv->props["prop"].readTy); + CHECK_EQ("any", toString(*ttv->props["prop"].readTy)); } TEST_CASE_FIXTURE(Fixture, "quantify_any_does_not_bind_to_itself") diff --git a/tests/TypeInfer.builtins.test.cpp b/tests/TypeInfer.builtins.test.cpp index e0a6ea57..0cb75ade 100644 --- a/tests/TypeInfer.builtins.test.cpp +++ b/tests/TypeInfer.builtins.test.cpp @@ -12,11 +12,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauTableCloneClonesType3) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) -LUAU_FASTFLAG(LuauStringFormatImprovements) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling) -LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads) @@ -713,19 +709,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_select_should_not_crash") end )"); - if (FFlag::LuauSolverV2 && FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ("Argument count mismatch. Function expects at least 1 argument, but none are specified", toString(result.errors[0])); CHECK_EQ("Argument count mismatch. Function expects at least 1 argument, but none are specified", toString(result.errors[1])); } - else if (FFlag::LuauSolverV2) - { - // Counterintuitively, the parameter l0 is unconstrained and therefore it is valid to pass nil. - // The new solver therefore considers that parameter to be optional. - LUAU_REQUIRE_ERROR_COUNT(1, result); - CHECK("Argument count mismatch. Function expects at least 1 argument, but none are specified" == toString(result.errors[0])); - } else { LUAU_REQUIRE_ERROR_COUNT(2, result); @@ -1317,14 +1306,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_persistent_typelevel_change") REQUIRE(mathTy); TableType* ttv = getMutable(mathTy); REQUIRE(ttv); - const FunctionType* ftv; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(ttv->props["frexp"].readTy); - ftv = get(*ttv->props["frexp"].readTy); - } - else - ftv = get(ttv->props["frexp"].type_DEPRECATED()); + + REQUIRE(ttv->props["frexp"].readTy); + const FunctionType* ftv = get(*ttv->props["frexp"].readTy); + REQUIRE(ftv); auto original = ftv->level; @@ -1679,7 +1664,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_any") TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_any_2") { ScopedFastFlag _{FFlag::LuauSolverV2, true}; - ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true}; CheckResult result = check(R"( local fmt = "Hello, %s!" :: any @@ -1695,7 +1679,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_any_2") TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_singleton_types") { ScopedFastFlag _{FFlag::LuauSolverV2, true}; - ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true}; CheckResult result = check(R"( local fmt: "Hello, %s!" = "Hello, %s!" @@ -1713,7 +1696,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_singleton_types TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_string_is_dynamic") { ScopedFastFlag _{FFlag::LuauSolverV2, true}; - ScopedFastFlag sff{FFlag::LuauStringFormatImprovements, true}; CheckResult result = check(R"( local fmt: string = "Hello, %s!" @@ -1733,7 +1715,6 @@ TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauEnableWriteOnlyProperties, true}, {FFlag::LuauWriteOnlyPropertyMangling, true}, {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, }; diff --git a/tests/TypeInfer.definitions.test.cpp b/tests/TypeInfer.definitions.test.cpp index c87ee7a3..a3975ea7 100644 --- a/tests/TypeInfer.definitions.test.cpp +++ b/tests/TypeInfer.definitions.test.cpp @@ -11,7 +11,6 @@ using namespace Luau; LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) TEST_SUITE_BEGIN("DefinitionTests"); @@ -171,18 +170,19 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function") REQUIRE(!result.success); CHECK_EQ(result.parseResult.errors.size(), 0); REQUIRE(bool(result.module)); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (FFlag::LuauSolverV2) REQUIRE_EQ(result.module->errors.size(), 2); else REQUIRE_EQ(result.module->errors.size(), 1); + GenericError* ge = get(result.module->errors[0]); REQUIRE(ge); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (FFlag::LuauSolverV2) CHECK_EQ("Cannot overload read type of non-function class member 'X'", ge->message); else CHECK_EQ("Cannot overload non-function class member 'X'", ge->message); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + if (FFlag::LuauSolverV2) { GenericError* ge2 = get(result.module->errors[1]); REQUIRE(ge2); @@ -380,14 +380,8 @@ TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_re const auto& method = cls->props["myMethod"]; CHECK_EQ(method.documentationSymbol, "@test/globaltype/MyClass.myMethod"); - FunctionType* function; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(method.readTy); - function = getMutable(*method.readTy); - } - else - function = getMutable(method.type_DEPRECATED()); + REQUIRE(method.readTy); + FunctionType* function = getMutable(*method.readTy); REQUIRE(function); REQUIRE(function->definition.has_value()); diff --git a/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index 132adf6a..7d1bf1be 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/tests/TypeInfer.functions.test.cpp @@ -24,16 +24,12 @@ LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauCollapseShouldNotCrash) -LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauFormatUseLastPosition) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) -LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(LuauSolverAgnosticStringification) LUAU_FASTFLAG(LuauSuppressErrorsForMultipleNonviableOverloads) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) TEST_SUITE_BEGIN("TypeInferFunctions"); @@ -82,8 +78,6 @@ TEST_CASE_FIXTURE(Fixture, "tc_function") TEST_CASE_FIXTURE(Fixture, "check_function_bodies") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( function myFunction(): number local a = 0 @@ -195,15 +189,9 @@ TEST_CASE_FIXTURE(Fixture, "generalize_table_property") const TableType* tt = get(follow(t)); REQUIRE(tt); - TypeId fooTy; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - const Property& foo = tt->props.at("foo"); - REQUIRE(foo.readTy); - fooTy = *foo.readTy; - } - else - fooTy = tt->props.at("foo").type_DEPRECATED(); + const Property& foo = tt->props.at("foo"); + REQUIRE(foo.readTy); + TypeId fooTy = *foo.readTy; CHECK("(a) -> a" == toString(fooTy)); } @@ -249,15 +237,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "vararg_function_is_quantified") REQUIRE(ttv->props.count("f")); - TypeId k; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - const Property& f = ttv->props["f"]; - REQUIRE(f.readTy); - k = *f.readTy; - } - else - k = ttv->props["f"].type_DEPRECATED(); + const Property& f = ttv->props["f"]; + REQUIRE(f.readTy); + TypeId k = *f.readTy; REQUIRE(k); } @@ -1510,8 +1492,6 @@ local a: TableWithFunc = { x = 3, y = 4, f = function(a, b) return a + b end } TEST_CASE_FIXTURE(Fixture, "infer_return_value_type") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( local function f(): {string|number} return {1, "b", 3} @@ -1697,8 +1677,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "function_decl_non_self_sealed_overwrite") TEST_CASE_FIXTURE(BuiltinsFixture, "function_decl_non_self_sealed_overwrite_2") { - ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true}; - CheckResult result = check(R"( local t: { f: ((x: number) -> number)? } = {} @@ -1713,7 +1691,7 @@ t.f = function(x) end )"); - if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization4) { LUAU_CHECK_ERROR_COUNT(2, result); LUAU_CHECK_ERROR(result, WhereClauseNeeded); // x2 @@ -1783,8 +1761,6 @@ TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_th TEST_CASE_FIXTURE(BuiltinsFixture, "function_decl_non_self_unsealed_overwrite") { - ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true}; - CheckResult result = check(R"( local t = { f = nil :: ((x: number) -> number)? } @@ -2061,10 +2037,7 @@ u.b().foo() CHECK_EQ(toString(result.errors[2]), "Argument count mismatch. Function expects 1 to 3 arguments, but none are specified"); CHECK_EQ(toString(result.errors[3]), "Argument count mismatch. Function expects 2 to 4 arguments, but none are specified"); CHECK_EQ(toString(result.errors[4]), "Argument count mismatch. Function expects at least 1 argument, but none are specified"); - if (FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments) - CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 3 arguments, but only 1 is specified"); - else - CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 2 to 3 arguments, but only 1 is specified"); + CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 3 arguments, but only 1 is specified"); CHECK_EQ(toString(result.errors[6]), "Argument count mismatch. Function expects at least 1 argument, but none are specified"); CHECK_EQ(toString(result.errors[7]), "Argument count mismatch. Function expects at least 1 argument, but none are specified"); CHECK_EQ(toString(result.errors[8]), "Argument count mismatch. Function expects at least 1 argument, but none are specified"); @@ -2454,8 +2427,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_packs_are_not_variadic") TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( function num() return 5 @@ -2478,8 +2449,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str") TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_after_num_or_str") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( local function num_or_str() if math.random() > 0.5 then @@ -2623,7 +2592,11 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_return_type") { - ScopedFastFlag _{FFlag::LuauSolverV2, true}; + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, + }; // CLI-114134: This test: // a) Has a kind of weird result (suggesting `number | false` is not great); @@ -2632,12 +2605,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_return_type") // clearly `number`. Hopefully the egraph will be able to unfold this. CheckResult result = check(R"( -function fib(n) - return n < 2 and 1 or fib(n-1) + fib(n-2) -end -)"); + function fib(n) + return n < 2 and 1 or fib(n-1) + fib(n-2) + end + )"); - LUAU_REQUIRE_ERRORS(result); + LUAU_REQUIRE_ERROR_COUNT(1, result); auto err = get(result.errors.back()); LUAU_ASSERT(err); CHECK("false | number" == toString(err->recommendedReturn)); @@ -2647,13 +2620,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_arg_type") { if (!FFlag::LuauSolverV2) return; - CheckResult result = check(R"( -function fib(n, u) - return (n or u) and (n < u and n + fib(n,u)) -end -)"); - LUAU_REQUIRE_ERRORS(result); + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; + + CheckResult result = check(R"( + function fib(n, u) + return (n or u) and (n < u and n + fib(n,u)) + end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); auto err = get(result.errors.back()); LUAU_ASSERT(err); CHECK("number" == toString(err->recommendedReturn)); @@ -2932,10 +2911,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_ast_stat_fun") TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSimplifyOutOfLine2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSimplifyOutOfLine2, true}; CheckResult result = check(R"( function foo(player) @@ -2954,17 +2930,23 @@ 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::LuauEagerGeneralization4) + if (FFlag::LuauTrackFreeInteriorTypePacks) { LUAU_REQUIRE_ERROR_COUNT(2, result); + auto tm1 = get(result.errors[0]); REQUIRE(tm1); CHECK(toString(tm1->wantedType) == "string"); CHECK(toString(tm1->givenType) == "boolean"); + auto tm2 = get(result.errors[1]); REQUIRE(tm2); CHECK(toString(tm2->wantedType) == "string"); - CHECK(toString(tm2->givenType) == "unknown & ~(false?)"); + + if (FFlag::LuauEagerGeneralization4) + CHECK(toString(tm2->givenType) == "unknown & ~(false?)"); + else + CHECK(toString(tm2->givenType) == "~(false?)"); } else { diff --git a/tests/TypeInfer.generics.test.cpp b/tests/TypeInfer.generics.test.cpp index 94398aea..7370022f 100644 --- a/tests/TypeInfer.generics.test.cpp +++ b/tests/TypeInfer.generics.test.cpp @@ -10,15 +10,13 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauIntersectNotNil) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauContainsAnyGenericFollowBeforeChecking) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) using namespace Luau; @@ -908,8 +906,6 @@ end TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( --!strict -- At one point this produced a UAF @@ -1213,15 +1209,9 @@ TEST_CASE_FIXTURE(Fixture, "generic_table_method") REQUIRE(tTable != nullptr); REQUIRE(tTable->props.count("bar")); - TypeId barType; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - Property& bar = tTable->props["bar"]; - REQUIRE(bar.readTy); - barType = *bar.readTy; - } - else - barType = tTable->props["bar"].type_DEPRECATED(); + Property& bar = tTable->props["bar"]; + REQUIRE(bar.readTy); + TypeId barType = *bar.readTy; REQUIRE(barType != nullptr); const FunctionType* ftv = get(follow(barType)); @@ -1263,15 +1253,8 @@ TEST_CASE_FIXTURE(Fixture, "correctly_instantiate_polymorphic_member_functions") std::optional fooProp = get(t->props, "foo"); REQUIRE(bool(fooProp)); - - const FunctionType* foo; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(fooProp->readTy); - foo = get(follow(*fooProp->readTy)); - } - else - foo = get(follow(fooProp->type_DEPRECATED())); + REQUIRE(fooProp->readTy); + const FunctionType* foo = get(follow(*fooProp->readTy)); REQUIRE(bool(foo)); std::optional ret_ = first(foo->retTypes); @@ -1318,14 +1301,8 @@ TEST_CASE_FIXTURE(Fixture, "instantiate_cyclic_generic_function") std::optional methodProp = get(argTable->props, "method"); REQUIRE(bool(methodProp)); - const FunctionType* methodFunction; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(methodProp->readTy); - methodFunction = get(follow(*methodProp->readTy)); - } - else - methodFunction = get(follow(methodProp->type_DEPRECATED())); + REQUIRE(methodProp->readTy); + const FunctionType* methodFunction = get(follow(*methodProp->readTy)); REQUIRE(methodFunction != nullptr); std::optional methodArg = first(methodFunction->argTypes); @@ -1525,12 +1502,12 @@ TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded" // Important FIXME CLI-158432: This test exposes some problems with overload // selection and generic type substitution when -// FFlag::LuauStuckTypeFunctionsStillDispatch is set. TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions") { ScopedFastFlag _[] = { {FFlag::LuauSubtypingCheckFunctionGenericCounts, true}, {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; CheckResult result; @@ -1547,13 +1524,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions") local c = sumrec(function(x, y, f) return f(x, y) end) -- type binders are not inferred )"); - if (FFlag::LuauStuckTypeFunctionsStillDispatch) // FIXME CLI-158432 - CHECK("add | number" == toString(requireType("b"))); - else - CHECK("number" == toString(requireType("b"))); - + CHECK("add | number" == toString(requireType("b"))); // FIXME CLI-161128 CHECK("(a, a, (a, a) -> a) -> a" == toString(requireType("sum"))); CHECK("(a, a, (a, a) -> a) -> a" == toString(requireTypeAtPosition({7, 29}))); + LUAU_REQUIRE_ERROR_COUNT(1, result); // FIXME CLI-161128 + CHECK(get(result.errors[0])); } else { @@ -1567,10 +1542,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions") local b = sumrec(sum) -- ok local c = sumrec(function(x, y, f) return f(x, y) end) -- type binders are not inferred )"); - } - - if (!FFlag::LuauStuckTypeFunctionsStillDispatch) // FIXME CLI-158432 LUAU_REQUIRE_NO_ERRORS(result); + } } @@ -1857,7 +1830,8 @@ TEST_CASE_FIXTURE(Fixture, "generic_type_packs_shouldnt_be_bound_to_themselves") ScopedFastFlag flags[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauSubtypingCheckFunctionGenericCounts, true}, - {FFlag::LuauEagerGeneralization4, true} + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} }; CheckResult result = check(R"( diff --git a/tests/TypeInfer.intersectionTypes.test.cpp b/tests/TypeInfer.intersectionTypes.test.cpp index 4aa367ae..3cc2dd6a 100644 --- a/tests/TypeInfer.intersectionTypes.test.cpp +++ b/tests/TypeInfer.intersectionTypes.test.cpp @@ -10,11 +10,9 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("IntersectionTypes"); @@ -337,10 +335,7 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed") TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect") { - ScopedFastFlag sffs[] = { - {FFlag::LuauPushFunctionTypesInFunctionStatement, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauPushFunctionTypesInFunctionStatement, true}; CheckResult result = check(R"( type X = { x: (number) -> number } @@ -463,8 +458,6 @@ Type 'number' could not be converted into 'X')"; TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_all") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type X = { x: number } type Y = { y: number } diff --git a/tests/TypeInfer.modules.test.cpp b/tests/TypeInfer.modules.test.cpp index a5b5c391..43bd8137 100644 --- a/tests/TypeInfer.modules.test.cpp +++ b/tests/TypeInfer.modules.test.cpp @@ -15,7 +15,8 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving) +LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving3) +LUAU_FASTINT(LuauSolverConstraintLimit) using namespace Luau; @@ -854,7 +855,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "internal_types_are_scrubbed_from_module") ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::DebugLuauMagicTypes, true}, - {FFlag::LuauLimitDynamicConstraintSolving, true} + {FFlag::LuauLimitDynamicConstraintSolving3, true} }; fileResolver.source["game/A"] = R"( @@ -867,4 +868,55 @@ return function(): _luau_blocked_type return nil :: any end CHECK("(...any) -> *error-type*" == toString(getFrontend().moduleResolver.getModule("game/A")->returnType)); } +TEST_CASE_FIXTURE(BuiltinsFixture, "internal_type_errors_are_only_reported_once") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::DebugLuauMagicTypes, true}, + {FFlag::LuauLimitDynamicConstraintSolving3, true} + }; + + fileResolver.source["game/A"] = R"( +return function(): { X: _luau_blocked_type, Y: _luau_blocked_type } return nil :: any end + )"; + + CheckResult result = getFrontend().check("game/A"); + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(get(result.errors[0])); + CHECK("(...any) -> { X: *error-type*, Y: *error-type* }" == toString(getFrontend().moduleResolver.getModule("game/A")->returnType)); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "scrub_unsealed_tables") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauLimitDynamicConstraintSolving3, true} + }; + + ScopedFastInt sfi{FInt::LuauSolverConstraintLimit, 5}; + + fileResolver.source["game/A"] = R"( + type Array = {T} + type Hello = Array>>>>>>>>> + local X = {} + X.foo = 42 + X.bar = "" + return X + )"; + + fileResolver.source["game/B"] = R"( + local x = require(game.A) + x.lmao = 42 + return {} + )"; + + CheckResult result = getFrontend().check("game/B"); + // This is going to have a _ton_ of errors + LUAU_REQUIRE_ERRORS(result); + LUAU_CHECK_ERROR(result, CodeTooComplex); + LUAU_CHECK_ERROR(result, ConstraintSolvingIncompleteError); + LUAU_CHECK_ERROR(result, InternalError); + LUAU_CHECK_ERROR(result, CannotExtendTable); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.negations.test.cpp b/tests/TypeInfer.negations.test.cpp index 2a190aab..0e6ee6ce 100644 --- a/tests/TypeInfer.negations.test.cpp +++ b/tests/TypeInfer.negations.test.cpp @@ -10,6 +10,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) namespace { @@ -52,7 +53,10 @@ TEST_CASE_FIXTURE(NegationFixture, "string_is_not_a_subtype_of_negated_string") TEST_CASE_FIXTURE(Fixture, "cofinite_strings_can_be_compared_for_equality") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; CheckResult result = check(R"( function f(e) diff --git a/tests/TypeInfer.oop.test.cpp b/tests/TypeInfer.oop.test.cpp index 0e199476..3cc84dd0 100644 --- a/tests/TypeInfer.oop.test.cpp +++ b/tests/TypeInfer.oop.test.cpp @@ -14,7 +14,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) TEST_SUITE_BEGIN("TypeInferOOP"); @@ -29,11 +29,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defi someTable.Function1() -- Argument count mismatch )"); - if (!FFlag::LuauSolverV2 || FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments) - { - LUAU_REQUIRE_ERROR_COUNT(1, result); - REQUIRE(get(result.errors[0])); - } + LUAU_REQUIRE_ERROR_COUNT(1, result); + REQUIRE(get(result.errors[0])); } TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2") @@ -47,11 +44,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_ someTable.Function2() -- Argument count mismatch )"); - if (!FFlag::LuauSolverV2 || FFlag::LuauArityMismatchOnUndersaturatedUnknownArguments) - { - LUAU_REQUIRE_ERROR_COUNT(1, result); - REQUIRE(get(result.errors[0])); - } + LUAU_REQUIRE_ERROR_COUNT(1, result); + REQUIRE(get(result.errors[0])); } TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_another_overload_works") @@ -561,6 +555,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "textbook_class_pattern") ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; CheckResult result = check(R"( @@ -593,6 +588,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "textbook_class_pattern_2") ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; CheckResult result = check(R"( diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 80227834..296596d8 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -12,17 +12,13 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable) -LUAU_FASTFLAG(LuauSimplificationTableExternType) -LUAU_FASTFLAG(LuauBetterCannotCallFunctionPrimitive) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) -LUAU_FASTFLAG(LuauNormalizationIntersectTablesPreservesExternTypes) LUAU_FASTFLAG(LuauNormalizationReorderFreeTypeIntersect) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauRefineNoRefineAlways) LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex) LUAU_FASTFLAG(LuauForceSimplifyConstraint2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) using namespace Luau; @@ -661,7 +657,7 @@ TEST_CASE_FIXTURE(Fixture, "free_type_is_equal_to_an_lvalue") { ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, - {FFlag::LuauStuckTypeFunctionsStillDispatch, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, {FFlag::LuauNormalizationReorderFreeTypeIntersect, true}, }; @@ -1633,8 +1629,6 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "refine_param_of_type_folder_or_p TEST_CASE_FIXTURE(RefinementExternTypeFixture, "isa_type_refinement_must_be_known_ahead_of_time") { - ScopedFastFlag sff{FFlag::LuauSimplificationTableExternType, true}; - CheckResult result = check(R"( local function f(x): Instance if x:IsA("Folder") then @@ -1673,19 +1667,10 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "asserting_optional_properties_sh local pos = part1.Position )"); - if (FFlag::LuauSolverV2 && !FFlag::LuauNormalizationIntersectTablesPreservesExternTypes) - { - // CLI-142467: this is a major regression that we need to address. - CHECK_EQ("never", toString(requireTypeAtPosition({3, 15}))); - CHECK_EQ("any", toString(requireTypeAtPosition({6, 29}))); - } - else - { - LUAU_REQUIRE_NO_ERRORS(result); + LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ("WeldConstraint", toString(requireTypeAtPosition({3, 15}))); - CHECK_EQ("Vector3", toString(requireTypeAtPosition({6, 29}))); - } + CHECK_EQ("WeldConstraint", toString(requireTypeAtPosition({3, 15}))); + CHECK_EQ("Vector3", toString(requireTypeAtPosition({6, 29}))); } TEST_CASE_FIXTURE(RefinementExternTypeFixture, "asserting_non_existent_properties_should_not_refine_extern_types_to_never") @@ -1703,20 +1688,11 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "asserting_non_existent_propertie LUAU_REQUIRE_ERRORS(result); CHECK_EQ(toString(result.errors[0]), "Key 'Part8' not found in class 'WeldConstraint'"); - if (FFlag::LuauSolverV2 && !FFlag::LuauNormalizationIntersectTablesPreservesExternTypes) - { - // CLI-142467: this is a major regression that we need to address. - CHECK_EQ("never", toString(requireTypeAtPosition({3, 15}))); + CHECK_EQ("WeldConstraint", toString(requireTypeAtPosition({3, 15}))); + if (FFlag::LuauSolverV2) CHECK_EQ("any", toString(requireTypeAtPosition({6, 29}))); - } else - { - CHECK_EQ("WeldConstraint", toString(requireTypeAtPosition({3, 15}))); - if (FFlag::LuauSolverV2) - CHECK_EQ("any", toString(requireTypeAtPosition({6, 29}))); - else - CHECK_EQ("*error-type*", toString(requireTypeAtPosition({6, 29}))); - } + CHECK_EQ("*error-type*", toString(requireTypeAtPosition({6, 29}))); } TEST_CASE_FIXTURE(RefinementExternTypeFixture, "x_is_not_instance_or_else_not_part") @@ -2252,7 +2228,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_isindexkey_refine_conjunction" ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauEagerGeneralization4, true}, - {FFlag::LuauStuckTypeFunctionsStillDispatch, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, {FFlag::LuauForceSimplifyConstraint2, true}, }; @@ -2265,7 +2241,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_isindexkey_refine_conjunction" end )"); - LUAU_REQUIRE_ERROR_COUNT(3, result); + LUAU_CHECK_ERROR_COUNT(3, result); // For some reason we emit three error here. for (const auto& e : result.errors) @@ -2712,7 +2688,6 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_single") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauBetterCannotCallFunctionPrimitive, true}, {FFlag::LuauRefineTablesWithReadType, true}, }; @@ -2732,7 +2707,6 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_union") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauBetterCannotCallFunctionPrimitive, true}, {FFlag::LuauRefineTablesWithReadType, true}, }; @@ -2906,7 +2880,6 @@ TEST_CASE_FIXTURE(Fixture, "force_simplify_constraint_doesnt_drop_blocked_type") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, {FFlag::LuauForceSimplifyConstraint2, true}, {FFlag::LuauSimplifyOutOfLine2, true}, }; diff --git a/tests/TypeInfer.singletons.test.cpp b/tests/TypeInfer.singletons.test.cpp index d1fa937e..96bbc15e 100644 --- a/tests/TypeInfer.singletons.test.cpp +++ b/tests/TypeInfer.singletons.test.cpp @@ -7,7 +7,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("TypeSingletons"); @@ -376,10 +375,7 @@ TEST_CASE_FIXTURE(Fixture, "indexer_can_be_union_of_singletons") TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( --!strict @@ -396,8 +392,6 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes") TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_string") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type Cat = { tag: 'cat', catfood: string } type Dog = { tag: 'dog', dogfood: string } diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index 7a305834..c1272608 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -24,23 +24,17 @@ LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) -LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) -LUAU_FASTFLAG(LuauDisablePrimitiveInferenceInLargeTables) LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit) -LUAU_FASTFLAG(LuauAutocompleteMissingFollows) -LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(LuauRelateTablesAreNeverDisjoint) -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch) LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) -LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) LUAU_FASTFLAG(LuauRefineTablesWithReadType) LUAU_FASTFLAG(LuauSolverAgnosticStringification) LUAU_FASTFLAG(LuauDfgForwardNilFromAndOr) -LUAU_FASTFLAG(LuauInferActualIfElseExprType) +LUAU_FASTFLAG(LuauInferActualIfElseExprType2) LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(LuauNormalizationLimitTyvarUnionSize) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) TEST_SUITE_BEGIN("TableTests"); @@ -90,33 +84,18 @@ TEST_CASE_FIXTURE(Fixture, "basic") std::optional fooProp = get(tType->props, "foo"); REQUIRE(bool(fooProp)); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(fooProp->readTy); - CHECK_EQ(PrimitiveType::String, getPrimitiveType(*fooProp->readTy)); - } - else - CHECK_EQ(PrimitiveType::String, getPrimitiveType(fooProp->type_DEPRECATED())); + REQUIRE(fooProp->readTy); + CHECK_EQ(PrimitiveType::String, getPrimitiveType(*fooProp->readTy)); std::optional bazProp = get(tType->props, "baz"); REQUIRE(bool(bazProp)); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(bazProp->readTy); - CHECK_EQ(PrimitiveType::Number, getPrimitiveType(*bazProp->readTy)); - } - else - CHECK_EQ(PrimitiveType::Number, getPrimitiveType(bazProp->type_DEPRECATED())); + REQUIRE(bazProp->readTy); + CHECK_EQ(PrimitiveType::Number, getPrimitiveType(*bazProp->readTy)); std::optional quuxProp = get(tType->props, "quux"); REQUIRE(bool(quuxProp)); - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(quuxProp->readTy); - CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(*quuxProp->readTy)); - } - else - CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(quuxProp->type_DEPRECATED())); + REQUIRE(quuxProp->readTy); + CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(*quuxProp->readTy)); } TEST_CASE_FIXTURE(Fixture, "augment_table") @@ -146,15 +125,9 @@ TEST_CASE_FIXTURE(Fixture, "augment_nested_table") REQUIRE(tType->props.find("p") != tType->props.end()); - const TableType* pType; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - Property& p = tType->props["p"]; - REQUIRE(p.readTy); - pType = get(p.readTy); - } - else - pType = get(tType->props["p"].type_DEPRECATED()); + Property& p = tType->props["p"]; + REQUIRE(p.readTy); + const TableType* pType = get(p.readTy); REQUIRE(pType != nullptr); CHECK("{ p: { foo: string } }" == toString(requireType("t"), {true})); @@ -298,14 +271,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function") std::optional fooProp = get(tableType->props, "foo"); REQUIRE(bool(fooProp)); - const FunctionType* methodType; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(fooProp->readTy); - methodType = get(follow(fooProp->readTy)); - } - else - methodType = get(follow(fooProp->type_DEPRECATED())); + REQUIRE(fooProp->readTy); + const FunctionType* methodType = get(follow(fooProp->readTy)); REQUIRE(methodType != nullptr); } @@ -320,14 +287,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2") std::optional uProp = get(tableType->props, "U"); REQUIRE(bool(uProp)); - TypeId uType; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(uProp->readTy); - uType = *uProp->readTy; - } - else - uType = uProp->type_DEPRECATED(); + REQUIRE(uProp->readTy); + TypeId uType = *uProp->readTy; const TableType* uTable = get(uType); REQUIRE(uTable != nullptr); @@ -335,14 +296,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2") std::optional fooProp = get(uTable->props, "foo"); REQUIRE(bool(fooProp)); - const FunctionType* methodType; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - REQUIRE(fooProp->readTy); - methodType = get(follow(fooProp->readTy)); - } - else - methodType = get(follow(fooProp->type_DEPRECATED())); + REQUIRE(fooProp->readTy); + const FunctionType* methodType = get(follow(fooProp->readTy)); REQUIRE(methodType != nullptr); std::vector methodArgs = flatten(methodType->argTypes).first; @@ -958,8 +913,6 @@ TEST_CASE_FIXTURE(Fixture, "array_factory_function") TEST_CASE_FIXTURE(Fixture, "sealed_table_indexers_must_unify") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( function f(a: {number}): {string} return a @@ -1112,16 +1065,9 @@ TEST_CASE_FIXTURE(Fixture, "assigning_to_an_unsealed_table_with_string_literal_s REQUIRE(tableType->indexer == std::nullopt); REQUIRE(0 != tableType->props.count("a")); - - TypeId propertyA; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - Property& a = tableType->props["a"]; - REQUIRE(a.readTy); - propertyA = *a.readTy; - } - else - propertyA = tableType->props["a"].type_DEPRECATED(); + Property& a = tableType->props["a"]; + REQUIRE(a.readTy); + TypeId propertyA = *a.readTy; REQUIRE(propertyA != nullptr); CHECK_EQ(*getBuiltins()->stringType, *propertyA); } @@ -1750,8 +1696,6 @@ TEST_CASE_FIXTURE(Fixture, "right_table_missing_key2") TEST_CASE_FIXTURE(Fixture, "casting_unsealed_tables_with_props_into_table_with_indexer") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type StringToStringMap = { [string]: string } local rt: StringToStringMap = { ["foo"] = 1 } @@ -1854,8 +1798,6 @@ TEST_CASE_FIXTURE(Fixture, "casting_tables_with_props_into_table_with_indexer4") TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multiple_errors") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( function f(vec1: {x: number}): {x: number, y: number, z: number} return vec1 @@ -1888,8 +1830,6 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multi TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multiple_errors2") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type MixedTable = {[number]: number, x: number} local t: MixedTable = {"fail"} @@ -2098,11 +2038,7 @@ TEST_CASE_FIXTURE(Fixture, "key_setting_inference_given_nil_upper_bound") TEST_CASE_FIXTURE(Fixture, "explicit_nil_indexer") { - - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; auto result = check(R"( local function _(t: { [string]: number? }): number @@ -2357,12 +2293,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "quantifying_a_bound_var_works") REQUIRE_MESSAGE(ttv, "Expected a table but got " << toString(ty, {true})); REQUIRE(ttv->props.count("new")); Property& prop = ttv->props["new"]; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - REQUIRE(prop.readTy); - else - REQUIRE(prop.type_DEPRECATED()); - const FunctionType* ftv = - (FFlag::LuauRemoveTypeCallsForReadWriteProps) ? get(follow(*prop.readTy)) : get(follow(prop.type_DEPRECATED())); + REQUIRE(prop.readTy); + const FunctionType* ftv = get(follow(*prop.readTy)); REQUIRE(ftv); const TypePack* res = get(follow(ftv->retTypes)); REQUIRE(res); @@ -2427,7 +2359,6 @@ local t: { a: {Foo}, b: number } = { // since mutating properties means table properties should be invariant. TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_tables_in_assignment_is_unsound") { - ScopedFastFlag sff{FFlag::LuauStuckTypeFunctionsStillDispatch, true}; CheckResult result = check(R"( --!strict @@ -3220,15 +3151,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_quantify_table_that_belongs_to_outer_sc REQUIRE(counterType); REQUIRE(counterType->props.count("new")); - const FunctionType* newType; - if (FFlag::LuauRemoveTypeCallsForReadWriteProps) - { - Property& newProp = counterType->props["new"]; - REQUIRE(newProp.readTy); - newType = get(follow(*newProp.readTy)); - } - else - newType = get(follow(counterType->props["new"].type_DEPRECATED())); + Property& newProp = counterType->props["new"]; + REQUIRE(newProp.readTy); + const FunctionType* newType = get(follow(*newProp.readTy)); REQUIRE(newType); std::optional newRetType = *first(newType->retTypes); @@ -3713,7 +3638,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_leak_free_table_props") TEST_CASE_FIXTURE(Fixture, "mixed_tables_with_implicit_numbered_keys") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( local t: { [string]: number } = { 5, 6, 7 } )"); @@ -3820,7 +3744,10 @@ 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 _{FFlag::LuauEagerGeneralization4, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; CheckResult result = check(R"( local function f(s) @@ -4346,10 +4273,7 @@ TEST_CASE_FIXTURE(Fixture, "cyclic_shifted_tables") TEST_CASE_FIXTURE(Fixture, "cli_84607_missing_prop_in_array_or_dict") { - ScopedFastFlag sffs[] = { - {FFlag::LuauFixIndexerSubtypingOrdering, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauFixIndexerSubtypingOrdering, true}; CheckResult result = check(R"( type Thing = { name: string, prop: boolean } @@ -4417,10 +4341,7 @@ TEST_CASE_FIXTURE(Fixture, "simple_method_definition") TEST_CASE_FIXTURE(Fixture, "identify_all_problematic_table_fields") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type T = { @@ -4514,7 +4435,6 @@ TEST_CASE_FIXTURE(Fixture, "infer_write_property") 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} @@ -4585,7 +4505,6 @@ TEST_CASE_FIXTURE(Fixture, "write_to_read_only_property") 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}) @@ -4599,7 +4518,6 @@ TEST_CASE_FIXTURE(Fixture, "write_to_write_only_property") 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}) @@ -4615,7 +4533,6 @@ TEST_CASE_FIXTURE(Fixture, "bidirectional_typechecking_with_write_only_property" 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}) @@ -4659,15 +4576,7 @@ TEST_CASE_FIXTURE(Fixture, "write_annotations_are_supported_with_the_new_solver" end )"); - 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); - } + LUAU_REQUIRE_NO_ERRORS(result); } TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported") @@ -4716,6 +4625,7 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties") ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; CheckResult result = check(R"( @@ -5248,10 +5158,7 @@ TEST_CASE_FIXTURE(Fixture, "function_check_constraint_too_eager") TEST_CASE_FIXTURE(BuiltinsFixture, "magic_functions_bidirectionally_inferred") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local function getStuff(): (string, number, string) @@ -5355,13 +5262,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "write_only_table_field_duplicate") } )"); - 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])); - } + LUAU_REQUIRE_NO_ERRORS(result); } TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_musnt_assert") @@ -5444,10 +5345,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_optional_in_table") TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; auto result = check(R"( local Numbers = { str = ( "" :: string ) } @@ -5466,7 +5364,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table") TEST_CASE_FIXTURE(Fixture, "optional_function_in_table") { - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; LUAU_CHECK_NO_ERRORS(check(R"( local t: { (() -> ())? } = { @@ -5520,10 +5418,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1543_optional_generic_param") TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; auto result = check(R"( type Book = { title: string, author: string } @@ -5550,10 +5445,7 @@ TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference") TEST_CASE_FIXTURE(Fixture, "generic_index_syntax_bidirectional_infer_with_tables") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; auto result = check((R"( local function getStatus(): string @@ -5615,7 +5507,7 @@ TEST_CASE_FIXTURE(Fixture, "deeply_nested_classish_inference") TEST_CASE_FIXTURE(Fixture, "bigger_nested_table_causes_big_type_error") { - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; auto result = check(R"( type File = { @@ -5724,10 +5616,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_match_literal_type_crash_again") TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( --!strict @@ -5744,10 +5633,8 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict") TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + CheckResult result = check(R"( --!strict local dict: { code1: boolean } = { @@ -5763,10 +5650,8 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check") TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_regression") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + CheckResult result = check(R"( --!strict local d1: { code1: boolean } = { @@ -5783,10 +5668,7 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_regression") TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( --!strict @@ -5805,13 +5687,8 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment") TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, - }; - - ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2}; CheckResult result = check(R"( type Word = "foo" | "bar" @@ -5824,13 +5701,8 @@ TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables") TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_nested_tables") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, - }; - - ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2}; CheckResult result = check(R"( type Word = "foo" | "bar" @@ -5841,13 +5713,8 @@ TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_nested_tables") TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, - }; - - ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + ScopedFastInt sfi{FInt::LuauPrimitiveInferenceInTableLimit, 2}; CheckResult result = check(R"( type Word = "foo" | "bar" @@ -5856,7 +5723,7 @@ TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed") )"); 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 that all the errors are localized to `words`, not `otherWords` CHECK(err.location.begin.line == 2); } @@ -5865,10 +5732,7 @@ TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed") TEST_CASE_FIXTURE(Fixture, "extremely_large_table" * doctest::timeout(2.0)) { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, - }; + ScopedFastFlag _{FFlag::LuauSolverV2, true}; const std::string source = "local res = {\n" + rep("\"foo\",\n", 100'000) + "}"; LUAU_REQUIRE_NO_ERRORS(check(source)); @@ -5888,10 +5752,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1838") TEST_CASE_FIXTURE(Fixture, "oss_1859") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( --!strict @@ -6049,9 +5910,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1450") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true}, {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} }; CheckResult results = check(R"( @@ -6114,10 +5975,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1888_and_or_subscriptable") TEST_CASE_FIXTURE(Fixture, "cli_119126_regression") { - ScopedFastFlag sffs[] = { - {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, - }; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult results = check(R"( type literals = "foo" | "bar" | "foobar" diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index f3be42bd..004a9ad1 100644 --- a/tests/TypeInfer.test.cpp +++ b/tests/TypeInfer.test.cpp @@ -28,19 +28,16 @@ LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) -LUAU_FASTFLAG(LuauSkipLvalueForCompoundAssignment) -LUAU_FASTFLAG(LuauMissingFollowInAssignIndexConstraint) LUAU_FASTFLAG(LuauOccursCheckForRefinement) LUAU_FASTFLAG(LuauInferPolarityOfReadWriteProperties) -LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) -LUAU_FASTFLAG(LuauInferActualIfElseExprType) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) +LUAU_FASTFLAG(LuauInferActualIfElseExprType2) LUAU_FASTFLAG(LuauForceSimplifyConstraint2) LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement) LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete) LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2) LUAU_FASTFLAG(LuauMissingFollowMappedGenericPacks) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) using namespace Luau; @@ -1218,7 +1215,7 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer") if (FFlag::LuauSolverV2) { - CHECK(4 == result.errors.size()); + REQUIRE(4 == result.errors.size()); CHECK(Location{{2, 22}, {2, 42}} == result.errors[0].location); CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location); CHECK(Location{{3, 45}, {3, 46}} == result.errors[2].location); @@ -2021,6 +2018,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert") ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; auto result = check(R"( @@ -2056,6 +2054,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2") ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; CheckResult result = check(R"( @@ -2089,6 +2088,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion") ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; LUAU_REQUIRE_ERRORS(check(R"( @@ -2258,8 +2258,6 @@ end TEST_CASE_FIXTURE(Fixture, "self_bound_due_to_compound_assign") { - ScopedFastFlag _{FFlag::LuauSkipLvalueForCompoundAssignment, true}; - loadDefinition(R"( declare class Camera CameraType: string @@ -2368,7 +2366,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "is_safe_integer_example") TEST_CASE_FIXTURE(BuiltinsFixture, "type_remover_heap_use_after_free") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; + ScopedFastFlag sff[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} + }; LUAU_REQUIRE_ERRORS(check(R"( _ = if l0.n0.n0 then {n4(...,setmetatable(setmetatable(_),_)),_ == _,} elseif _.ceil._ then _ elseif _ then not _ @@ -2388,8 +2389,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_remover_heap_use_after_free") TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_assign_index_constraint") { - ScopedFastFlag _{FFlag::LuauMissingFollowInAssignIndexConstraint, true}; - LUAU_REQUIRE_ERRORS(check(R"( _._G = nil for _ in ... do @@ -2421,7 +2420,6 @@ TEST_CASE_FIXTURE(Fixture, "fuzzer_infer_divergent_rw_props") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauEnableWriteOnlyProperties, true}, {FFlag::LuauInferPolarityOfReadWriteProperties, true}, }; @@ -2446,11 +2444,10 @@ TEST_CASE_FIXTURE(Fixture, "oss_1815_verbatim") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauInferActualIfElseExprType, true}, + {FFlag::LuauInferActualIfElseExprType2, true}, // This is needed so that we don't hide the string literal free types // behind a `union<_, _>` {FFlag::LuauSimplifyOutOfLine2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult results = check(R"( @@ -2482,8 +2479,7 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_bidirectional_inference") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauInferActualIfElseExprType, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, + {FFlag::LuauInferActualIfElseExprType2, true}, }; CheckResult results = check(R"( @@ -2504,8 +2500,7 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_two_errors") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauInferActualIfElseExprType, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, + {FFlag::LuauInferActualIfElseExprType2, true}, }; CheckResult results = check(R"( diff --git a/tests/TypeInfer.typePacks.test.cpp b/tests/TypeInfer.typePacks.test.cpp index 330976e0..a3cd50db 100644 --- a/tests/TypeInfer.typePacks.test.cpp +++ b/tests/TypeInfer.typePacks.test.cpp @@ -13,8 +13,6 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) -LUAU_FASTFLAG(LuauFixEmptyTypePackStringification) TEST_SUITE_BEGIN("TypePackTests"); @@ -338,10 +336,7 @@ local c: Packed REQUIRE(ttvA->instantiatedTypeParams.size() == 1); REQUIRE(ttvA->instantiatedTypePackParams.size() == 1); CHECK_EQ(toString(ttvA->instantiatedTypeParams[0], {true}), "number"); - if (FFlag::LuauFixEmptyTypePackStringification) - CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), "()"); - else - CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), ""); + CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), "()"); auto ttvB = get(requireType("b")); REQUIRE(ttvB); @@ -1095,8 +1090,6 @@ TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments") TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments_free") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( function foo(...: T...): T... return ... diff --git a/tests/TypeInfer.typestates.test.cpp b/tests/TypeInfer.typestates.test.cpp index dc486c22..4ca8d641 100644 --- a/tests/TypeInfer.typestates.test.cpp +++ b/tests/TypeInfer.typestates.test.cpp @@ -5,7 +5,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) using namespace Luau; @@ -63,8 +63,6 @@ TEST_CASE_FIXTURE(TypeStateFixture, "assign_different_values_to_x") TEST_CASE_FIXTURE(TypeStateFixture, "parameter_x_was_constrained_by_two_types") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - // Parameter `x` has a fresh type `'x` bounded by `never` and `unknown`. // The first use of `x` constrains `x`'s upper bound by `string | number`. // The second use of `x`, aliased by `y`, constrains `x`'s upper bound by `string?`. @@ -404,6 +402,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_futur ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, }; CheckResult result = check(R"( diff --git a/tests/TypeInfer.unionTypes.test.cpp b/tests/TypeInfer.unionTypes.test.cpp index 3f90356b..c1de47a7 100644 --- a/tests/TypeInfer.unionTypes.test.cpp +++ b/tests/TypeInfer.unionTypes.test.cpp @@ -11,7 +11,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("UnionTypes"); @@ -583,8 +582,6 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_union_all") TEST_CASE_FIXTURE(Fixture, "error_detailed_optional") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; - CheckResult result = check(R"( type X = { x: number } diff --git a/tests/TypeInfer.unknownnever.test.cpp b/tests/TypeInfer.unknownnever.test.cpp index 19d6a388..4ef2db6e 100644 --- a/tests/TypeInfer.unknownnever.test.cpp +++ b/tests/TypeInfer.unknownnever.test.cpp @@ -8,8 +8,8 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauEagerGeneralization4); -LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch); LUAU_FASTFLAG(LuauForceSimplifyConstraint2) +LUAU_FASTFLAG(LuauTrackFreeInteriorTypePacks) TEST_SUITE_BEGIN("TypeInferUnknownNever"); @@ -332,7 +332,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_unify_operands_if_one_of_the_operand_is_never_i { ScopedFastFlag sffs[] = { {FFlag::LuauEagerGeneralization4, true}, - {FFlag::LuauStuckTypeFunctionsStillDispatch, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true}, {FFlag::LuauForceSimplifyConstraint2, true}, }; @@ -360,7 +360,7 @@ TEST_CASE_FIXTURE(Fixture, "math_operators_and_never") { ScopedFastFlag sff[] = { {FFlag::LuauEagerGeneralization4, true}, - {FFlag::LuauStuckTypeFunctionsStillDispatch, true}, + {FFlag::LuauTrackFreeInteriorTypePacks, true} }; CheckResult result = check(R"( diff --git a/tools/natvis/CodeGen.natvis b/tools/natvis/CodeGen.natvis index 2c34b486..24d1ec6c 100644 --- a/tools/natvis/CodeGen.natvis +++ b/tools/natvis/CodeGen.natvis @@ -9,6 +9,11 @@ cl dl bl + spl + bpl + sil + dil + e{(int)index,d}b eax ecx