From 62919400429622116472ecc81eddcd8a0afca264 Mon Sep 17 00:00:00 2001 From: NixAJ Date: Tue, 17 Sep 2024 19:26:58 +0200 Subject: [PATCH] Updated Luau to version 0.643 --- Dependencies/luau/Luau/.gitignore | 1 + .../include/Luau/BuiltinDefinitions.h | 3 +- .../Luau/Analysis/include/Luau/Frontend.h | 4 + .../luau/Luau/Analysis/include/Luau/Set.h | 2 +- .../Luau/Analysis/include/Luau/Subtyping.h | 144 +- .../luau/Luau/Analysis/include/Luau/Type.h | 22 +- .../Luau/Analysis/include/Luau/TypeChecker2.h | 194 + .../Luau/Analysis/include/Luau/VisitType.h | 10 +- .../luau/Luau/Analysis/src/AnyTypeSummary.cpp | 19 +- .../luau/Luau/Analysis/src/AstQuery.cpp | 9 +- .../luau/Luau/Analysis/src/Autocomplete.cpp | 14 +- .../Luau/Analysis/src/BuiltinDefinitions.cpp | 67 +- Dependencies/luau/Luau/Analysis/src/Clone.cpp | 4 +- .../Luau/Analysis/src/ConstraintGenerator.cpp | 2 +- .../luau/Luau/Analysis/src/DataFlowGraph.cpp | 4 +- .../luau/Luau/Analysis/src/Frontend.cpp | 24 +- .../luau/Luau/Analysis/src/Generalization.cpp | 16 +- .../luau/Luau/Analysis/src/Instantiation.cpp | 4 +- .../luau/Luau/Analysis/src/Linter.cpp | 4 +- .../luau/Luau/Analysis/src/Module.cpp | 7 +- .../Analysis/src/NonStrictTypeChecker.cpp | 14 +- .../luau/Luau/Analysis/src/Normalize.cpp | 32 +- .../Luau/Analysis/src/OverloadResolution.cpp | 15 +- Dependencies/luau/Luau/Analysis/src/Scope.cpp | 6 +- .../luau/Luau/Analysis/src/Simplify.cpp | 13 +- .../luau/Luau/Analysis/src/Substitution.cpp | 6 +- .../luau/Luau/Analysis/src/Subtyping.cpp | 374 +- .../luau/Luau/Analysis/src/Symbol.cpp | 4 +- Dependencies/luau/Luau/Analysis/src/ToDot.cpp | 4 +- .../luau/Luau/Analysis/src/ToString.cpp | 26 +- Dependencies/luau/Luau/Analysis/src/Type.cpp | 2 +- .../luau/Luau/Analysis/src/TypeChecker2.cpp | 4611 ++++++++--------- .../luau/Luau/Analysis/src/TypeFunction.cpp | 102 +- .../luau/Luau/Analysis/src/TypeInfer.cpp | 230 +- .../luau/Luau/Analysis/src/TypePack.cpp | 2 +- .../luau/Luau/Analysis/src/TypePath.cpp | 14 +- .../luau/Luau/Analysis/src/TypeUtils.cpp | 10 +- .../luau/Luau/Analysis/src/Unifier.cpp | 6 +- Dependencies/luau/Luau/Ast/src/Parser.cpp | 3 +- Dependencies/luau/Luau/CLI/Require.cpp | 13 +- Dependencies/luau/Luau/CMakeLists.txt | 1 + .../Luau/CodeGen/include/Luau/IrVisitUseDef.h | 47 +- .../Luau/CodeGen/src/BytecodeAnalysis.cpp | 4 - .../luau/Luau/CodeGen/src/CodeGenAssembly.cpp | 5 +- .../luau/Luau/CodeGen/src/EmitBuiltinsX64.cpp | 36 - .../luau/Luau/CodeGen/src/IrBuilder.cpp | 4 - .../luau/Luau/CodeGen/src/IrLoweringA64.cpp | 116 +- .../luau/Luau/CodeGen/src/IrLoweringX64.cpp | 18 +- .../Luau/CodeGen/src/IrTranslateBuiltins.cpp | 88 +- .../luau/Luau/CodeGen/src/IrTranslation.cpp | 21 +- .../CodeGen/src/IrValueLocationTracking.cpp | 6 +- .../Luau/CodeGen/src/OptimizeConstProp.cpp | 12 +- .../luau/Luau/Common/include/Luau/Bytecode.h | 2 +- .../luau/Luau/Common/include/Luau/Common.h | 13 + .../Common/include/Luau/ExperimentalFlags.h | 3 +- .../Luau/Compiler/src/BytecodeBuilder.cpp | 112 +- .../luau/Luau/Compiler/src/Compiler.cpp | 58 +- Dependencies/luau/Luau/Compiler/src/Types.cpp | 11 +- .../luau/Luau/EqSat/include/Luau/EGraph.h | 5 +- .../luau/Luau/EqSat/include/Luau/Language.h | 7 - .../Luau/EqSat/include/Luau/LanguageHash.h | 1 + .../luau/Luau/EqSat/src/UnionFind.cpp | 3 + Dependencies/luau/Luau/Makefile | 3 +- Dependencies/luau/Luau/VM/src/lstrlib.cpp | 9 +- Dependencies/luau/Luau/VM/src/ltable.cpp | 2 +- Dependencies/luau/Luau/fuzz/proto.cpp | 4 +- .../luau/Luau/tests/AnyTypeSummary.test.cpp | 507 +- .../luau/Luau/tests/AstQuery.test.cpp | 6 +- .../luau/Luau/tests/Autocomplete.test.cpp | 36 +- .../luau/Luau/tests/Compiler.test.cpp | 9 - .../luau/Luau/tests/Conformance.test.cpp | 18 + .../Luau/tests/ConstraintGeneratorFixture.cpp | 4 +- .../luau/Luau/tests/ConstraintSolver.test.cpp | 2 +- .../luau/Luau/tests/DataFlowGraph.test.cpp | 4 +- Dependencies/luau/Luau/tests/Differ.test.cpp | 68 +- Dependencies/luau/Luau/tests/Error.test.cpp | 6 +- Dependencies/luau/Luau/tests/Fixture.cpp | 10 +- Dependencies/luau/Luau/tests/Fixture.h | 1 + .../luau/Luau/tests/Frontend.test.cpp | 28 +- .../luau/Luau/tests/Generalization.test.cpp | 4 +- .../luau/Luau/tests/IrBuilder.test.cpp | 10 - .../luau/Luau/tests/IrLowering.test.cpp | 61 +- Dependencies/luau/Luau/tests/Linter.test.cpp | 6 +- Dependencies/luau/Luau/tests/Module.test.cpp | 11 +- .../Luau/tests/NonStrictTypeChecker.test.cpp | 4 +- .../luau/Luau/tests/NonstrictMode.test.cpp | 16 +- .../luau/Luau/tests/Normalize.test.cpp | 44 +- Dependencies/luau/Luau/tests/Parser.test.cpp | 10 +- .../luau/Luau/tests/RequireByString.test.cpp | 6 + .../luau/Luau/tests/RuntimeLimits.test.cpp | 4 +- .../luau/Luau/tests/Simplify.test.cpp | 4 +- .../luau/Luau/tests/Subtyping.test.cpp | 40 +- Dependencies/luau/Luau/tests/Symbol.test.cpp | 4 +- Dependencies/luau/Luau/tests/ToDot.test.cpp | 10 +- .../luau/Luau/tests/ToString.test.cpp | 54 +- Dependencies/luau/Luau/tests/TxnLog.test.cpp | 8 +- .../luau/Luau/tests/TypeFunction.test.cpp | 183 +- .../Luau/tests/TypeInfer.aliases.test.cpp | 42 +- .../Luau/tests/TypeInfer.annotations.test.cpp | 8 +- .../Luau/tests/TypeInfer.anyerror.test.cpp | 22 +- .../Luau/tests/TypeInfer.builtins.test.cpp | 60 +- .../Luau/tests/TypeInfer.classes.test.cpp | 30 +- .../Luau/tests/TypeInfer.functions.test.cpp | 148 +- .../Luau/tests/TypeInfer.generics.test.cpp | 132 +- .../TypeInfer.intersectionTypes.test.cpp | 252 +- .../luau/Luau/tests/TypeInfer.loops.test.cpp | 72 +- .../Luau/tests/TypeInfer.modules.test.cpp | 10 +- .../Luau/tests/TypeInfer.negations.test.cpp | 3 + .../luau/Luau/tests/TypeInfer.oop.test.cpp | 12 +- .../Luau/tests/TypeInfer.operators.test.cpp | 74 +- .../Luau/tests/TypeInfer.primitives.test.cpp | 2 +- .../Luau/tests/TypeInfer.provisional.test.cpp | 68 +- .../Luau/tests/TypeInfer.refinements.test.cpp | 91 +- .../Luau/tests/TypeInfer.singletons.test.cpp | 28 +- .../luau/Luau/tests/TypeInfer.tables.test.cpp | 240 +- .../luau/Luau/tests/TypeInfer.test.cpp | 60 +- .../Luau/tests/TypeInfer.tryUnify.test.cpp | 14 +- .../Luau/tests/TypeInfer.typePacks.test.cpp | 38 +- .../Luau/tests/TypeInfer.typestates.test.cpp | 18 +- .../Luau/tests/TypeInfer.unionTypes.test.cpp | 52 +- .../tests/TypeInfer.unknownnever.test.cpp | 30 +- .../luau/Luau/tests/TypePath.test.cpp | 12 +- Dependencies/luau/Luau/tests/TypeVar.test.cpp | 2 +- .../luau/Luau/tests/Unifier2.test.cpp | 4 +- .../luau/Luau/tests/VisitType.test.cpp | 35 +- .../luau/Luau/tests/conformance/vector.lua | 19 + Dependencies/luau/Luau/tests/main.cpp | 2 +- .../tests/require/with_config/src/.luaurc | 3 +- .../src/directory_alias_requirer.luau | 1 + .../subdirectory/subdirectory_dependency.luau | 1 + Dependencies/luau/Luau/tools/faillist.txt | 45 - .../luau/Luau/tools/fuzz/requirements.txt | 2 +- .../luau/Luau/tools/lldb_formatters.py | 2 +- Dependencies/luau/Luau/tools/test_dcr.py | 2 +- .../luau/{Version-0.639 => Version-0.643} | 0 135 files changed, 5075 insertions(+), 4371 deletions(-) create mode 100644 Dependencies/luau/Luau/tests/require/with_config/src/directory_alias_requirer.luau create mode 100644 Dependencies/luau/Luau/tests/require/with_config/src/subdirectory/subdirectory_dependency.luau rename Dependencies/luau/{Version-0.639 => Version-0.643} (100%) diff --git a/Dependencies/luau/Luau/.gitignore b/Dependencies/luau/Luau/.gitignore index 8e5c95dd..764b97cf 100644 --- a/Dependencies/luau/Luau/.gitignore +++ b/Dependencies/luau/Luau/.gitignore @@ -1,5 +1,6 @@ /build/ /build[.-]*/ +/out /cmake/ /cmake[.-]*/ /coverage/ diff --git a/Dependencies/luau/Luau/Analysis/include/Luau/BuiltinDefinitions.h b/Dependencies/luau/Luau/Analysis/include/Luau/BuiltinDefinitions.h index 94b0d87f..71e50580 100644 --- a/Dependencies/luau/Luau/Analysis/include/Luau/BuiltinDefinitions.h +++ b/Dependencies/luau/Luau/Analysis/include/Luau/BuiltinDefinitions.h @@ -13,6 +13,7 @@ struct Frontend; struct GlobalTypes; struct TypeChecker; struct TypeArena; +struct Subtyping; void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeCheckForAutocomplete = false); TypeId makeUnion(TypeArena& arena, std::vector&& types); @@ -65,7 +66,7 @@ TypeId makeFunction( // Polymorphic void attachMagicFunction(TypeId ty, MagicFunction fn); void attachDcrMagicFunction(TypeId ty, DcrMagicFunction fn); void attachDcrMagicRefinement(TypeId ty, DcrMagicRefinement fn); - +void attachDcrMagicFunctionTypeCheck(TypeId ty, DcrMagicFunctionTypeCheck fn); Property makeProperty(TypeId ty, std::optional documentationSymbol = std::nullopt); void assignPropDocumentationSymbols(TableType::Props& props, const std::string& baseName); diff --git a/Dependencies/luau/Luau/Analysis/include/Luau/Frontend.h b/Dependencies/luau/Luau/Analysis/include/Luau/Frontend.h index f476b582..d8a40d24 100644 --- a/Dependencies/luau/Luau/Analysis/include/Luau/Frontend.h +++ b/Dependencies/luau/Luau/Analysis/include/Luau/Frontend.h @@ -108,6 +108,10 @@ struct FrontendOptions // When true, some internal complexity limits will be scaled down for modules that miss the limit set by moduleTimeLimitSec bool applyInternalLimitScaling = false; + + // An optional callback which is called for every *dirty* module was checked + // Is multi-threaded typechecking is used, this callback might be called from multiple threads and has to be thread-safe + std::function customModuleCheck; }; struct CheckResult diff --git a/Dependencies/luau/Luau/Analysis/include/Luau/Set.h b/Dependencies/luau/Luau/Analysis/include/Luau/Set.h index 274375cf..613e5aa5 100644 --- a/Dependencies/luau/Luau/Analysis/include/Luau/Set.h +++ b/Dependencies/luau/Luau/Analysis/include/Luau/Set.h @@ -4,7 +4,7 @@ #include "Luau/Common.h" #include "Luau/DenseHash.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) namespace Luau { diff --git a/Dependencies/luau/Luau/Analysis/include/Luau/Subtyping.h b/Dependencies/luau/Luau/Analysis/include/Luau/Subtyping.h index 01f1a7ab..18217a6b 100644 --- a/Dependencies/luau/Luau/Analysis/include/Luau/Subtyping.h +++ b/Dependencies/luau/Luau/Analysis/include/Luau/Subtyping.h @@ -125,7 +125,6 @@ struct Subtyping NotNull normalizer; NotNull iceReporter; - NotNull scope; TypeCheckLimits limits; enum class Variance @@ -144,8 +143,7 @@ struct Subtyping NotNull builtinTypes, NotNull typeArena, NotNull normalizer, - NotNull iceReporter, - NotNull scope + NotNull iceReporter ); Subtyping(const Subtyping&) = delete; @@ -164,75 +162,125 @@ struct Subtyping // TODO cyclic types // TODO recursion limits - SubtypingResult isSubtype(TypeId subTy, TypeId superTy); - SubtypingResult isSubtype(TypePackId subTy, TypePackId superTy); + SubtypingResult isSubtype(TypeId subTy, TypeId superTy, NotNull scope); + SubtypingResult isSubtype(TypePackId subTy, TypePackId superTy, NotNull scope); private: DenseHashMap, SubtypingResult, TypePairHash> resultCache{{}}; SubtypingResult cache(SubtypingEnvironment& env, SubtypingResult res, TypeId subTy, TypeId superTy); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypePackId subTy, TypePackId superTy); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp, NotNull scope); template - SubtypingResult isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy); + SubtypingResult isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull scope); template - SubtypingResult isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy); + SubtypingResult isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull scope); template - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TryPair& pair); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TryPair& pair, NotNull scope); template - SubtypingResult isContravariantWith(SubtypingEnvironment& env, const TryPair& pair); + SubtypingResult isContravariantWith(SubtypingEnvironment& env, const TryPair& pair, NotNull); template - SubtypingResult isInvariantWith(SubtypingEnvironment& env, const TryPair& pair); - - SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy); - - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation); - - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const PrimitiveType* superPrim); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const PrimitiveType* superPrim); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const SingletonType* superSingleton); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const FunctionType* subFunction, const FunctionType* superFunction); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable); - - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableIndexer& subIndexer, const TableIndexer& superIndexer); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const Property& subProperty, const Property& superProperty, const std::string& name); + SubtypingResult isInvariantWith(SubtypingEnvironment& env, const TryPair& pair, NotNull); + + SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy, NotNull scope); + + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation, NotNull scope); + + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const PrimitiveType* superPrim, NotNull scope); + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const SingletonType* subSingleton, + const PrimitiveType* superPrim, + NotNull scope + ); + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const SingletonType* subSingleton, + const SingletonType* superSingleton, + NotNull scope + ); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass, NotNull scope); + SubtypingResult + isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const ClassType* subClass, TypeId superTy, const TableType* superTable, NotNull); + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const FunctionType* subFunction, + const FunctionType* superFunction, + NotNull scope + ); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable, NotNull scope); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable, NotNull scope); + + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const TableIndexer& subIndexer, + const TableIndexer& superIndexer, + NotNull scope + ); + SubtypingResult + isCovariantWith(SubtypingEnvironment& env, const Property& subProperty, const Property& superProperty, const std::string& name, NotNull); SubtypingResult isCovariantWith( SubtypingEnvironment& env, const std::shared_ptr& subNorm, - const std::shared_ptr& superNorm + const std::shared_ptr& superNorm, + NotNull scope ); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const NormalizedClassType& superClass); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const NormalizedStringType& superString); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const TypeIds& superTables); SubtypingResult isCovariantWith( SubtypingEnvironment& env, - const NormalizedFunctionType& subFunction, - const NormalizedFunctionType& superFunction + const NormalizedClassType& subClass, + const NormalizedClassType& superClass, + NotNull scope + ); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables, NotNull scope); + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const NormalizedStringType& subString, + const NormalizedStringType& superString, + NotNull scope ); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes); + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const NormalizedStringType& subString, + const TypeIds& superTables, + NotNull scope + ); + SubtypingResult + isCovariantWith(SubtypingEnvironment& env, const NormalizedFunctionType& subFunction, const NormalizedFunctionType& superFunction, NotNull); + SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes, NotNull scope); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const VariadicTypePack* subVariadic, const VariadicTypePack* superVariadic); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFunctionInstance, const TypeId superTy); - SubtypingResult isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFunctionInstance); + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const VariadicTypePack* subVariadic, + const VariadicTypePack* superVariadic, + NotNull scope + ); + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const TypeFunctionInstanceType* subFunctionInstance, + const TypeId superTy, + NotNull scope + ); + SubtypingResult isCovariantWith( + SubtypingEnvironment& env, + const TypeId subTy, + const TypeFunctionInstanceType* superFunctionInstance, + NotNull scope + ); bool bindGeneric(SubtypingEnvironment& env, TypeId subTp, TypeId superTp); bool bindGeneric(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp); @@ -240,7 +288,7 @@ struct Subtyping template TypeId makeAggregateType(const Container& container, TypeId orElse); - std::pair handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance); + std::pair handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance, NotNull scope); [[noreturn]] void unexpected(TypeId ty); [[noreturn]] void unexpected(TypePackId tp); diff --git a/Dependencies/luau/Luau/Analysis/include/Luau/Type.h b/Dependencies/luau/Luau/Analysis/include/Luau/Type.h index 2b2cb960..a43dbff9 100644 --- a/Dependencies/luau/Luau/Analysis/include/Luau/Type.h +++ b/Dependencies/luau/Luau/Analysis/include/Luau/Type.h @@ -34,6 +34,8 @@ using ScopePtr = std::shared_ptr; struct TypeFunction; struct Constraint; +struct Subtyping; +struct TypeChecker2; /** * There are three kinds of type variables: @@ -289,7 +291,6 @@ struct MagicFunctionCallContext }; using DcrMagicFunction = std::function; - struct MagicRefinementContext { NotNull scope; @@ -297,8 +298,17 @@ struct MagicRefinementContext std::vector> discriminantTypes; }; -using DcrMagicRefinement = void (*)(const MagicRefinementContext&); +struct MagicFunctionTypeCheckContext +{ + NotNull typechecker; + NotNull builtinTypes; + const class AstExprCall* callSite; + TypePackId arguments; + NotNull checkScope; +}; +using DcrMagicRefinement = void (*)(const MagicRefinementContext&); +using DcrMagicFunctionTypeCheck = std::function; struct FunctionType { // Global monomorphic function @@ -359,6 +369,14 @@ struct FunctionType MagicFunction magicFunction = nullptr; DcrMagicFunction dcrMagicFunction = nullptr; DcrMagicRefinement dcrMagicRefinement = nullptr; + + // Callback to allow custom typechecking of builtin function calls whose argument types + // will only be resolved after constraint solving. For example, the arguments to string.format + // have types that can only be decided after parsing the format string and unifying + // with the passed in values, but the correctness of the call can only be decided after + // all the types have been finalized. + DcrMagicFunctionTypeCheck dcrMagicTypeCheck = nullptr; + bool hasSelf; // `hasNoFreeOrGenericTypes` should be true if and only if the type does not have any free or generic types present inside it. // this flag is used as an optimization to exit early from procedures that manipulate free or generic types. diff --git a/Dependencies/luau/Luau/Analysis/include/Luau/TypeChecker2.h b/Dependencies/luau/Luau/Analysis/include/Luau/TypeChecker2.h index 981fdfe6..0faf036d 100644 --- a/Dependencies/luau/Luau/Analysis/include/Luau/TypeChecker2.h +++ b/Dependencies/luau/Luau/Analysis/include/Luau/TypeChecker2.h @@ -2,7 +2,15 @@ #pragma once +#include "Luau/Error.h" #include "Luau/NotNull.h" +#include "Luau/Common.h" +#include "Luau/TypeUtils.h" +#include "Luau/Type.h" +#include "Luau/TypeFwd.h" +#include "Luau/TypeOrPack.h" +#include "Luau/Normalize.h" +#include "Luau/Subtyping.h" namespace Luau { @@ -13,6 +21,42 @@ struct TypeCheckLimits; struct UnifierSharedState; struct SourceModule; struct Module; +struct InternalErrorReporter; +struct Scope; +struct PropertyType; +struct PropertyTypes; +struct StackPusher; + +struct Reasonings +{ + // the list of reasons + std::vector reasons; + + // this should be true if _all_ of the reasons have an error suppressing type, and false otherwise. + bool suppressed; + + std::string toString() + { + // DenseHashSet ordering is entirely undefined, so we want to + // sort the reasons here to achieve a stable error + // stringification. + std::sort(reasons.begin(), reasons.end()); + std::string allReasons; + bool first = true; + for (const std::string& reason : reasons) + { + if (first) + first = false; + else + allReasons += "\n\t"; + + allReasons += reason; + } + + return allReasons; + } +}; + void check( NotNull builtinTypes, @@ -23,4 +67,154 @@ void check( Module* module ); +struct TypeChecker2 +{ + NotNull builtinTypes; + DcrLogger* logger; + const NotNull limits; + const NotNull ice; + const SourceModule* sourceModule; + Module* module; + + TypeContext typeContext = TypeContext::Default; + std::vector> stack; + std::vector functionDeclStack; + + DenseHashSet seenTypeFunctionInstances{nullptr}; + + Normalizer normalizer; + Subtyping _subtyping; + NotNull subtyping; + + TypeChecker2( + NotNull builtinTypes, + NotNull unifierState, + NotNull limits, + DcrLogger* logger, + const SourceModule* sourceModule, + Module* module + ); + + void visit(AstStatBlock* block); + void reportError(TypeErrorData data, const Location& location); + Reasonings explainReasonings(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& r); + Reasonings explainReasonings(TypePackId subTp, TypePackId superTp, Location location, const SubtypingResult& r); + +private: + static bool allowsNoReturnValues(const TypePackId tp); + static Location getEndLocation(const AstExprFunction* function); + bool isErrorCall(const AstExprCall* call); + bool hasBreak(AstStat* node); + const AstStat* getFallthrough(const AstStat* node); + std::optional pushStack(AstNode* node); + void checkForInternalTypeFunction(TypeId ty, Location location); + TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location); + TypePackId lookupPack(AstExpr* expr); + TypeId lookupType(AstExpr* expr); + TypeId lookupAnnotation(AstType* annotation); + std::optional lookupPackAnnotation(AstTypePack* annotation); + TypeId lookupExpectedType(AstExpr* expr); + TypePackId lookupExpectedPack(AstExpr* expr, TypeArena& arena); + TypePackId reconstructPack(AstArray exprs, TypeArena& arena); + Scope* findInnermostScope(Location location); + void visit(AstStat* stat); + void visit(AstStatIf* ifStatement); + void visit(AstStatWhile* whileStatement); + void visit(AstStatRepeat* repeatStatement); + void visit(AstStatBreak*); + void visit(AstStatContinue*); + void visit(AstStatReturn* ret); + void visit(AstStatExpr* expr); + void visit(AstStatLocal* local); + void visit(AstStatFor* forStatement); + void visit(AstStatForIn* forInStatement); + std::optional getBindingType(AstExpr* expr); + void reportErrorsFromAssigningToNever(AstExpr* lhs, TypeId rhsType); + void visit(AstStatAssign* assign); + void visit(AstStatCompoundAssign* stat); + void visit(AstStatFunction* stat); + void visit(AstStatLocalFunction* stat); + void visit(const AstTypeList* typeList); + void visit(AstStatTypeAlias* stat); + void visit(AstStatTypeFunction* stat); + void visit(AstTypeList types); + void visit(AstStatDeclareFunction* stat); + void visit(AstStatDeclareGlobal* stat); + void visit(AstStatDeclareClass* stat); + void visit(AstStatError* stat); + void visit(AstExpr* expr, ValueContext context); + void visit(AstExprGroup* expr, ValueContext context); + void visit(AstExprConstantNil* expr); + void visit(AstExprConstantBool* expr); + void visit(AstExprConstantNumber* expr); + void visit(AstExprConstantString* expr); + void visit(AstExprLocal* expr); + void visit(AstExprGlobal* expr); + void visit(AstExprVarargs* expr); + void visitCall(AstExprCall* call); + void visit(AstExprCall* call); + std::optional tryStripUnionFromNil(TypeId ty); + TypeId stripFromNilAndReport(TypeId ty, const Location& location); + void visitExprName(AstExpr* expr, Location location, const std::string& propName, ValueContext context, TypeId astIndexExprTy); + void visit(AstExprIndexName* indexName, ValueContext context); + void indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const MetatableType* metaTable, TypeId exprType, TypeId indexType); + void visit(AstExprIndexExpr* indexExpr, ValueContext context); + void visit(AstExprFunction* fn); + void visit(AstExprTable* expr); + void visit(AstExprUnary* expr); + TypeId visit(AstExprBinary* expr, AstNode* overrideKey = nullptr); + void visit(AstExprTypeAssertion* expr); + void visit(AstExprIfElse* expr); + void visit(AstExprInterpString* interpString); + void visit(AstExprError* expr); + TypeId flattenPack(TypePackId pack); + void visitGenerics(AstArray generics, AstArray genericPacks); + void visit(AstType* ty); + void visit(AstTypeReference* ty); + void visit(AstTypeTable* table); + void visit(AstTypeFunction* ty); + void visit(AstTypeTypeof* ty); + void visit(AstTypeUnion* ty); + void visit(AstTypeIntersection* ty); + void visit(AstTypePack* pack); + void visit(AstTypePackExplicit* tp); + void visit(AstTypePackVariadic* tp); + void visit(AstTypePackGeneric* tp); + + template + Reasonings explainReasonings_(TID subTy, TID superTy, Location location, const SubtypingResult& r); + + void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result); + void explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result); + bool testIsSubtype(TypeId subTy, TypeId superTy, Location location); + bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location); + void reportError(TypeError e); + void reportErrors(ErrorVec errors); + PropertyTypes lookupProp( + const NormalizedType* norm, + const std::string& prop, + ValueContext context, + const Location& location, + TypeId astIndexExprType, + std::vector& errors + ); + // If the provided type does not have the named property, report an error. + void checkIndexTypeFromType(TypeId tableTy, const std::string& prop, ValueContext context, const Location& location, TypeId astIndexExprType); + PropertyType hasIndexTypeFromType( + TypeId ty, + const std::string& prop, + ValueContext context, + const Location& location, + DenseHashSet& seen, + TypeId astIndexExprType, + std::vector& errors + ); + + void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const; + bool isErrorSuppressing(Location loc, TypeId ty); + bool isErrorSuppressing(Location loc1, TypeId ty1, Location loc2, TypeId ty2); + bool isErrorSuppressing(Location loc, TypePackId tp); + bool isErrorSuppressing(Location loc1, TypePackId tp1, Location loc2, TypePackId tp2); +}; + } // namespace Luau diff --git a/Dependencies/luau/Luau/Analysis/include/Luau/VisitType.h b/Dependencies/luau/Luau/Analysis/include/Luau/VisitType.h index e588d06b..e943cced 100644 --- a/Dependencies/luau/Luau/Analysis/include/Luau/VisitType.h +++ b/Dependencies/luau/Luau/Analysis/include/Luau/VisitType.h @@ -11,7 +11,7 @@ LUAU_FASTINT(LuauVisitRecursionLimit) LUAU_FASTFLAG(LuauBoundLazyTypes2) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) namespace Luau { @@ -226,12 +226,12 @@ struct GenericTypeVisitor } else if (auto ftv = get(ty)) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (visit(ty, *ftv)) { // TODO: Replace these if statements with assert()s when we - // delete FFlag::DebugLuauDeferredConstraintResolution. + // delete FFlag::LuauSolverV2. // // When the old solver is used, these pointers are always // unused. When the new solver is used, they are never null. @@ -276,7 +276,7 @@ struct GenericTypeVisitor { for (auto& [_name, prop] : ttv->props) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (auto ty = prop.readTy) traverse(*ty); @@ -314,7 +314,7 @@ struct GenericTypeVisitor { for (const auto& [name, prop] : ctv->props) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (auto ty = prop.readTy) traverse(*ty); diff --git a/Dependencies/luau/Luau/Analysis/src/AnyTypeSummary.cpp b/Dependencies/luau/Luau/Analysis/src/AnyTypeSummary.cpp index 4ff2051b..85f567af 100644 --- a/Dependencies/luau/Luau/Analysis/src/AnyTypeSummary.cpp +++ b/Dependencies/luau/Luau/Analysis/src/AnyTypeSummary.cpp @@ -38,7 +38,7 @@ #include -LUAU_FASTFLAGVARIABLE(StudioReportLuauAny, false); +LUAU_FASTFLAGVARIABLE(StudioReportLuauAny2, false); LUAU_FASTINTVARIABLE(LuauAnySummaryRecursionLimit, 300); LUAU_FASTFLAG(DebugLuauMagicTypes); @@ -211,14 +211,17 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatLocal* local, const Module if (!maybeRequire) continue; - if (isAnyCast(scope, local->values.data[posn], module, builtinTypes)) + if (std::min(local->values.size - 1, posn) < head.size()) { - TelemetryTypePair types; + if (isAnyCast(scope, local->values.data[posn], module, builtinTypes)) + { + TelemetryTypePair types; - types.inferredType = toString(head[std::min(local->values.size - 1, posn)]); + types.inferredType = toString(head[std::min(local->values.size - 1, posn)]); - TypeInfo ti{Pattern::Casts, toString(ctxNode), types}; - typeInfo.push_back(ti); + TypeInfo ti{Pattern::Casts, toString(ctxNode), types}; + typeInfo.push_back(ti); + } } } else @@ -292,7 +295,7 @@ void AnyTypeSummary::visit(const Scope* scope, AstStatAssign* assign, const Modu types.annotatedType = toString(tp); auto loc = std::min(assign->vars.size - 1, posn); - if (head.size() >= assign->vars.size) + if (head.size() >= assign->vars.size && posn < head.size()) { types.inferredType = toString(head[posn]); } @@ -689,7 +692,7 @@ bool AnyTypeSummary::containsAny(TypeId typ) { for (auto& [_name, prop] : ty->props) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (auto newT = follow(prop.readTy)) { diff --git a/Dependencies/luau/Luau/Analysis/src/AstQuery.cpp b/Dependencies/luau/Luau/Analysis/src/AstQuery.cpp index 243834f8..c8470373 100644 --- a/Dependencies/luau/Luau/Analysis/src/AstQuery.cpp +++ b/Dependencies/luau/Luau/Analysis/src/AstQuery.cpp @@ -11,8 +11,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); -LUAU_FASTFLAGVARIABLE(LuauFixBindingForGlobalPos, false); +LUAU_FASTFLAG(LuauSolverV2) namespace Luau { @@ -327,7 +326,7 @@ static std::optional findBindingLocalStatement(const SourceModule { // Bindings coming from global sources (e.g., definition files) have a zero position. // They cannot be defined from a local statement - if (FFlag::LuauFixBindingForGlobalPos && binding.location == Location{{0, 0}, {0, 0}}) + if (binding.location == Location{{0, 0}, {0, 0}}) return std::nullopt; std::vector nodes = findAstAncestryOfPosition(source, binding.location.begin); @@ -531,7 +530,7 @@ std::optional getDocumentationSymbolAtPosition(const Source { if (auto propIt = ttv->props.find(indexName->index.value); propIt != ttv->props.end()) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (auto ty = propIt->second.readTy) return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol); @@ -544,7 +543,7 @@ std::optional getDocumentationSymbolAtPosition(const Source { if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end()) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (auto ty = propIt->second.readTy) return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol); diff --git a/Dependencies/luau/Luau/Analysis/src/Autocomplete.cpp b/Dependencies/luau/Luau/Analysis/src/Autocomplete.cpp index a4acfb85..ee865edd 100644 --- a/Dependencies/luau/Luau/Analysis/src/Autocomplete.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Autocomplete.cpp @@ -13,7 +13,7 @@ #include #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); static const std::unordered_set kStatementStartingKeywords = {"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"}; @@ -142,11 +142,11 @@ static bool checkTypeMatch(TypeId subTy, TypeId superTy, NotNull scope, T UnifierSharedState unifierState(&iceReporter); Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}}; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { - Subtyping subtyping{builtinTypes, NotNull{typeArena}, NotNull{&normalizer}, NotNull{&iceReporter}, scope}; + Subtyping subtyping{builtinTypes, NotNull{typeArena}, NotNull{&normalizer}, NotNull{&iceReporter}}; - return subtyping.isSubtype(subTy, superTy).isSubtype; + return subtyping.isSubtype(subTy, superTy, scope).isSubtype; } else { @@ -293,7 +293,7 @@ static void autocompleteProps( { Luau::TypeId type; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (auto ty = prop.readTy) type = follow(*ty); @@ -1964,7 +1964,7 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName return {}; ModulePtr module; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) module = frontend.moduleResolver.getModule(moduleName); else module = frontend.moduleResolverForAutocomplete.getModule(moduleName); @@ -1974,7 +1974,7 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName NotNull builtinTypes = frontend.builtinTypes; Scope* globalScope; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) globalScope = frontend.globals.globalScope.get(); else globalScope = frontend.globalsForAutocomplete.globalScope.get(); diff --git a/Dependencies/luau/Luau/Analysis/src/BuiltinDefinitions.cpp b/Dependencies/luau/Luau/Analysis/src/BuiltinDefinitions.cpp index e565f6bf..21ae0f11 100644 --- a/Dependencies/luau/Luau/Analysis/src/BuiltinDefinitions.cpp +++ b/Dependencies/luau/Luau/Analysis/src/BuiltinDefinitions.cpp @@ -10,10 +10,12 @@ #include "Luau/ConstraintGenerator.h" #include "Luau/NotNull.h" #include "Luau/TypeInfer.h" +#include "Luau/TypeChecker2.h" #include "Luau/TypeFunction.h" #include "Luau/TypePack.h" #include "Luau/Type.h" #include "Luau/TypeUtils.h" +#include "Luau/Subtyping.h" #include @@ -23,7 +25,8 @@ * about a function that takes any number of values, but where each value must have some specific type. */ -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); +LUAU_FASTFLAGVARIABLE(LuauDCRMagicFunctionTypeChecker, false); namespace Luau { @@ -181,6 +184,14 @@ void attachDcrMagicRefinement(TypeId ty, DcrMagicRefinement fn) LUAU_ASSERT(!"Got a non functional type"); } +void attachDcrMagicFunctionTypeCheck(TypeId ty, DcrMagicFunctionTypeCheck fn) +{ + if (auto ftv = getMutable(ty)) + ftv->dcrMagicTypeCheck = fn; + else + LUAU_ASSERT(!"Got a non functional type"); +} + Property makeProperty(TypeId ty, std::optional documentationSymbol) { return { @@ -260,7 +271,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC TypeArena& arena = globals.globalTypes; NotNull builtinTypes = globals.builtinTypes; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) builtinTypeFunctions().addToScope(NotNull{&arena}, NotNull{globals.globalScope.get()}); LoadDefinitionFileResult loadResult = frontend.loadDefinitionFile( @@ -305,7 +316,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC // getmetatable : ({ @metatable MT, {+ +} }) -> MT addGlobalBinding(globals, "getmetatable", makeFunction(arena, std::nullopt, {genericMT}, {}, {tableMetaMT}, {genericMT}), "@luau"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { TypeId genericT = arena.addType(GenericType{"T"}); TypeId tMetaMT = arena.addType(MetatableType{genericT, genericMT}); @@ -354,7 +365,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC attachMagicFunction(getGlobalBinding(globals, "assert"), magicFunctionAssert); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // declare function assert(value: T, errorMessage: string?): intersect TypeId genericT = arena.addType(GenericType{"T"}); @@ -374,7 +385,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC if (TableType* ttv = getMutable(getGlobalBinding(globals, "table"))) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-114044 - The new solver does not yet support generic tables, // which act, in an odd way, like generics that are constrained to @@ -517,7 +528,7 @@ static bool dcrMagicFunctionFormat(MagicFunctionCallContext context) size_t paramOffset = 1; - // unify the prefix one argument at a time + // 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]); @@ -530,12 +541,52 @@ static bool dcrMagicFunctionFormat(MagicFunctionCallContext context) 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; } +static void dcrMagicFunctionTypeCheckFormat(MagicFunctionTypeCheckContext context) +{ + 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; + + 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[i + (calledWithSelf ? 0 : paramOffset)]->location; + // use subtyping instead here + SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope); + if (!result.isSubtype) + { + Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result); + context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location); + } + } +} + static std::vector parsePatternString(NotNull builtinTypes, const char* data, size_t size) { std::vector result; @@ -869,7 +920,7 @@ TypeId makeStringMetatable(NotNull builtinTypes) const TypePackId oneStringPack = arena->addTypePack({stringType}); const TypePackId anyTypePack = builtinTypes->anyTypePack; - const TypePackId variadicTailPack = FFlag::DebugLuauDeferredConstraintResolution ? builtinTypes->unknownTypePack : anyTypePack; + const TypePackId variadicTailPack = FFlag::LuauSolverV2 ? builtinTypes->unknownTypePack : anyTypePack; const TypePackId emptyPack = arena->addTypePack({}); const TypePackId stringVariadicList = arena->addTypePack(TypePackVar{VariadicTypePack{stringType}}); const TypePackId numberVariadicList = arena->addTypePack(TypePackVar{VariadicTypePack{numberType}}); @@ -880,6 +931,8 @@ TypeId makeStringMetatable(NotNull builtinTypes) formatFTV.isCheckedFunction = true; const TypeId formatFn = arena->addType(formatFTV); attachDcrMagicFunction(formatFn, dcrMagicFunctionFormat); + if (FFlag::LuauDCRMagicFunctionTypeChecker) + attachDcrMagicFunctionTypeCheck(formatFn, dcrMagicFunctionTypeCheckFormat); const TypeId stringToStringType = makeFunction(*arena, std::nullopt, {}, {}, {stringType}, {}, {stringType}, /* checked */ true); diff --git a/Dependencies/luau/Luau/Analysis/src/Clone.cpp b/Dependencies/luau/Luau/Analysis/src/Clone.cpp index 7446846a..4af3e7f8 100644 --- a/Dependencies/luau/Luau/Analysis/src/Clone.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Clone.cpp @@ -6,7 +6,7 @@ #include "Luau/TypePack.h" #include "Luau/Unifiable.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +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) @@ -191,7 +191,7 @@ class TypeCloner Property shallowClone(const Property& p) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { std::optional cloneReadTy; if (auto ty = p.readTy) diff --git a/Dependencies/luau/Luau/Analysis/src/ConstraintGenerator.cpp b/Dependencies/luau/Luau/Analysis/src/ConstraintGenerator.cpp index ba2d1beb..face6825 100644 --- a/Dependencies/luau/Luau/Analysis/src/ConstraintGenerator.cpp +++ b/Dependencies/luau/Luau/Analysis/src/ConstraintGenerator.cpp @@ -1793,7 +1793,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall* std::vector> expectedTypesForCall = getExpectedCallTypesForFunctionOverloads(fnType); - module->astOriginalCallTypes[call] = fnType; + module->astOriginalCallTypes[call->func] = fnType; Checkpoint argBeginCheckpoint = checkpoint(this); diff --git a/Dependencies/luau/Luau/Analysis/src/DataFlowGraph.cpp b/Dependencies/luau/Luau/Analysis/src/DataFlowGraph.cpp index 73c2193c..9c42e4d8 100644 --- a/Dependencies/luau/Luau/Analysis/src/DataFlowGraph.cpp +++ b/Dependencies/luau/Luau/Analysis/src/DataFlowGraph.cpp @@ -10,7 +10,7 @@ #include LUAU_FASTFLAG(DebugLuauFreezeArena) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) namespace Luau { @@ -139,7 +139,7 @@ DataFlowGraph DataFlowGraphBuilder::build(AstStatBlock* block, NotNull result = getCheckResult(name, true, frontendOptions.forAutocomplete)) @@ -511,7 +512,7 @@ CheckResult Frontend::check(const ModuleName& name, std::optionallintResult; - if (FFlag::StudioReportLuauAny && item.options.retainFullTypeGraphs) + if (FFlag::StudioReportLuauAny2 && item.options.retainFullTypeGraphs) { if (item.module) { @@ -546,7 +547,7 @@ std::vector Frontend::checkQueuedModules( ) { FrontendOptions frontendOptions = optionOverride.value_or(options); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) frontendOptions.forAutocomplete = false; // By taking data into locals, we make sure queue is cleared at the end, even if an ICE or a different exception is thrown @@ -780,7 +781,7 @@ std::vector Frontend::checkQueuedModules( std::optional Frontend::getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) forAutocomplete = false; auto it = sourceNodes.find(name); @@ -1040,6 +1041,9 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) item.stats.timeCheck += duration; item.stats.filesStrict += 1; + if (DFFlag::LuauRunCustomModuleChecks && item.options.customModuleCheck) + item.options.customModuleCheck(sourceModule, *moduleForAutocomplete); + item.module = moduleForAutocomplete; return; } @@ -1057,10 +1061,10 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) item.stats.filesStrict += mode == Mode::Strict; item.stats.filesNonstrict += mode == Mode::Nonstrict; - if (module == nullptr) - throw InternalCompilerError("Frontend::check produced a nullptr module for " + item.name, item.name); + if (DFFlag::LuauRunCustomModuleChecks && item.options.customModuleCheck) + item.options.customModuleCheck(sourceModule, *module); - if (FFlag::DebugLuauDeferredConstraintResolution && mode == Mode::NoCheck) + if (FFlag::LuauSolverV2 && mode == Mode::NoCheck) module->errors.clear(); if (item.options.runLintChecks) @@ -1527,7 +1531,7 @@ ModulePtr Frontend::check( TypeCheckLimits typeCheckLimits ) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { auto prepareModuleScopeWrap = [this, forAutocomplete](const ModuleName& name, const ScopePtr& scope) { diff --git a/Dependencies/luau/Luau/Analysis/src/Generalization.cpp b/Dependencies/luau/Luau/Analysis/src/Generalization.cpp index ea736642..d209cb81 100644 --- a/Dependencies/luau/Luau/Analysis/src/Generalization.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Generalization.cpp @@ -887,9 +887,6 @@ std::optional generalize( if (ty->owningArena != arena || ty->persistent) return ty; - if (const FunctionType* ft = get(ty); ft && (!ft->generics.empty() || !ft->genericPacks.empty())) - return ty; - FreeTypeSearcher fts{scope, cachedTypes}; fts.traverse(ty); @@ -912,8 +909,17 @@ std::optional generalize( FunctionType* ftv = getMutable(ty); if (ftv) { - ftv->generics = std::move(gen.generics); - ftv->genericPacks = std::move(gen.genericPacks); + // If we're generalizing a function type, add any of the newly inferred + // generics to the list of existing generic types. + for (const auto g : std::move(gen.generics)) + { + ftv->generics.push_back(g); + } + // Ditto for generic packs. + for (const auto gp : std::move(gen.genericPacks)) + { + ftv->genericPacks.push_back(gp); + } } return ty; diff --git a/Dependencies/luau/Luau/Analysis/src/Instantiation.cpp b/Dependencies/luau/Luau/Analysis/src/Instantiation.cpp index 0c610aba..4b6d1115 100644 --- a/Dependencies/luau/Luau/Analysis/src/Instantiation.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Instantiation.cpp @@ -10,7 +10,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) namespace Luau { @@ -157,7 +157,7 @@ TypeId ReplaceGenerics::clean(TypeId ty) clone.definitionLocation = ttv->definitionLocation; return addType(std::move(clone)); } - else if (FFlag::DebugLuauDeferredConstraintResolution) + else if (FFlag::LuauSolverV2) { TypeId res = freshType(NotNull{arena}, builtinTypes, scope); getMutable(res)->level = level; diff --git a/Dependencies/luau/Luau/Analysis/src/Linter.cpp b/Dependencies/luau/Luau/Analysis/src/Linter.cpp index 23457f4c..c4f46c84 100644 --- a/Dependencies/luau/Luau/Analysis/src/Linter.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Linter.cpp @@ -14,7 +14,7 @@ LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAttribute) LUAU_FASTFLAG(LuauNativeAttribute) @@ -1971,7 +1971,7 @@ class LintTableLiteral : AstVisitor bool visit(AstTypeTable* node) override { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { struct Rec { diff --git a/Dependencies/luau/Luau/Analysis/src/Module.cpp b/Dependencies/luau/Luau/Analysis/src/Module.cpp index f9a3f67a..3a049216 100644 --- a/Dependencies/luau/Luau/Analysis/src/Module.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Module.cpp @@ -14,8 +14,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); -LUAU_FASTFLAGVARIABLE(LuauSkipEmptyInstantiations, false); +LUAU_FASTFLAG(LuauSolverV2); namespace Luau { @@ -122,7 +121,7 @@ struct ClonePublicInterface : Substitution if (FunctionType* ftv = getMutable(result)) { - if (FFlag::LuauSkipEmptyInstantiations && ftv->generics.empty() && ftv->genericPacks.empty()) + if (ftv->generics.empty() && ftv->genericPacks.empty()) { GenericTypeFinder marker; marker.traverse(result); @@ -220,7 +219,7 @@ void Module::clonePublicInterface(NotNull builtinTypes, InternalEr ScopePtr moduleScope = getModuleScope(); TypePackId returnType = moduleScope->returnType; - std::optional varargPack = FFlag::DebugLuauDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack; + std::optional varargPack = FFlag::LuauSolverV2 ? std::nullopt : moduleScope->varargPack; TxnLog log; ClonePublicInterface clonePublicInterface{&log, builtinTypes, this}; diff --git a/Dependencies/luau/Luau/Analysis/src/NonStrictTypeChecker.cpp b/Dependencies/luau/Luau/Analysis/src/NonStrictTypeChecker.cpp index 16225e96..116cf5cb 100644 --- a/Dependencies/luau/Luau/Analysis/src/NonStrictTypeChecker.cpp +++ b/Dependencies/luau/Luau/Analysis/src/NonStrictTypeChecker.cpp @@ -182,7 +182,7 @@ struct NonStrictTypeChecker , arena(arena) , module(module) , normalizer{arena, builtinTypes, unifierState, /* cache inhabitance */ true} - , subtyping{builtinTypes, arena, NotNull(&normalizer), ice, NotNull{module->getModuleScope().get()}} + , subtyping{builtinTypes, arena, NotNull(&normalizer), ice} , dfg(dfg) , limits(limits) { @@ -531,7 +531,7 @@ struct NonStrictTypeChecker NonStrictContext visit(AstExprCall* call) { NonStrictContext fresh{}; - TypeId* originalCallTy = module->astOriginalCallTypes.find(call); + TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func); if (!originalCallTy) return fresh; @@ -699,6 +699,7 @@ struct NonStrictTypeChecker // If this fragment of the ast will run time error, return the type that causes this std::optional willRunTimeError(AstExpr* fragment, const NonStrictContext& context) { + NotNull scope{Luau::findScopeAtPosition(*module, fragment->location.end).get()}; DefId def = dfg->getDef(fragment); std::vector defs; collectOperands(def, &defs); @@ -708,7 +709,7 @@ struct NonStrictTypeChecker { TypeId actualType = lookupType(fragment); - SubtypingResult r = subtyping.isSubtype(actualType, *contextTy); + SubtypingResult r = subtyping.isSubtype(actualType, *contextTy, scope); if (r.normalizationTooComplex) reportError(NormalizationTooComplex{}, fragment->location); if (r.isSubtype) @@ -721,6 +722,7 @@ struct NonStrictTypeChecker std::optional willRunTimeErrorFunctionDefinition(AstLocal* fragment, const NonStrictContext& context) { + NotNull scope{Luau::findScopeAtPosition(*module, fragment->location.end).get()}; DefId def = dfg->getDef(fragment); std::vector defs; collectOperands(def, &defs); @@ -728,8 +730,8 @@ struct NonStrictTypeChecker { if (std::optional contextTy = context.find(def)) { - SubtypingResult r1 = subtyping.isSubtype(builtinTypes->unknownType, *contextTy); - SubtypingResult r2 = subtyping.isSubtype(*contextTy, builtinTypes->unknownType); + SubtypingResult r1 = subtyping.isSubtype(builtinTypes->unknownType, *contextTy, scope); + SubtypingResult r2 = subtyping.isSubtype(*contextTy, builtinTypes->unknownType, scope); if (r1.normalizationTooComplex || r2.normalizationTooComplex) reportError(NormalizationTooComplex{}, fragment->location); bool isUnknown = r1.isSubtype && r2.isSubtype; @@ -747,7 +749,7 @@ struct NonStrictTypeChecker if (!cachedResult) cachedResult = arena->addType(NegationType{baseType}); return cachedResult; - }; + } }; void checkNonStrict( diff --git a/Dependencies/luau/Luau/Analysis/src/Normalize.cpp b/Dependencies/luau/Luau/Analysis/src/Normalize.cpp index 8935a476..2db2f40c 100644 --- a/Dependencies/luau/Luau/Analysis/src/Normalize.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Normalize.cpp @@ -24,16 +24,16 @@ LUAU_FASTFLAGVARIABLE(LuauFixCyclicTablesBlowingStack, false); // This could theoretically be 2000 on amd64, but x86 requires this. LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200); LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000); -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); static bool fixReduceStackPressure() { - return FFlag::LuauFixReduceStackPressure || FFlag::DebugLuauDeferredConstraintResolution; + return FFlag::LuauFixReduceStackPressure || FFlag::LuauSolverV2; } static bool fixCyclicTablesBlowingStack() { - return FFlag::LuauFixCyclicTablesBlowingStack || FFlag::DebugLuauDeferredConstraintResolution; + return FFlag::LuauFixCyclicTablesBlowingStack || FFlag::LuauSolverV2; } namespace Luau @@ -42,7 +42,7 @@ namespace Luau // helper to make `FFlag::LuauNormalizeAwayUninhabitableTables` not explicitly required when DCR is enabled. static bool normalizeAwayUninhabitableTables() { - return FFlag::LuauNormalizeAwayUninhabitableTables || FFlag::DebugLuauDeferredConstraintResolution; + return FFlag::LuauNormalizeAwayUninhabitableTables || FFlag::LuauSolverV2; } static bool shouldEarlyExit(NormalizationResult res) @@ -553,7 +553,7 @@ NormalizationResult Normalizer::isInhabited(TypeId ty, Set& seen) { for (const auto& [_, prop] : ttv->props) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // A table enclosing a read property whose type is uninhabitable is also itself uninhabitable, // but not its write property. That just means the write property doesn't exist, and so is readonly. @@ -2583,7 +2583,7 @@ std::optional Normalizer::intersectionOfTables(TypeId here, TypeId there { const auto& [_name, tprop] = *tfound; // TODO: variance issues here, which can't be fixed until we have read/write property types - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (hprop.readTy.has_value()) { @@ -3249,7 +3249,7 @@ NormalizationResult Normalizer::intersectNormalWithTy(NormalizedType& here, Type // this is a noop since an intersection with `unknown` is trivial. return NormalizationResult::True; } - else if ((FFlag::LuauNormalizeNotUnknownIntersection || FFlag::DebugLuauDeferredConstraintResolution) && get(t)) + else if ((FFlag::LuauNormalizeNotUnknownIntersection || FFlag::LuauSolverV2) && get(t)) { // if we're intersecting with `~unknown`, this is equivalent to intersecting with `never` // this means we should clear the type entirely. @@ -3389,7 +3389,7 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm) if (!get(norm.buffers)) result.push_back(builtinTypes->bufferType); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { result.reserve(result.size() + norm.tables.size()); for (auto table : norm.tables) @@ -3427,11 +3427,11 @@ bool isSubtype(TypeId subTy, TypeId superTy, NotNull scope, NotNull scope, N Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; // Subtyping under DCR is not implemented using unification! - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { - Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&ice}, scope}; + Subtyping subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&ice}}; - return subtyping.isSubtype(subPack, superPack).isSubtype; + return subtyping.isSubtype(subPack, superPack, scope).isSubtype; } else { @@ -3466,7 +3466,7 @@ bool isSubtype(TypePackId subPack, TypePackId superPack, NotNull scope, N bool isConsistentSubtype(TypeId subTy, TypeId superTy, NotNull scope, NotNull builtinTypes, InternalErrorReporter& ice) { - LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(!FFlag::LuauSolverV2); UnifierSharedState sharedState{&ice}; TypeArena arena; @@ -3486,7 +3486,7 @@ bool isConsistentSubtype( InternalErrorReporter& ice ) { - LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(!FFlag::LuauSolverV2); UnifierSharedState sharedState{&ice}; TypeArena arena; diff --git a/Dependencies/luau/Luau/Analysis/src/OverloadResolution.cpp b/Dependencies/luau/Luau/Analysis/src/OverloadResolution.cpp index 612f4ad2..972c9e3a 100644 --- a/Dependencies/luau/Luau/Analysis/src/OverloadResolution.cpp +++ b/Dependencies/luau/Luau/Analysis/src/OverloadResolution.cpp @@ -28,7 +28,7 @@ OverloadResolver::OverloadResolver( , scope(scope) , ice(reporter) , limits(limits) - , subtyping({builtinTypes, arena, normalizer, ice, scope}) + , subtyping({builtinTypes, arena, normalizer, ice}) , callLoc(callLocation) { } @@ -41,7 +41,7 @@ std::pair OverloadResolver::selectOverload(T { Subtyping::Variance variance = subtyping.variance; subtyping.variance = Subtyping::Variance::Contravariant; - SubtypingResult r = subtyping.isSubtype(argsPack, ftv->argTypes); + SubtypingResult r = subtyping.isSubtype(argsPack, ftv->argTypes, scope); subtyping.variance = variance; if (r.isSubtype) @@ -92,7 +92,7 @@ void OverloadResolver::resolve(TypeId fnTy, const TypePack* args, AstExpr* selfE std::optional OverloadResolver::testIsSubtype(const Location& location, TypeId subTy, TypeId superTy) { - auto r = subtyping.isSubtype(subTy, superTy); + auto r = subtyping.isSubtype(subTy, superTy, scope); ErrorVec errors; if (r.normalizationTooComplex) @@ -107,6 +107,7 @@ std::optional OverloadResolver::testIsSubtype(const Location& location case ErrorSuppression::NormalizationFailed: errors.emplace_back(location, NormalizationTooComplex{}); // intentionally fallthrough here since we couldn't prove this was error-suppressing + [[fallthrough]]; case ErrorSuppression::DoNotSuppress: errors.emplace_back(location, TypeMismatch{superTy, subTy}); break; @@ -121,7 +122,7 @@ std::optional OverloadResolver::testIsSubtype(const Location& location std::optional OverloadResolver::testIsSubtype(const Location& location, TypePackId subTy, TypePackId superTy) { - auto r = subtyping.isSubtype(subTy, superTy); + auto r = subtyping.isSubtype(subTy, superTy, scope); ErrorVec errors; if (r.normalizationTooComplex) @@ -136,6 +137,7 @@ std::optional OverloadResolver::testIsSubtype(const Location& location case ErrorSuppression::NormalizationFailed: errors.emplace_back(location, NormalizationTooComplex{}); // intentionally fallthrough here since we couldn't prove this was error-suppressing + [[fallthrough]]; case ErrorSuppression::DoNotSuppress: errors.emplace_back(location, TypePackMismatch{superTy, subTy}); break; @@ -206,7 +208,7 @@ std::pair OverloadResolver::checkOverload_ TypePackId typ = arena->addTypePack(*args); TypeId prospectiveFunction = arena->addType(FunctionType{typ, builtinTypes->anyTypePack}); - SubtypingResult sr = subtyping.isSubtype(fnTy, prospectiveFunction); + SubtypingResult sr = subtyping.isSubtype(fnTy, prospectiveFunction, scope); if (sr.isSubtype) return {Analysis::Ok, {}}; @@ -250,7 +252,7 @@ std::pair OverloadResolver::checkOverload_ // nil, then this overload does not match. for (size_t i = firstUnsatisfiedArgument; i < requiredHead.size(); ++i) { - if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i]).isSubtype) + 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}}; @@ -301,6 +303,7 @@ std::pair OverloadResolver::checkOverload_ case ErrorSuppression::NormalizationFailed: errors.emplace_back(argLocation, NormalizationTooComplex{}); // intentionally fallthrough here since we couldn't prove this was error-suppressing + [[fallthrough]]; case ErrorSuppression::DoNotSuppress: // TODO extract location from the SubtypingResult path and argExprs switch (reason.variance) diff --git a/Dependencies/luau/Luau/Analysis/src/Scope.cpp b/Dependencies/luau/Luau/Analysis/src/Scope.cpp index 791167c8..27894505 100644 --- a/Dependencies/luau/Luau/Analysis/src/Scope.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Scope.cpp @@ -2,7 +2,7 @@ #include "Luau/Scope.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); namespace Luau { @@ -184,7 +184,7 @@ std::optional Scope::linearSearchForBinding(const std::string& name, bo // Updates the `this` scope with the assignments from the `childScope` including ones that doesn't exist in `this`. void Scope::inheritAssignments(const ScopePtr& childScope) { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; for (const auto& [k, a] : childScope->lvalueTypes) @@ -194,7 +194,7 @@ void Scope::inheritAssignments(const ScopePtr& childScope) // Updates the `this` scope with the refinements from the `childScope` excluding ones that doesn't exist in `this`. void Scope::inheritRefinements(const ScopePtr& childScope) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { for (const auto& [k, a] : childScope->rvalueRefinements) { diff --git a/Dependencies/luau/Luau/Analysis/src/Simplify.cpp b/Dependencies/luau/Luau/Analysis/src/Simplify.cpp index ca9d5898..099e6a0d 100644 --- a/Dependencies/luau/Luau/Analysis/src/Simplify.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Simplify.cpp @@ -12,7 +12,7 @@ #include LUAU_FASTINT(LuauTypeReductionRecursionLimit) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8); namespace Luau @@ -141,6 +141,7 @@ Relation combine(Relation a, Relation b) case Relation::Superset: return Relation::Intersects; } + break; case Relation::Coincident: switch (b) { @@ -155,6 +156,7 @@ Relation combine(Relation a, Relation b) case Relation::Superset: return Relation::Intersects; } + break; case Relation::Superset: switch (b) { @@ -169,6 +171,7 @@ Relation combine(Relation a, Relation b) case Relation::Superset: return Relation::Superset; } + break; case Relation::Subset: switch (b) { @@ -183,6 +186,7 @@ Relation combine(Relation a, Relation b) case Relation::Superset: return Relation::Intersects; } + break; case Relation::Intersects: switch (b) { @@ -197,6 +201,7 @@ Relation combine(Relation a, Relation b) case Relation::Superset: return Relation::Intersects; } + break; } LUAU_UNREACHABLE(); @@ -1398,7 +1403,7 @@ TypeId TypeSimplifier::simplify(TypeId ty, DenseHashSet& seen) SimplifyResult simplifyIntersection(NotNull builtinTypes, NotNull arena, TypeId left, TypeId right) { - LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(FFlag::LuauSolverV2); TypeSimplifier s{builtinTypes, arena}; @@ -1413,7 +1418,7 @@ SimplifyResult simplifyIntersection(NotNull builtinTypes, NotNull< SimplifyResult simplifyIntersection(NotNull builtinTypes, NotNull arena, std::set parts) { - LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(FFlag::LuauSolverV2); TypeSimplifier s{builtinTypes, arena}; @@ -1424,7 +1429,7 @@ SimplifyResult simplifyIntersection(NotNull builtinTypes, NotNull< SimplifyResult simplifyUnion(NotNull builtinTypes, NotNull arena, TypeId left, TypeId right) { - LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(FFlag::LuauSolverV2); TypeSimplifier s{builtinTypes, arena}; diff --git a/Dependencies/luau/Luau/Analysis/src/Substitution.cpp b/Dependencies/luau/Luau/Analysis/src/Substitution.cpp index 3eeb2095..526d8212 100644 --- a/Dependencies/luau/Luau/Analysis/src/Substitution.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Substitution.cpp @@ -9,7 +9,7 @@ #include LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256); namespace Luau @@ -182,7 +182,7 @@ void Tarjan::visitChildren(TypeId ty, int index) LUAU_ASSERT(!ttv->boundTo); for (const auto& [name, prop] : ttv->props) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { visitChild(prop.readTy); visitChild(prop.writeTy); @@ -740,7 +740,7 @@ void Substitution::replaceChildren(TypeId ty) LUAU_ASSERT(!ttv->boundTo); for (auto& [name, prop] : ttv->props) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (prop.readTy) prop.readTy = replace(prop.readTy); diff --git a/Dependencies/luau/Luau/Analysis/src/Subtyping.cpp b/Dependencies/luau/Luau/Analysis/src/Subtyping.cpp index 4bbe2ecd..ee199b66 100644 --- a/Dependencies/luau/Luau/Analysis/src/Subtyping.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Subtyping.cpp @@ -333,28 +333,25 @@ Subtyping::Subtyping( NotNull builtinTypes, NotNull typeArena, NotNull normalizer, - NotNull iceReporter, - NotNull scope + NotNull iceReporter ) : builtinTypes(builtinTypes) , arena(typeArena) , normalizer(normalizer) , iceReporter(iceReporter) - , scope(scope) { } -SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy) +SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy, NotNull scope) { SubtypingEnvironment env; - SubtypingResult result = isCovariantWith(env, subTy, superTy); + SubtypingResult result = isCovariantWith(env, subTy, superTy, scope); for (const auto& [subTy, bounds] : env.mappedGenerics) { const auto& lb = bounds.lowerBound; const auto& ub = bounds.upperBound; - TypeId lowerBound = makeAggregateType(lb, builtinTypes->neverType); TypeId upperBound = makeAggregateType(ub, builtinTypes->unknownType); @@ -382,7 +379,7 @@ SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy) result.isSubtype = false; } - SubtypingResult boundsResult = isCovariantWith(env, lowerBound, upperBound); + SubtypingResult boundsResult = isCovariantWith(env, lowerBound, upperBound, scope); boundsResult.reasoning.clear(); result.andAlso(boundsResult); @@ -406,10 +403,10 @@ SubtypingResult Subtyping::isSubtype(TypeId subTy, TypeId superTy) return result; } -SubtypingResult Subtyping::isSubtype(TypePackId subTp, TypePackId superTp) +SubtypingResult Subtyping::isSubtype(TypePackId subTp, TypePackId superTp, NotNull scope) { SubtypingEnvironment env; - return isCovariantWith(env, subTp, superTp); + return isCovariantWith(env, subTp, superTp, scope); } SubtypingResult Subtyping::cache(SubtypingEnvironment& env, SubtypingResult result, TypeId subTy, TypeId superTy) @@ -443,7 +440,7 @@ struct SeenSetPopper }; } // namespace -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, TypeId superTy, NotNull scope) { subTy = follow(subTy); superTy = follow(superTy); @@ -501,20 +498,20 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub // tested as though it were its upper bounds. We do not yet support bounded // generics, so the upper bound is always unknown. if (auto subGeneric = get(subTy); subGeneric && subsumes(subGeneric->scope, scope)) - return isCovariantWith(env, builtinTypes->neverType, superTy); + return isCovariantWith(env, builtinTypes->neverType, superTy, scope); if (auto superGeneric = get(superTy); superGeneric && subsumes(superGeneric->scope, scope)) - return isCovariantWith(env, subTy, builtinTypes->unknownType); + return isCovariantWith(env, subTy, builtinTypes->unknownType, scope); SubtypingResult result; if (auto subUnion = get(subTy)) - result = isCovariantWith(env, subUnion, superTy); + result = isCovariantWith(env, subUnion, superTy, scope); else if (auto superUnion = get(superTy)) { - result = isCovariantWith(env, subTy, superUnion); + result = isCovariantWith(env, subTy, superUnion, scope); if (!result.isSubtype && !result.normalizationTooComplex) { - SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy)); + SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); if (semantic.isSubtype) { semantic.reasoning.clear(); @@ -523,13 +520,13 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub } } else if (auto superIntersection = get(superTy)) - result = isCovariantWith(env, subTy, superIntersection); + result = isCovariantWith(env, subTy, superIntersection, scope); else if (auto subIntersection = get(subTy)) { - result = isCovariantWith(env, subIntersection, superTy); + result = isCovariantWith(env, subIntersection, superTy, scope); if (!result.isSubtype && !result.normalizationTooComplex) { - SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy)); + SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); if (semantic.isSubtype) { // Clear the semantic reasoning, as any reasonings within @@ -550,7 +547,8 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub { // any = unknown | error, so we rewrite this to match. // As per TAPL: A | B <: T iff A <: T && B <: T - result = isCovariantWith(env, builtinTypes->unknownType, superTy).andAlso(isCovariantWith(env, builtinTypes->errorType, superTy)); + result = + isCovariantWith(env, builtinTypes->unknownType, superTy, scope).andAlso(isCovariantWith(env, builtinTypes->errorType, superTy, scope)); } else if (get(superTy)) { @@ -566,15 +564,15 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub else if (get(superTy)) result = {false}; else if (get(subTy)) - result = {false}; + result = {true}; else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p.first->ty, p.second->ty).withBothComponent(TypePath::TypeField::Negated); + result = isCovariantWith(env, p.first->ty, p.second->ty, scope).withBothComponent(TypePath::TypeField::Negated); else if (auto subNegation = get(subTy)) { - result = isCovariantWith(env, subNegation, superTy); + result = isCovariantWith(env, subNegation, superTy, scope); if (!result.isSubtype && !result.normalizationTooComplex) { - SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy)); + SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); if (semantic.isSubtype) { semantic.reasoning.clear(); @@ -584,10 +582,10 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub } else if (auto superNegation = get(superTy)) { - result = isCovariantWith(env, subTy, superNegation); + result = isCovariantWith(env, subTy, superNegation, scope); if (!result.isSubtype && !result.normalizationTooComplex) { - SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy)); + SubtypingResult semantic = isCovariantWith(env, normalizer->normalize(subTy), normalizer->normalize(superTy), scope); if (semantic.isSubtype) { semantic.reasoning.clear(); @@ -600,14 +598,14 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub if (auto substSubTy = env.applyMappedGenerics(builtinTypes, arena, subTy)) subTypeFunctionInstance = get(*substSubTy); - result = isCovariantWith(env, subTypeFunctionInstance, superTy); + result = isCovariantWith(env, subTypeFunctionInstance, superTy, scope); } else if (auto superTypeFunctionInstance = get(superTy)) { if (auto substSuperTy = env.applyMappedGenerics(builtinTypes, arena, superTy)) superTypeFunctionInstance = get(*substSuperTy); - result = isCovariantWith(env, subTy, superTypeFunctionInstance); + result = isCovariantWith(env, subTy, superTypeFunctionInstance, scope); } else if (auto subGeneric = get(subTy); subGeneric && variance == Variance::Covariant) { @@ -622,41 +620,41 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub result.isCacheable = false; } else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) { auto [subFunction, superPrimitive] = p; result.isSubtype = superPrimitive->type == PrimitiveType::Function; } else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, subTy, p.first, superTy, p.second); + result = isCovariantWith(env, subTy, p.first, superTy, p.second, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); else if (auto p = get2(subTy, superTy)) - result = isCovariantWith(env, p); + result = isCovariantWith(env, p, scope); assertReasoningValid(subTy, superTy, result, builtinTypes); return cache(env, result, subTy, superTy); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp, NotNull scope) { subTp = follow(subTp); superTp = follow(superTp); @@ -675,7 +673,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId // Match head types pairwise for (size_t i = 0; i < headSize; ++i) - results.push_back(isCovariantWith(env, subHead[i], superHead[i]).withBothComponent(TypePath::Index{i})); + results.push_back(isCovariantWith(env, subHead[i], superHead[i], scope).withBothComponent(TypePath::Index{i})); // Handle mismatched head sizes @@ -686,7 +684,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId if (auto vt = get(*subTail)) { for (size_t i = headSize; i < superHead.size(); ++i) - results.push_back(isCovariantWith(env, vt->ty, superHead[i]) + results.push_back(isCovariantWith(env, vt->ty, superHead[i], scope) .withSubPath(TypePath::PathBuilder().tail().variadic().build()) .withSuperComponent(TypePath::Index{i})); } @@ -704,7 +702,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId if (TypePackId* other = env.mappedGenericPacks.find(*subTail)) // TODO: TypePath can't express "slice of a pack + its tail". - results.push_back(isCovariantWith(env, *other, superTailPack).withSubComponent(TypePath::PackField::Tail)); + results.push_back(isCovariantWith(env, *other, superTailPack, scope).withSubComponent(TypePath::PackField::Tail)); else env.mappedGenericPacks.try_insert(*subTail, superTailPack); @@ -741,7 +739,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId if (auto vt = get(*superTail)) { for (size_t i = headSize; i < subHead.size(); ++i) - results.push_back(isCovariantWith(env, subHead[i], vt->ty) + results.push_back(isCovariantWith(env, subHead[i], vt->ty, scope) .withSubComponent(TypePath::Index{i}) .withSuperPath(TypePath::PathBuilder().tail().variadic().build())); } @@ -759,7 +757,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId if (TypePackId* other = env.mappedGenericPacks.find(*superTail)) // TODO: TypePath can't express "slice of a pack + its tail". - results.push_back(isContravariantWith(env, subTailPack, *other).withSuperComponent(TypePath::PackField::Tail)); + results.push_back(isContravariantWith(env, subTailPack, *other, scope).withSuperComponent(TypePath::PackField::Tail)); else env.mappedGenericPacks.try_insert(*superTail, subTailPack); @@ -794,7 +792,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId { // Variadic component is added by the isCovariantWith // implementation; no need to add it here. - results.push_back(isCovariantWith(env, p).withBothComponent(TypePath::PackField::Tail)); + results.push_back(isCovariantWith(env, p, scope).withBothComponent(TypePath::PackField::Tail)); } else if (auto p = get2(*subTail, *superTail)) { @@ -899,11 +897,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId } template -SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy) +SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull scope) { VarianceFlipper vf{&variance}; - SubtypingResult result = isCovariantWith(env, superTy, subTy); + SubtypingResult result = isCovariantWith(env, superTy, subTy, scope); if (result.reasoning.empty()) result.reasoning.insert(SubtypingReasoning{TypePath::kEmpty, TypePath::kEmpty, SubtypingVariance::Contravariant}); else @@ -931,9 +929,9 @@ SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, SubTy& } template -SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy) +SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, SubTy&& subTy, SuperTy&& superTy, NotNull scope) { - SubtypingResult result = isCovariantWith(env, subTy, superTy).andAlso(isContravariantWith(env, subTy, superTy)); + SubtypingResult result = isCovariantWith(env, subTy, superTy, scope).andAlso(isContravariantWith(env, subTy, superTy, scope)); if (result.reasoning.empty()) result.reasoning.insert(SubtypingReasoning{TypePath::kEmpty, TypePath::kEmpty, SubtypingVariance::Invariant}); @@ -948,19 +946,19 @@ SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, SubTy&& su } template -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TryPair& pair) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TryPair& pair, NotNull scope) { - return isCovariantWith(env, pair.first, pair.second); + return isCovariantWith(env, pair.first, pair.second, scope); } template -SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, const TryPair& pair) +SubtypingResult Subtyping::isContravariantWith(SubtypingEnvironment& env, const TryPair& pair, NotNull scope) { - return isContravariantWith(env, pair.first, pair.second); + return isContravariantWith(env, pair.first, pair.second, scope); } template -SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, const TryPair& pair) +SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, const TryPair& pair, NotNull scope) { return isInvariantWith(env, pair.first, pair.second); } @@ -996,13 +994,13 @@ SubtypingResult Subtyping::isInvariantWith(SubtypingEnvironment& env, const TryP * other just asks for boolean ~ 'b. We can dispatch this and only commit * boolean ~ 'b. This constraint does not teach us anything about 'a. */ -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const UnionType* superUnion, NotNull scope) { // As per TAPL: T <: A | B iff T <: A || T <: B for (TypeId ty : superUnion) { - SubtypingResult next = isCovariantWith(env, subTy, ty); + SubtypingResult next = isCovariantWith(env, subTy, ty, scope); if (next.isSubtype) return SubtypingResult{true}; } @@ -1015,37 +1013,37 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub return SubtypingResult{false}; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const UnionType* subUnion, TypeId superTy, NotNull scope) { // As per TAPL: A | B <: T iff A <: T && B <: T std::vector subtypings; size_t i = 0; for (TypeId ty : subUnion) - subtypings.push_back(isCovariantWith(env, ty, superTy).withSubComponent(TypePath::Index{i++})); + subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++})); return SubtypingResult::all(subtypings); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId subTy, const IntersectionType* superIntersection, NotNull scope) { // As per TAPL: T <: A & B iff T <: A && T <: B std::vector subtypings; size_t i = 0; for (TypeId ty : superIntersection) - subtypings.push_back(isCovariantWith(env, subTy, ty).withSuperComponent(TypePath::Index{i++})); + subtypings.push_back(isCovariantWith(env, subTy, ty, scope).withSuperComponent(TypePath::Index{i++})); return SubtypingResult::all(subtypings); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const IntersectionType* subIntersection, TypeId superTy, NotNull scope) { // As per TAPL: A & B <: T iff A <: T || B <: T std::vector subtypings; size_t i = 0; for (TypeId ty : subIntersection) - subtypings.push_back(isCovariantWith(env, ty, superTy).withSubComponent(TypePath::Index{i++})); + subtypings.push_back(isCovariantWith(env, ty, superTy, scope).withSubComponent(TypePath::Index{i++})); return SubtypingResult::any(subtypings); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NegationType* subNegation, TypeId superTy, NotNull scope) { TypeId negatedTy = follow(subNegation->ty); @@ -1057,17 +1055,17 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Nega if (is(negatedTy)) { // ¬never ~ unknown - result = isCovariantWith(env, builtinTypes->unknownType, superTy).withSubComponent(TypePath::TypeField::Negated); + result = isCovariantWith(env, builtinTypes->unknownType, superTy, scope).withSubComponent(TypePath::TypeField::Negated); } else if (is(negatedTy)) { // ¬unknown ~ never - result = isCovariantWith(env, builtinTypes->neverType, superTy).withSubComponent(TypePath::TypeField::Negated); + result = isCovariantWith(env, builtinTypes->neverType, superTy, scope).withSubComponent(TypePath::TypeField::Negated); } else if (is(negatedTy)) { // ¬any ~ any - result = isCovariantWith(env, negatedTy, superTy).withSubComponent(TypePath::TypeField::Negated); + result = isCovariantWith(env, negatedTy, superTy, scope).withSubComponent(TypePath::TypeField::Negated); } else if (auto u = get(negatedTy)) { @@ -1078,11 +1076,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Nega for (TypeId ty : u) { if (auto negatedPart = get(follow(ty))) - subtypings.push_back(isCovariantWith(env, negatedPart->ty, superTy).withSubComponent(TypePath::TypeField::Negated)); + subtypings.push_back(isCovariantWith(env, negatedPart->ty, superTy, scope).withSubComponent(TypePath::TypeField::Negated)); else { NegationType negatedTmp{ty}; - subtypings.push_back(isCovariantWith(env, &negatedTmp, superTy)); + subtypings.push_back(isCovariantWith(env, &negatedTmp, superTy, scope)); } } @@ -1097,11 +1095,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Nega for (TypeId ty : i) { if (auto negatedPart = get(follow(ty))) - subtypings.push_back(isCovariantWith(env, negatedPart->ty, superTy).withSubComponent(TypePath::TypeField::Negated)); + subtypings.push_back(isCovariantWith(env, negatedPart->ty, superTy, scope).withSubComponent(TypePath::TypeField::Negated)); else { NegationType negatedTmp{ty}; - subtypings.push_back(isCovariantWith(env, &negatedTmp, superTy)); + subtypings.push_back(isCovariantWith(env, &negatedTmp, superTy, scope)); } } @@ -1121,7 +1119,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Nega return result; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const NegationType* superNegation, NotNull scope) { TypeId negatedTy = follow(superNegation->ty); @@ -1130,17 +1128,17 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type if (is(negatedTy)) { // ¬never ~ unknown - result = isCovariantWith(env, subTy, builtinTypes->unknownType); + result = isCovariantWith(env, subTy, builtinTypes->unknownType, scope); } else if (is(negatedTy)) { // ¬unknown ~ never - result = isCovariantWith(env, subTy, builtinTypes->neverType); + result = isCovariantWith(env, subTy, builtinTypes->neverType, scope); } else if (is(negatedTy)) { // ¬any ~ any - result = isSubtype(subTy, negatedTy); + result = isSubtype(subTy, negatedTy, scope); } else if (auto u = get(negatedTy)) { @@ -1151,11 +1149,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type for (TypeId ty : u) { if (auto negatedPart = get(follow(ty))) - subtypings.push_back(isCovariantWith(env, subTy, negatedPart->ty)); + subtypings.push_back(isCovariantWith(env, subTy, negatedPart->ty, scope)); else { NegationType negatedTmp{ty}; - subtypings.push_back(isCovariantWith(env, subTy, &negatedTmp)); + subtypings.push_back(isCovariantWith(env, subTy, &negatedTmp, scope)); } } @@ -1170,11 +1168,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type for (TypeId ty : i) { if (auto negatedPart = get(follow(ty))) - subtypings.push_back(isCovariantWith(env, subTy, negatedPart->ty)); + subtypings.push_back(isCovariantWith(env, subTy, negatedPart->ty, scope)); else { NegationType negatedTmp{ty}; - subtypings.push_back(isCovariantWith(env, subTy, &negatedTmp)); + subtypings.push_back(isCovariantWith(env, subTy, &negatedTmp, scope)); } } @@ -1218,7 +1216,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type else if (auto p = get2(subTy, negatedTy)) result = {*p.first != *p.second}; else if (auto p = get2(subTy, negatedTy)) - result = SubtypingResult::negate(isCovariantWith(env, p.first, p.second)); + result = SubtypingResult::negate(isCovariantWith(env, p.first, p.second, scope)); else if (get2(subTy, negatedTy)) result = {true}; else if (is(negatedTy)) @@ -1229,12 +1227,22 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type return result.withSuperComponent(TypePath::TypeField::Negated); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const PrimitiveType* superPrim) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const PrimitiveType* subPrim, + const PrimitiveType* superPrim, + NotNull scope +) { return {subPrim->type == superPrim->type}; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const PrimitiveType* superPrim) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const SingletonType* subSingleton, + const PrimitiveType* superPrim, + NotNull scope +) { if (get(subSingleton) && superPrim->type == PrimitiveType::String) return {true}; @@ -1244,12 +1252,17 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Sing return {false}; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const SingletonType* superSingleton) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const SingletonType* subSingleton, + const SingletonType* superSingleton, + NotNull scope +) { return {*subSingleton == *superSingleton}; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const TableType* superTable, NotNull scope) { SubtypingResult result{true}; @@ -1260,23 +1273,23 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl { std::vector results; if (auto subIter = subTable->props.find(name); subIter != subTable->props.end()) - results.push_back(isCovariantWith(env, subIter->second, superProp, name)); + results.push_back(isCovariantWith(env, subIter->second, superProp, name, scope)); else if (subTable->indexer) { - if (isCovariantWith(env, builtinTypes->stringType, subTable->indexer->indexType).isSubtype) + if (isCovariantWith(env, builtinTypes->stringType, subTable->indexer->indexType, scope).isSubtype) { if (superProp.isShared()) - results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type()) + results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type(), scope) .withSubComponent(TypePath::TypeField::IndexResult) .withSuperComponent(TypePath::Property::read(name))); else { if (superProp.readTy) - results.push_back(isCovariantWith(env, subTable->indexer->indexResultType, *superProp.readTy) + results.push_back(isCovariantWith(env, subTable->indexer->indexResultType, *superProp.readTy, scope) .withSubComponent(TypePath::TypeField::IndexResult) .withSuperComponent(TypePath::Property::read(name))); if (superProp.writeTy) - results.push_back(isContravariantWith(env, subTable->indexer->indexResultType, *superProp.writeTy) + results.push_back(isContravariantWith(env, subTable->indexer->indexResultType, *superProp.writeTy, scope) .withSubComponent(TypePath::TypeField::IndexResult) .withSuperComponent(TypePath::Property::write(name))); } @@ -1292,7 +1305,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl if (superTable->indexer) { if (subTable->indexer) - result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer)); + result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope)); else return {false}; } @@ -1300,13 +1313,13 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl return result; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const MetatableType* superMt, NotNull scope) { - return isCovariantWith(env, subMt->table, superMt->table) - .andAlso(isCovariantWith(env, subMt->metatable, superMt->metatable).withBothComponent(TypePath::TypeField::Metatable)); + return isCovariantWith(env, subMt->table, superMt->table, scope) + .andAlso(isCovariantWith(env, subMt->metatable, superMt->metatable, scope).withBothComponent(TypePath::TypeField::Metatable)); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const MetatableType* subMt, const TableType* superTable, NotNull scope) { if (auto subTable = get(follow(subMt->table))) { @@ -1319,7 +1332,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta // that the metatable isn't a subtype of the table, even though they have // compatible properties/shapes. We'll revisit this later when we have a // better understanding of how important this is. - return isCovariantWith(env, subTable, superTable); + return isCovariantWith(env, subTable, superTable, scope); } else { @@ -1328,7 +1341,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Meta } } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const ClassType* subClass, const ClassType* superClass, NotNull scope) { return {isSubclass(subClass, superClass)}; } @@ -1338,7 +1351,8 @@ SubtypingResult Subtyping::isCovariantWith( TypeId subTy, const ClassType* subClass, TypeId superTy, - const TableType* superTable + const TableType* superTable, + NotNull scope ) { SubtypingResult result{true}; @@ -1349,7 +1363,7 @@ SubtypingResult Subtyping::isCovariantWith( { if (auto classProp = lookupClassProp(subClass, name)) { - result.andAlso(isCovariantWith(env, *classProp, prop, name)); + result.andAlso(isCovariantWith(env, *classProp, prop, name, scope)); } else { @@ -1363,19 +1377,26 @@ SubtypingResult Subtyping::isCovariantWith( return result; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const FunctionType* subFunction, const FunctionType* superFunction) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const FunctionType* subFunction, + const FunctionType* superFunction, + NotNull scope +) { SubtypingResult result; { - result.orElse(isContravariantWith(env, subFunction->argTypes, superFunction->argTypes).withBothComponent(TypePath::PackField::Arguments)); + result.orElse( + isContravariantWith(env, subFunction->argTypes, superFunction->argTypes, scope).withBothComponent(TypePath::PackField::Arguments) + ); } - result.andAlso(isCovariantWith(env, subFunction->retTypes, superFunction->retTypes).withBothComponent(TypePath::PackField::Returns)); + result.andAlso(isCovariantWith(env, subFunction->retTypes, superFunction->retTypes, scope).withBothComponent(TypePath::PackField::Returns)); return result; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableType* subTable, const PrimitiveType* superPrim, NotNull scope) { SubtypingResult result{false}; if (superPrim->type == PrimitiveType::Table) @@ -1384,7 +1405,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl return result; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const PrimitiveType* subPrim, const TableType* superTable, NotNull scope) { SubtypingResult result{false}; if (subPrim->type == PrimitiveType::String) @@ -1397,7 +1418,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim { if (auto stringTable = get(it->second.type())) result.orElse( - isCovariantWith(env, stringTable, superTable).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()) + isCovariantWith(env, stringTable, superTable, scope).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()) ); } } @@ -1412,7 +1433,12 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim return result; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const SingletonType* subSingleton, const TableType* superTable) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const SingletonType* subSingleton, + const TableType* superTable, + NotNull scope +) { SubtypingResult result{false}; if (auto stringleton = get(subSingleton)) @@ -1425,7 +1451,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Sing { if (auto stringTable = get(it->second.type())) result.orElse( - isCovariantWith(env, stringTable, superTable).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()) + isCovariantWith(env, stringTable, superTable, scope).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()) ); } } @@ -1434,25 +1460,38 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Sing return result; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TableIndexer& subIndexer, const TableIndexer& superIndexer) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const TableIndexer& subIndexer, + const TableIndexer& superIndexer, + NotNull scope +) { - return isInvariantWith(env, subIndexer.indexType, superIndexer.indexType) + return isInvariantWith(env, subIndexer.indexType, superIndexer.indexType, scope) .withBothComponent(TypePath::TypeField::IndexLookup) - .andAlso(isInvariantWith(env, subIndexer.indexResultType, superIndexer.indexResultType).withBothComponent(TypePath::TypeField::IndexResult)); + .andAlso( + isInvariantWith(env, subIndexer.indexResultType, superIndexer.indexResultType, scope).withBothComponent(TypePath::TypeField::IndexResult) + ); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Property& subProp, const Property& superProp, const std::string& name) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const Property& subProp, + const Property& superProp, + const std::string& name, + NotNull scope +) { SubtypingResult res{true}; if (superProp.isShared() && subProp.isShared()) - res.andAlso(isInvariantWith(env, subProp.type(), superProp.type()).withBothComponent(TypePath::Property::read(name))); + res.andAlso(isInvariantWith(env, subProp.type(), superProp.type(), scope).withBothComponent(TypePath::Property::read(name))); else { if (superProp.readTy.has_value() && subProp.readTy.has_value()) - res.andAlso(isCovariantWith(env, *subProp.readTy, *superProp.readTy).withBothComponent(TypePath::Property::read(name))); + res.andAlso(isCovariantWith(env, *subProp.readTy, *superProp.readTy, scope).withBothComponent(TypePath::Property::read(name))); if (superProp.writeTy.has_value() && subProp.writeTy.has_value()) - res.andAlso(isContravariantWith(env, *subProp.writeTy, *superProp.writeTy).withBothComponent(TypePath::Property::write(name))); + res.andAlso(isContravariantWith(env, *subProp.writeTy, *superProp.writeTy, scope).withBothComponent(TypePath::Property::write(name))); if (superProp.isReadWrite()) { @@ -1469,29 +1508,37 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prop SubtypingResult Subtyping::isCovariantWith( SubtypingEnvironment& env, const std::shared_ptr& subNorm, - const std::shared_ptr& superNorm + const std::shared_ptr& superNorm, + NotNull scope ) { if (!subNorm || !superNorm) return {false, true}; - SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops); - result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans)); - result.andAlso(isCovariantWith(env, subNorm->classes, superNorm->classes).orElse(isCovariantWith(env, subNorm->classes, superNorm->tables))); - result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors)); - result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils)); - result.andAlso(isCovariantWith(env, subNorm->numbers, superNorm->numbers)); - result.andAlso(isCovariantWith(env, subNorm->strings, superNorm->strings)); - result.andAlso(isCovariantWith(env, subNorm->strings, superNorm->tables)); - result.andAlso(isCovariantWith(env, subNorm->threads, superNorm->threads)); - result.andAlso(isCovariantWith(env, subNorm->buffers, superNorm->buffers)); - result.andAlso(isCovariantWith(env, subNorm->tables, superNorm->tables)); - result.andAlso(isCovariantWith(env, subNorm->functions, superNorm->functions)); + SubtypingResult result = isCovariantWith(env, subNorm->tops, superNorm->tops, scope); + result.andAlso(isCovariantWith(env, subNorm->booleans, superNorm->booleans, scope)); + result.andAlso( + isCovariantWith(env, subNorm->classes, superNorm->classes, scope).orElse(isCovariantWith(env, subNorm->classes, superNorm->tables, scope)) + ); + result.andAlso(isCovariantWith(env, subNorm->errors, superNorm->errors, scope)); + result.andAlso(isCovariantWith(env, subNorm->nils, superNorm->nils, scope)); + result.andAlso(isCovariantWith(env, subNorm->numbers, superNorm->numbers, scope)); + result.andAlso(isCovariantWith(env, subNorm->strings, superNorm->strings, scope)); + result.andAlso(isCovariantWith(env, subNorm->strings, superNorm->tables, scope)); + result.andAlso(isCovariantWith(env, subNorm->threads, superNorm->threads, scope)); + result.andAlso(isCovariantWith(env, subNorm->buffers, superNorm->buffers, scope)); + result.andAlso(isCovariantWith(env, subNorm->tables, superNorm->tables, scope)); + result.andAlso(isCovariantWith(env, subNorm->functions, superNorm->functions, scope)); // isCovariantWith(subNorm->tyvars, superNorm->tyvars); return result; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const NormalizedClassType& superClass) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const NormalizedClassType& subClass, + const NormalizedClassType& superClass, + NotNull scope +) { for (const auto& [subClassTy, _] : subClass.classes) { @@ -1499,13 +1546,13 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm for (const auto& [superClassTy, superNegations] : superClass.classes) { - result.orElse(isCovariantWith(env, subClassTy, superClassTy)); + result.orElse(isCovariantWith(env, subClassTy, superClassTy, scope)); if (!result.isSubtype) continue; for (TypeId negation : superNegations) { - result.andAlso(SubtypingResult::negate(isCovariantWith(env, subClassTy, negation))); + result.andAlso(SubtypingResult::negate(isCovariantWith(env, subClassTy, negation, scope))); if (result.isSubtype) break; } @@ -1518,14 +1565,19 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm return {true}; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NormalizedClassType& subClass, const TypeIds& superTables) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const NormalizedClassType& subClass, + const TypeIds& superTables, + NotNull scope +) { for (const auto& [subClassTy, _] : subClass.classes) { SubtypingResult result; for (TypeId superTableTy : superTables) - result.orElse(isCovariantWith(env, subClassTy, superTableTy)); + result.orElse(isCovariantWith(env, subClassTy, superTableTy, scope)); if (!result.isSubtype) return result; @@ -1534,13 +1586,23 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm return {true}; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const NormalizedStringType& superString) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const NormalizedStringType& subString, + const NormalizedStringType& superString, + NotNull scope +) { bool isSubtype = Luau::isSubtype(subString, superString); return {isSubtype}; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const NormalizedStringType& subString, const TypeIds& superTables) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const NormalizedStringType& subString, + const TypeIds& superTables, + NotNull scope +) { if (subString.isNever()) return {true}; @@ -1550,7 +1612,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm SubtypingResult result; for (const auto& superTable : superTables) { - result.orElse(isCovariantWith(env, builtinTypes->stringType, superTable)); + result.orElse(isCovariantWith(env, builtinTypes->stringType, superTable, scope)); if (result.isSubtype) return result; } @@ -1566,7 +1628,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm SubtypingResult result{true}; for (const auto& [_, subString] : subString.singletons) { - result.andAlso(isCovariantWith(env, subString, superTable)); + result.andAlso(isCovariantWith(env, subString, superTable, scope)); if (!result.isSubtype) break; } @@ -1583,7 +1645,8 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Norm SubtypingResult Subtyping::isCovariantWith( SubtypingEnvironment& env, const NormalizedFunctionType& subFunction, - const NormalizedFunctionType& superFunction + const NormalizedFunctionType& superFunction, + NotNull scope ) { if (subFunction.isNever()) @@ -1591,10 +1654,10 @@ SubtypingResult Subtyping::isCovariantWith( else if (superFunction.isTop) return {true}; else - return isCovariantWith(env, subFunction.parts, superFunction.parts); + return isCovariantWith(env, subFunction.parts, superFunction.parts, scope); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes) +SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeIds& subTypes, const TypeIds& superTypes, NotNull scope) { std::vector results; @@ -1602,15 +1665,20 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Type { results.emplace_back(); for (TypeId superTy : superTypes) - results.back().orElse(isCovariantWith(env, subTy, superTy)); + results.back().orElse(isCovariantWith(env, subTy, superTy, scope)); } return SubtypingResult::all(results); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const VariadicTypePack* subVariadic, const VariadicTypePack* superVariadic) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const VariadicTypePack* subVariadic, + const VariadicTypePack* superVariadic, + NotNull scope +) { - return isCovariantWith(env, subVariadic->ty, superVariadic->ty).withBothComponent(TypePath::TypeField::Variadic); + return isCovariantWith(env, subVariadic->ty, superVariadic->ty, scope).withBothComponent(TypePath::TypeField::Variadic); } bool Subtyping::bindGeneric(SubtypingEnvironment& env, TypeId subTy, TypeId superTy) @@ -1633,20 +1701,30 @@ bool Subtyping::bindGeneric(SubtypingEnvironment& env, TypeId subTy, TypeId supe return true; } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeFunctionInstanceType* subFunctionInstance, const TypeId superTy) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const TypeFunctionInstanceType* subFunctionInstance, + const TypeId superTy, + NotNull scope +) { // Reduce the type function instance - auto [ty, errors] = handleTypeFunctionReductionResult(subFunctionInstance); + auto [ty, errors] = handleTypeFunctionReductionResult(subFunctionInstance, scope); // If we return optional, that means the type function was irreducible - we can reduce that to never - return isCovariantWith(env, ty, superTy).withErrors(errors).withSubComponent(TypePath::Reduction{ty}); + return isCovariantWith(env, ty, superTy, scope).withErrors(errors).withSubComponent(TypePath::Reduction{ty}); } -SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const TypeId subTy, const TypeFunctionInstanceType* superFunctionInstance) +SubtypingResult Subtyping::isCovariantWith( + SubtypingEnvironment& env, + const TypeId subTy, + const TypeFunctionInstanceType* superFunctionInstance, + NotNull scope +) { // Reduce the type function instance - auto [ty, errors] = handleTypeFunctionReductionResult(superFunctionInstance); - return isCovariantWith(env, subTy, ty).withErrors(errors).withSuperComponent(TypePath::Reduction{ty}); + auto [ty, errors] = handleTypeFunctionReductionResult(superFunctionInstance, scope); + return isCovariantWith(env, subTy, ty, scope).withErrors(errors).withSuperComponent(TypePath::Reduction{ty}); } /* @@ -1681,7 +1759,7 @@ TypeId Subtyping::makeAggregateType(const Container& container, TypeId orElse) return arena->addType(T{std::vector(begin(container), end(container))}); } -std::pair Subtyping::handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance) +std::pair Subtyping::handleTypeFunctionReductionResult(const TypeFunctionInstanceType* functionInstance, NotNull scope) { TypeFunctionContext context{arena, builtinTypes, scope, normalizer, iceReporter, NotNull{&limits}}; TypeId function = arena->addType(*functionInstance); diff --git a/Dependencies/luau/Luau/Analysis/src/Symbol.cpp b/Dependencies/luau/Luau/Analysis/src/Symbol.cpp index 4b808f19..5e5b9d8c 100644 --- a/Dependencies/luau/Luau/Analysis/src/Symbol.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Symbol.cpp @@ -3,7 +3,7 @@ #include "Luau/Common.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) namespace Luau { @@ -14,7 +14,7 @@ bool Symbol::operator==(const Symbol& rhs) const return local == rhs.local; else if (global.value) return rhs.global.value && global == rhs.global.value; // Subtlety: AstName::operator==(const char*) uses strcmp, not pointer identity. - else if (FFlag::DebugLuauDeferredConstraintResolution) + else if (FFlag::LuauSolverV2) return !rhs.local && !rhs.global.value; // Reflexivity: we already know `this` Symbol is empty, so check that rhs is. else return false; diff --git a/Dependencies/luau/Luau/Analysis/src/ToDot.cpp b/Dependencies/luau/Luau/Analysis/src/ToDot.cpp index aa2dc1e3..4408063f 100644 --- a/Dependencies/luau/Luau/Analysis/src/ToDot.cpp +++ b/Dependencies/luau/Luau/Analysis/src/ToDot.cpp @@ -10,7 +10,7 @@ #include #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); namespace Luau { @@ -254,7 +254,7 @@ void StateDot::visitChildren(TypeId ty, int index) finishNodeLabel(ty); finishNode(); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (!get(t.lowerBound)) visitChild(t.lowerBound, index, "[lowerBound]"); diff --git a/Dependencies/luau/Luau/Analysis/src/ToString.cpp b/Dependencies/luau/Luau/Analysis/src/ToString.cpp index 879ddfab..f0850835 100644 --- a/Dependencies/luau/Luau/Analysis/src/ToString.cpp +++ b/Dependencies/luau/Luau/Analysis/src/ToString.cpp @@ -19,7 +19,7 @@ #include #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) /* * Enables increasing levels of verbosity for Luau type names when stringifying. @@ -83,10 +83,10 @@ struct FindCyclicTypes final : TypeVisitor if (!visited.insert(ty)) return false; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // TODO: Replace these if statements with assert()s when we - // delete FFlag::DebugLuauDeferredConstraintResolution. + // delete FFlag::LuauSolverV2. // // When the old solver is used, these pointers are always // unused. When the new solver is used, they are never null. @@ -411,7 +411,7 @@ struct TypeStringifier void stringify(const std::string& name, const Property& prop) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return _newStringify(name, prop); emitKey(name); @@ -473,7 +473,7 @@ struct TypeStringifier // TODO: ftv.lowerBound and ftv.upperBound should always be non-nil when // the new solver is used. This can be replaced with an assert. - if (FFlag::DebugLuauDeferredConstraintResolution && ftv.lowerBound && ftv.upperBound) + if (FFlag::LuauSolverV2 && ftv.lowerBound && ftv.upperBound) { const TypeId lowerBound = follow(ftv.lowerBound); const TypeId upperBound = follow(ftv.upperBound); @@ -511,7 +511,7 @@ struct TypeStringifier if (FInt::DebugLuauVerboseTypeNames >= 2) { state.emit("-"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) state.emitLevel(ftv.scope); else state.emit(ftv.level); @@ -540,7 +540,7 @@ struct TypeStringifier if (FInt::DebugLuauVerboseTypeNames >= 2) { state.emit("-"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) state.emitLevel(gtv.scope); else state.emit(gtv.level); @@ -643,7 +643,7 @@ struct TypeStringifier state.emit(">"); } - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (ftv.isCheckedFunction) state.emit("@checked "); @@ -726,10 +726,10 @@ struct TypeStringifier std::string openbrace = "@@@"; std::string closedbrace = "@@@?!"; - switch (state.opts.hideTableKind ? (FFlag::DebugLuauDeferredConstraintResolution ? TableState::Sealed : TableState::Unsealed) : ttv.state) + switch (state.opts.hideTableKind ? (FFlag::LuauSolverV2 ? TableState::Sealed : TableState::Unsealed) : ttv.state) { case TableState::Sealed: - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { openbrace = "{"; closedbrace = "}"; @@ -742,7 +742,7 @@ struct TypeStringifier } break; case TableState::Unsealed: - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { state.result.invalid = true; openbrace = "{|"; @@ -1200,7 +1200,7 @@ struct TypePackStringifier if (FInt::DebugLuauVerboseTypeNames >= 2) { state.emit("-"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) state.emitLevel(pack.scope); else state.emit(pack.level); @@ -1219,7 +1219,7 @@ struct TypePackStringifier if (FInt::DebugLuauVerboseTypeNames >= 2) { state.emit("-"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) state.emitLevel(pack.scope); else state.emit(pack.level); diff --git a/Dependencies/luau/Luau/Analysis/src/Type.cpp b/Dependencies/luau/Luau/Analysis/src/Type.cpp index ffc4a97e..b024fdd2 100644 --- a/Dependencies/luau/Luau/Analysis/src/Type.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Type.cpp @@ -752,7 +752,7 @@ TypeId Property::type() const void Property::setType(TypeId ty) { readTy = ty; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) writeTy = ty; } diff --git a/Dependencies/luau/Luau/Analysis/src/TypeChecker2.cpp b/Dependencies/luau/Luau/Analysis/src/TypeChecker2.cpp index 5020adc6..7023fba9 100644 --- a/Dependencies/luau/Luau/Analysis/src/TypeChecker2.cpp +++ b/Dependencies/luau/Luau/Analysis/src/TypeChecker2.cpp @@ -76,6 +76,37 @@ struct StackPusher } }; +struct PropertyTypes +{ + // a vector of all the types assigned to the given property. + std::vector typesOfProp; + + // a vector of all the types that are missing the given property. + std::vector missingProp; + + bool foundOneProp() const + { + return !typesOfProp.empty(); + } + + bool noneMissingProp() const + { + return missingProp.empty(); + } + + bool foundMissingProp() const + { + return !missingProp.empty(); + } +}; + +struct PropertyType +{ + NormalizationResult present; + std::optional result; +}; + + static std::optional getIdentifierOfBaseVar(AstExpr* node) { if (AstExprGlobal* expr = node->as()) @@ -233,2955 +264,2907 @@ struct InternalTypeFunctionFinder : TypeOnceVisitor } }; -struct TypeChecker2 +void check( + NotNull builtinTypes, + NotNull unifierState, + NotNull limits, + DcrLogger* logger, + const SourceModule& sourceModule, + Module* module +) { - NotNull builtinTypes; - DcrLogger* logger; - const NotNull limits; - const NotNull ice; - const SourceModule* sourceModule; - Module* module; + LUAU_TIMETRACE_SCOPE("check", "Typechecking"); + + TypeChecker2 typeChecker{builtinTypes, unifierState, limits, logger, &sourceModule, module}; - TypeContext typeContext = TypeContext::Default; - std::vector> stack; - std::vector functionDeclStack; + typeChecker.visit(sourceModule.root); + + // if the only error we're producing is one about constraint solving being incomplete, we can silence it. + // this means we won't give this warning if types seem totally nonsensical, but there are no other errors. + // this is probably, on the whole, a good decision to not annoy users though. + if (module->errors.size() == 1 && get(module->errors[0])) + module->errors.clear(); - DenseHashSet seenTypeFunctionInstances{nullptr}; + unfreeze(module->interfaceTypes); + copyErrors(module->errors, module->interfaceTypes, builtinTypes); + freeze(module->interfaceTypes); +} - Normalizer normalizer; - Subtyping _subtyping; - NotNull subtyping; +TypeChecker2::TypeChecker2( + NotNull builtinTypes, + NotNull unifierState, + NotNull limits, + DcrLogger* logger, + const SourceModule* sourceModule, + Module* module +) + : builtinTypes(builtinTypes) + , logger(logger) + , limits(limits) + , ice(unifierState->iceHandler) + , sourceModule(sourceModule) + , module(module) + , normalizer{&module->internalTypes, builtinTypes, unifierState, /* cacheInhabitance */ true} + , _subtyping{builtinTypes, NotNull{&module->internalTypes}, NotNull{&normalizer}, NotNull{unifierState->iceHandler}} + , subtyping(&_subtyping) +{ +} - TypeChecker2(NotNull builtinTypes, NotNull unifierState, NotNull limits, DcrLogger* logger, - const SourceModule* sourceModule, Module* module) - : builtinTypes(builtinTypes) - , logger(logger) - , limits(limits) - , ice(unifierState->iceHandler) - , sourceModule(sourceModule) - , module(module) - , normalizer{&module->internalTypes, builtinTypes, unifierState, /* cacheInhabitance */ true} - , _subtyping{builtinTypes, NotNull{&module->internalTypes}, NotNull{&normalizer}, NotNull{unifierState->iceHandler}, - NotNull{module->getModuleScope().get()}} - , subtyping(&_subtyping) +bool TypeChecker2::allowsNoReturnValues(const TypePackId tp) +{ + for (TypeId ty : tp) { + if (!get(follow(ty))) + return false; } - static bool allowsNoReturnValues(const TypePackId tp) - { - for (TypeId ty : tp) - { - if (!get(follow(ty))) - return false; - } + return true; +} - return true; +Location TypeChecker2::getEndLocation(const AstExprFunction* function) +{ + Location loc = function->location; + if (loc.begin.line != loc.end.line) + { + Position begin = loc.end; + begin.column = std::max(0u, begin.column - 3); + loc = Location(begin, 3); } - static Location getEndLocation(const AstExprFunction* function) + return loc; +} + +bool TypeChecker2::isErrorCall(const AstExprCall* call) +{ + const AstExprGlobal* global = call->func->as(); + if (!global) + return false; + + if (global->name == "error") + return true; + else if (global->name == "assert") { - Location loc = function->location; - if (loc.begin.line != loc.end.line) - { - Position begin = loc.end; - begin.column = std::max(0u, begin.column - 3); - loc = Location(begin, 3); - } + // assert() will error because it is missing the first argument + if (call->args.size == 0) + return true; - return loc; + if (AstExprConstantBool* expr = call->args.data[0]->as()) + if (!expr->value) + return true; } - bool isErrorCall(const AstExprCall* call) - { - const AstExprGlobal* global = call->func->as(); - if (!global) - return false; + return false; +} - if (global->name == "error") - return true; - else if (global->name == "assert") +bool TypeChecker2::hasBreak(AstStat* node) +{ + if (AstStatBlock* stat = node->as()) + { + for (size_t i = 0; i < stat->body.size; ++i) { - // assert() will error because it is missing the first argument - if (call->args.size == 0) + if (hasBreak(stat->body.data[i])) return true; - - if (AstExprConstantBool* expr = call->args.data[0]->as()) - if (!expr->value) - return true; } return false; } - bool hasBreak(AstStat* node) - { - if (AstStatBlock* stat = node->as()) - { - for (size_t i = 0; i < stat->body.size; ++i) - { - if (hasBreak(stat->body.data[i])) - return true; - } - - return false; - } + if (node->is()) + return true; - if (node->is()) + if (AstStatIf* stat = node->as()) + { + if (hasBreak(stat->thenbody)) return true; - if (AstStatIf* stat = node->as()) - { - if (hasBreak(stat->thenbody)) - return true; - - if (stat->elsebody && hasBreak(stat->elsebody)) - return true; - - return false; - } + if (stat->elsebody && hasBreak(stat->elsebody)) + return true; return false; } - // returns the last statement before the block implicitly exits, or nullptr if the block does not implicitly exit - // i.e. returns nullptr if the block returns properly or never returns - const AstStat* getFallthrough(const AstStat* node) + return false; +} + +const AstStat* TypeChecker2::getFallthrough(const AstStat* node) +{ + if (const AstStatBlock* stat = node->as()) { - if (const AstStatBlock* stat = node->as()) + if (stat->body.size == 0) + return stat; + + for (size_t i = 0; i < stat->body.size - 1; ++i) { - if (stat->body.size == 0) - return stat; + if (getFallthrough(stat->body.data[i]) == nullptr) + return nullptr; + } - for (size_t i = 0; i < stat->body.size - 1; ++i) - { - if (getFallthrough(stat->body.data[i]) == nullptr) - return nullptr; - } + return getFallthrough(stat->body.data[stat->body.size - 1]); + } - return getFallthrough(stat->body.data[stat->body.size - 1]); - } + if (const AstStatIf* stat = node->as()) + { + if (const AstStat* thenf = getFallthrough(stat->thenbody)) + return thenf; - if (const AstStatIf* stat = node->as()) + if (stat->elsebody) { - if (const AstStat* thenf = getFallthrough(stat->thenbody)) - return thenf; - - if (stat->elsebody) - { - if (const AstStat* elsef = getFallthrough(stat->elsebody)) - return elsef; + if (const AstStat* elsef = getFallthrough(stat->elsebody)) + return elsef; - return nullptr; - } - else - return stat; + return nullptr; } + else + return stat; + } - if (node->is()) + if (node->is()) + return nullptr; + + if (const AstStatExpr* stat = node->as()) + { + if (AstExprCall* call = stat->expr->as(); call && isErrorCall(call)) return nullptr; - if (const AstStatExpr* stat = node->as()) + return stat; + } + + if (const AstStatWhile* stat = node->as()) + { + if (AstExprConstantBool* expr = stat->condition->as()) { - if (AstExprCall* call = stat->expr->as(); call && isErrorCall(call)) + if (expr->value && !hasBreak(stat->body)) return nullptr; - - return stat; } - if (const AstStatWhile* stat = node->as()) - { - if (AstExprConstantBool* expr = stat->condition->as()) - { - if (expr->value && !hasBreak(stat->body)) - return nullptr; - } - - return node; - } + return node; + } - if (const AstStatRepeat* stat = node->as()) + if (const AstStatRepeat* stat = node->as()) + { + if (AstExprConstantBool* expr = stat->condition->as()) { - if (AstExprConstantBool* expr = stat->condition->as()) - { - if (!expr->value && !hasBreak(stat->body)) - return nullptr; - } - - if (getFallthrough(stat->body) == nullptr) + if (!expr->value && !hasBreak(stat->body)) return nullptr; - - return node; } + if (getFallthrough(stat->body) == nullptr) + return nullptr; + return node; } - std::optional pushStack(AstNode* node) - { - if (Scope** scope = module->astScopes.find(node)) - return StackPusher{stack, *scope}; - else - return std::nullopt; - } + return node; +} - void checkForInternalTypeFunction(TypeId ty, Location location) - { - InternalTypeFunctionFinder finder(functionDeclStack); - finder.traverse(ty); +std::optional TypeChecker2::pushStack(AstNode* node) +{ + if (Scope** scope = module->astScopes.find(node)) + return StackPusher{stack, *scope}; + else + return std::nullopt; +} - for (TypeId internal : finder.internalFunctions) - reportError(WhereClauseNeeded{internal}, location); +void TypeChecker2::checkForInternalTypeFunction(TypeId ty, Location location) +{ + InternalTypeFunctionFinder finder(functionDeclStack); + finder.traverse(ty); - for (TypePackId internal : finder.internalPackFunctions) - reportError(PackWhereClauseNeeded{internal}, location); - } + for (TypeId internal : finder.internalFunctions) + reportError(WhereClauseNeeded{internal}, location); - TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location) - { - if (seenTypeFunctionInstances.find(instance)) - return instance; - seenTypeFunctionInstances.insert(instance); + for (TypePackId internal : finder.internalPackFunctions) + reportError(PackWhereClauseNeeded{internal}, location); +} - ErrorVec errors = reduceTypeFunctions( - instance, - location, - TypeFunctionContext{NotNull{&module->internalTypes}, builtinTypes, stack.back(), NotNull{&normalizer}, ice, limits}, - true - ) - .errors; - if (!isErrorSuppressing(location, instance)) - reportErrors(std::move(errors)); +TypeId TypeChecker2::checkForTypeFunctionInhabitance(TypeId instance, Location location) +{ + if (seenTypeFunctionInstances.find(instance)) return instance; - } - - TypePackId lookupPack(AstExpr* expr) - { - // If a type isn't in the type graph, it probably means that a recursion limit was exceeded. - // We'll just return anyType in these cases. Typechecking against any is very fast and this - // allows us not to think about this very much in the actual typechecking logic. - TypePackId* tp = module->astTypePacks.find(expr); - if (tp) - return follow(*tp); - else - return builtinTypes->anyTypePack; - } + seenTypeFunctionInstances.insert(instance); - TypeId lookupType(AstExpr* expr) - { - // If a type isn't in the type graph, it probably means that a recursion limit was exceeded. - // We'll just return anyType in these cases. Typechecking against any is very fast and this - // allows us not to think about this very much in the actual typechecking logic. - TypeId* ty = module->astTypes.find(expr); - if (ty) - return checkForTypeFunctionInhabitance(follow(*ty), expr->location); + ErrorVec errors = reduceTypeFunctions( + instance, + location, + TypeFunctionContext{NotNull{&module->internalTypes}, builtinTypes, stack.back(), NotNull{&normalizer}, ice, limits}, + true + ) + .errors; + if (!isErrorSuppressing(location, instance)) + reportErrors(std::move(errors)); + return instance; +} - TypePackId* tp = module->astTypePacks.find(expr); - if (tp) - return checkForTypeFunctionInhabitance(flattenPack(*tp), expr->location); +TypePackId TypeChecker2::lookupPack(AstExpr* expr) +{ + // If a type isn't in the type graph, it probably means that a recursion limit was exceeded. + // We'll just return anyType in these cases. Typechecking against any is very fast and this + // allows us not to think about this very much in the actual typechecking logic. + TypePackId* tp = module->astTypePacks.find(expr); + if (tp) + return follow(*tp); + else + return builtinTypes->anyTypePack; +} - return builtinTypes->anyType; - } +TypeId TypeChecker2::lookupType(AstExpr* expr) +{ + // If a type isn't in the type graph, it probably means that a recursion limit was exceeded. + // We'll just return anyType in these cases. Typechecking against any is very fast and this + // allows us not to think about this very much in the actual typechecking logic. + TypeId* ty = module->astTypes.find(expr); + if (ty) + return checkForTypeFunctionInhabitance(follow(*ty), expr->location); + + TypePackId* tp = module->astTypePacks.find(expr); + if (tp) + return checkForTypeFunctionInhabitance(flattenPack(*tp), expr->location); + + return builtinTypes->anyType; +} - TypeId lookupAnnotation(AstType* annotation) +TypeId TypeChecker2::lookupAnnotation(AstType* annotation) +{ + if (FFlag::DebugLuauMagicTypes) { - if (FFlag::DebugLuauMagicTypes) + if (auto ref = annotation->as(); ref && ref->name == "_luau_print" && ref->parameters.size > 0) { - if (auto ref = annotation->as(); ref && ref->name == "_luau_print" && ref->parameters.size > 0) + if (auto ann = ref->parameters.data[0].type) { - if (auto ann = ref->parameters.data[0].type) - { - TypeId argTy = lookupAnnotation(ref->parameters.data[0].type); - luauPrintLine(format( - "_luau_print (%d, %d): %s\n", annotation->location.begin.line, annotation->location.begin.column, toString(argTy).c_str() - )); - return follow(argTy); - } + TypeId argTy = lookupAnnotation(ref->parameters.data[0].type); + luauPrintLine( + format("_luau_print (%d, %d): %s\n", annotation->location.begin.line, annotation->location.begin.column, toString(argTy).c_str()) + ); + return follow(argTy); } } - - TypeId* ty = module->astResolvedTypes.find(annotation); - LUAU_ASSERT(ty); - return checkForTypeFunctionInhabitance(follow(*ty), annotation->location); } - std::optional lookupPackAnnotation(AstTypePack* annotation) - { - TypePackId* tp = module->astResolvedTypePacks.find(annotation); - if (tp != nullptr) - return {follow(*tp)}; - return {}; - } + TypeId* ty = module->astResolvedTypes.find(annotation); + LUAU_ASSERT(ty); + return checkForTypeFunctionInhabitance(follow(*ty), annotation->location); +} - TypeId lookupExpectedType(AstExpr* expr) - { - if (TypeId* ty = module->astExpectedTypes.find(expr)) - return follow(*ty); +std::optional TypeChecker2::lookupPackAnnotation(AstTypePack* annotation) +{ + TypePackId* tp = module->astResolvedTypePacks.find(annotation); + if (tp != nullptr) + return {follow(*tp)}; + return {}; +} - return builtinTypes->anyType; - } +TypeId TypeChecker2::lookupExpectedType(AstExpr* expr) +{ + if (TypeId* ty = module->astExpectedTypes.find(expr)) + return follow(*ty); - TypePackId lookupExpectedPack(AstExpr* expr, TypeArena& arena) - { - if (TypeId* ty = module->astExpectedTypes.find(expr)) - return arena.addTypePack(TypePack{{follow(*ty)}, std::nullopt}); + return builtinTypes->anyType; +} - return builtinTypes->anyTypePack; - } +TypePackId TypeChecker2::lookupExpectedPack(AstExpr* expr, TypeArena& arena) +{ + if (TypeId* ty = module->astExpectedTypes.find(expr)) + return arena.addTypePack(TypePack{{follow(*ty)}, std::nullopt}); - TypePackId reconstructPack(AstArray exprs, TypeArena& arena) - { - if (exprs.size == 0) - return arena.addTypePack(TypePack{{}, std::nullopt}); + return builtinTypes->anyTypePack; +} - std::vector head; +TypePackId TypeChecker2::reconstructPack(AstArray exprs, TypeArena& arena) +{ + if (exprs.size == 0) + return arena.addTypePack(TypePack{{}, std::nullopt}); - for (size_t i = 0; i < exprs.size - 1; ++i) - { - head.push_back(lookupType(exprs.data[i])); - } + std::vector head; - TypePackId tail = lookupPack(exprs.data[exprs.size - 1]); - return arena.addTypePack(TypePack{head, tail}); + for (size_t i = 0; i < exprs.size - 1; ++i) + { + head.push_back(lookupType(exprs.data[i])); } - Scope* findInnermostScope(Location location) - { - Scope* bestScope = module->getModuleScope().get(); + TypePackId tail = lookupPack(exprs.data[exprs.size - 1]); + return arena.addTypePack(TypePack{head, tail}); +} + +Scope* TypeChecker2::findInnermostScope(Location location) +{ + Scope* bestScope = module->getModuleScope().get(); - bool didNarrow; - do + bool didNarrow; + do + { + didNarrow = false; + for (auto scope : bestScope->children) { - didNarrow = false; - for (auto scope : bestScope->children) + if (scope->location.encloses(location)) { - if (scope->location.encloses(location)) - { - bestScope = scope.get(); - didNarrow = true; - break; - } + bestScope = scope.get(); + didNarrow = true; + break; } - } while (didNarrow && bestScope->children.size() > 0); - - return bestScope; - } - - void visit(AstStat* stat) - { - auto pusher = pushStack(stat); - - if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto f = stat->as()) - return visit(f); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else if (auto s = stat->as()) - return visit(s); - else - LUAU_ASSERT(!"TypeChecker2 encountered an unknown node type"); - } + } + } while (didNarrow && bestScope->children.size() > 0); - void visit(AstStatBlock* block) - { - auto StackPusher = pushStack(block); + return bestScope; +} - for (AstStat* statement : block->body) - visit(statement); - } +void TypeChecker2::visit(AstStat* stat) +{ + auto pusher = pushStack(stat); + + if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto f = stat->as()) + return visit(f); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else if (auto s = stat->as()) + return visit(s); + else + LUAU_ASSERT(!"TypeChecker2 encountered an unknown node type"); +} - void visit(AstStatIf* ifStatement) - { - { - InConditionalContext flipper{&typeContext}; - visit(ifStatement->condition, ValueContext::RValue); - } +void TypeChecker2::visit(AstStatBlock* block) +{ + auto StackPusher = pushStack(block); - visit(ifStatement->thenbody); - if (ifStatement->elsebody) - visit(ifStatement->elsebody); - } + for (AstStat* statement : block->body) + visit(statement); +} - void visit(AstStatWhile* whileStatement) +void TypeChecker2::visit(AstStatIf* ifStatement) +{ { - visit(whileStatement->condition, ValueContext::RValue); - visit(whileStatement->body); + InConditionalContext flipper{&typeContext}; + visit(ifStatement->condition, ValueContext::RValue); } - void visit(AstStatRepeat* repeatStatement) - { - visit(repeatStatement->body); - visit(repeatStatement->condition, ValueContext::RValue); - } + visit(ifStatement->thenbody); + if (ifStatement->elsebody) + visit(ifStatement->elsebody); +} + +void TypeChecker2::visit(AstStatWhile* whileStatement) +{ + visit(whileStatement->condition, ValueContext::RValue); + visit(whileStatement->body); +} - void visit(AstStatBreak*) {} +void TypeChecker2::visit(AstStatRepeat* repeatStatement) +{ + visit(repeatStatement->body); + visit(repeatStatement->condition, ValueContext::RValue); +} - void visit(AstStatContinue*) {} +void TypeChecker2::visit(AstStatBreak*) {} - void visit(AstStatReturn* ret) - { - Scope* scope = findInnermostScope(ret->location); - TypePackId expectedRetType = scope->returnType; +void TypeChecker2::visit(AstStatContinue*) {} - TypeArena* arena = &module->internalTypes; - TypePackId actualRetType = reconstructPack(ret->list, *arena); +void TypeChecker2::visit(AstStatReturn* ret) +{ + Scope* scope = findInnermostScope(ret->location); + TypePackId expectedRetType = scope->returnType; - testIsSubtype(actualRetType, expectedRetType, ret->location); + TypeArena* arena = &module->internalTypes; + TypePackId actualRetType = reconstructPack(ret->list, *arena); - for (AstExpr* expr : ret->list) - visit(expr, ValueContext::RValue); - } + testIsSubtype(actualRetType, expectedRetType, ret->location); - void visit(AstStatExpr* expr) - { - visit(expr->expr, ValueContext::RValue); - } + for (AstExpr* expr : ret->list) + visit(expr, ValueContext::RValue); +} + +void TypeChecker2::visit(AstStatExpr* expr) +{ + visit(expr->expr, ValueContext::RValue); +} - void visit(AstStatLocal* local) +void TypeChecker2::visit(AstStatLocal* local) +{ + size_t count = std::max(local->values.size, local->vars.size); + for (size_t i = 0; i < count; ++i) { - size_t count = std::max(local->values.size, local->vars.size); - for (size_t i = 0; i < count; ++i) + AstExpr* value = i < local->values.size ? local->values.data[i] : nullptr; + const bool isPack = value && (value->is() || value->is()); + + if (value) + visit(value, ValueContext::RValue); + + if (i != local->values.size - 1 || !isPack) { - AstExpr* value = i < local->values.size ? local->values.data[i] : nullptr; - const bool isPack = value && (value->is() || value->is()); + AstLocal* var = i < local->vars.size ? local->vars.data[i] : nullptr; - if (value) - visit(value, ValueContext::RValue); + if (var && var->annotation) + { + TypeId annotationType = lookupAnnotation(var->annotation); + TypeId valueType = value ? lookupType(value) : nullptr; + if (valueType) + testIsSubtype(valueType, annotationType, value->location); + + visit(var->annotation); + } + } + else if (value) + { + TypePackId valuePack = lookupPack(value); + TypePack valueTypes; + if (i < local->vars.size) + valueTypes = extendTypePack(module->internalTypes, builtinTypes, valuePack, local->vars.size - i); - if (i != local->values.size - 1 || !isPack) + Location errorLocation; + for (size_t j = i; j < local->vars.size; ++j) { - AstLocal* var = i < local->vars.size ? local->vars.data[i] : nullptr; + if (j - i >= valueTypes.head.size()) + { + errorLocation = local->vars.data[j]->location; + break; + } - if (var && var->annotation) + AstLocal* var = local->vars.data[j]; + if (var->annotation) { - TypeId annotationType = lookupAnnotation(var->annotation); - TypeId valueType = value ? lookupType(value) : nullptr; - if (valueType) - testIsSubtype(valueType, annotationType, value->location); + TypeId varType = lookupAnnotation(var->annotation); + testIsSubtype(valueTypes.head[j - i], varType, value->location); visit(var->annotation); } } - else if (value) + + if (valueTypes.head.size() < local->vars.size - i) { - TypePackId valuePack = lookupPack(value); - TypePack valueTypes; - if (i < local->vars.size) - valueTypes = extendTypePack(module->internalTypes, builtinTypes, valuePack, local->vars.size - i); + reportError( + CountMismatch{ + // We subtract 1 here because the final AST + // expression is not worth one value. It is worth 0 + // or more depending on valueTypes.head + local->values.size - 1 + valueTypes.head.size(), + std::nullopt, + local->vars.size, + local->values.data[local->values.size - 1]->is() ? CountMismatch::FunctionResult : CountMismatch::ExprListResult, + }, + errorLocation + ); + } + } + } +} - Location errorLocation; - for (size_t j = i; j < local->vars.size; ++j) - { - if (j - i >= valueTypes.head.size()) - { - errorLocation = local->vars.data[j]->location; - break; - } - - AstLocal* var = local->vars.data[j]; - if (var->annotation) - { - TypeId varType = lookupAnnotation(var->annotation); - testIsSubtype(valueTypes.head[j - i], varType, value->location); - - visit(var->annotation); - } - } +void TypeChecker2::visit(AstStatFor* forStatement) +{ + if (forStatement->var->annotation) + { + visit(forStatement->var->annotation); - if (valueTypes.head.size() < local->vars.size - i) - { - reportError( - CountMismatch{ - // We subtract 1 here because the final AST - // expression is not worth one value. It is worth 0 - // or more depending on valueTypes.head - local->values.size - 1 + valueTypes.head.size(), - std::nullopt, - local->vars.size, - local->values.data[local->values.size - 1]->is() ? CountMismatch::FunctionResult - : CountMismatch::ExprListResult, - }, - errorLocation - ); - } - } - } + TypeId annotatedType = lookupAnnotation(forStatement->var->annotation); + testIsSubtype(builtinTypes->numberType, annotatedType, forStatement->var->location); } - void visit(AstStatFor* forStatement) + auto checkNumber = [this](AstExpr* expr) { - if (forStatement->var->annotation) - { - visit(forStatement->var->annotation); - - TypeId annotatedType = lookupAnnotation(forStatement->var->annotation); - testIsSubtype(builtinTypes->numberType, annotatedType, forStatement->var->location); - } + if (!expr) + return; - auto checkNumber = [this](AstExpr* expr) - { - if (!expr) - return; + visit(expr, ValueContext::RValue); + testIsSubtype(lookupType(expr), builtinTypes->numberType, expr->location); + }; - visit(expr, ValueContext::RValue); - testIsSubtype(lookupType(expr), builtinTypes->numberType, expr->location); - }; + checkNumber(forStatement->from); + checkNumber(forStatement->to); + checkNumber(forStatement->step); - checkNumber(forStatement->from); - checkNumber(forStatement->to); - checkNumber(forStatement->step); + visit(forStatement->body); +} - visit(forStatement->body); +void TypeChecker2::visit(AstStatForIn* forInStatement) +{ + for (AstLocal* local : forInStatement->vars) + { + if (local->annotation) + visit(local->annotation); } - void visit(AstStatForIn* forInStatement) - { - for (AstLocal* local : forInStatement->vars) - { - if (local->annotation) - visit(local->annotation); - } + for (AstExpr* expr : forInStatement->values) + visit(expr, ValueContext::RValue); - for (AstExpr* expr : forInStatement->values) - visit(expr, ValueContext::RValue); + visit(forInStatement->body); - visit(forInStatement->body); + // Rule out crazy stuff. Maybe possible if the file is not syntactically valid. + if (!forInStatement->vars.size || !forInStatement->values.size) + return; - // Rule out crazy stuff. Maybe possible if the file is not syntactically valid. - if (!forInStatement->vars.size || !forInStatement->values.size) - return; + NotNull scope = stack.back(); + TypeArena& arena = module->internalTypes; - NotNull scope = stack.back(); - TypeArena& arena = module->internalTypes; + std::vector variableTypes; + for (AstLocal* var : forInStatement->vars) + { + std::optional ty = scope->lookup(var); + LUAU_ASSERT(ty); + variableTypes.emplace_back(*ty); + } - std::vector variableTypes; - for (AstLocal* var : forInStatement->vars) - { - std::optional ty = scope->lookup(var); - LUAU_ASSERT(ty); - variableTypes.emplace_back(*ty); - } + AstExpr* firstValue = forInStatement->values.data[0]; - AstExpr* firstValue = forInStatement->values.data[0]; + // we need to build up a typepack for the iterators/values portion of the for-in statement. + std::vector valueTypes; + std::optional iteratorTail; - // we need to build up a typepack for the iterators/values portion of the for-in statement. - std::vector valueTypes; - std::optional iteratorTail; + // since the first value may be the only iterator (e.g. if it is a call), we want to + // look to see if it has a resulting typepack as our iterators. + TypePackId* retPack = module->astTypePacks.find(firstValue); + if (retPack) + { + auto [head, tail] = flatten(*retPack); + valueTypes = head; + iteratorTail = tail; + } + else + { + valueTypes.emplace_back(lookupType(firstValue)); + } - // since the first value may be the only iterator (e.g. if it is a call), we want to - // look to see if it has a resulting typepack as our iterators. - TypePackId* retPack = module->astTypePacks.find(firstValue); - if (retPack) - { - auto [head, tail] = flatten(*retPack); - valueTypes = head; - iteratorTail = tail; - } - else - { - valueTypes.emplace_back(lookupType(firstValue)); - } + // if the initial and expected types from the iterator unified during constraint solving, + // we'll have a resolved type to use here, but we'll only use it if either the iterator is + // directly present in the for-in statement or if we have an iterator state constraining us + TypeId* resolvedTy = module->astForInNextTypes.find(firstValue); + if (resolvedTy && (!retPack || valueTypes.size() > 1)) + valueTypes[0] = *resolvedTy; - // if the initial and expected types from the iterator unified during constraint solving, - // we'll have a resolved type to use here, but we'll only use it if either the iterator is - // directly present in the for-in statement or if we have an iterator state constraining us - TypeId* resolvedTy = module->astForInNextTypes.find(firstValue); - if (resolvedTy && (!retPack || valueTypes.size() > 1)) - valueTypes[0] = *resolvedTy; + for (size_t i = 1; i < forInStatement->values.size - 1; ++i) + { + valueTypes.emplace_back(lookupType(forInStatement->values.data[i])); + } - for (size_t i = 1; i < forInStatement->values.size - 1; ++i) - { - valueTypes.emplace_back(lookupType(forInStatement->values.data[i])); - } + // if we had more than one value, the tail from the first value is no longer appropriate to use. + if (forInStatement->values.size > 1) + { + auto [head, tail] = flatten(lookupPack(forInStatement->values.data[forInStatement->values.size - 1])); + valueTypes.insert(valueTypes.end(), head.begin(), head.end()); + iteratorTail = tail; + } - // if we had more than one value, the tail from the first value is no longer appropriate to use. - if (forInStatement->values.size > 1) - { - auto [head, tail] = flatten(lookupPack(forInStatement->values.data[forInStatement->values.size - 1])); - valueTypes.insert(valueTypes.end(), head.begin(), head.end()); - iteratorTail = tail; - } + // and now we can put everything together to get the actual typepack of the iterators. + TypePackId iteratorPack = arena.addTypePack(valueTypes, iteratorTail); - // and now we can put everything together to get the actual typepack of the iterators. - TypePackId iteratorPack = arena.addTypePack(valueTypes, iteratorTail); + // ... and then expand it out to 3 values (if possible) + TypePack iteratorTypes = extendTypePack(arena, builtinTypes, iteratorPack, 3); + if (iteratorTypes.head.empty()) + { + reportError(GenericError{"for..in loops require at least one value to iterate over. Got zero"}, getLocation(forInStatement->values)); + return; + } + TypeId iteratorTy = follow(iteratorTypes.head[0]); - // ... and then expand it out to 3 values (if possible) - TypePack iteratorTypes = extendTypePack(arena, builtinTypes, iteratorPack, 3); - if (iteratorTypes.head.empty()) + auto checkFunction = [this, &arena, &forInStatement, &variableTypes](const FunctionType* iterFtv, std::vector iterTys, bool isMm) + { + if (iterTys.size() < 1 || iterTys.size() > 3) { - reportError(GenericError{"for..in loops require at least one value to iterate over. Got zero"}, getLocation(forInStatement->values)); + if (isMm) + reportError(GenericError{"__iter metamethod must return (next[, table[, state]])"}, getLocation(forInStatement->values)); + else + reportError(GenericError{"for..in loops must be passed (next[, table[, state]])"}, getLocation(forInStatement->values)); + return; } - TypeId iteratorTy = follow(iteratorTypes.head[0]); - auto checkFunction = [this, &arena, &forInStatement, &variableTypes](const FunctionType* iterFtv, std::vector iterTys, bool isMm) + // It is okay if there aren't enough iterators, but the iteratee must provide enough. + TypePack expectedVariableTypes = extendTypePack(arena, builtinTypes, iterFtv->retTypes, variableTypes.size()); + if (expectedVariableTypes.head.size() < variableTypes.size()) { - if (iterTys.size() < 1 || iterTys.size() > 3) - { - if (isMm) - reportError(GenericError{"__iter metamethod must return (next[, table[, state]])"}, getLocation(forInStatement->values)); - else - reportError(GenericError{"for..in loops must be passed (next[, table[, state]])"}, getLocation(forInStatement->values)); - - return; - } - - // It is okay if there aren't enough iterators, but the iteratee must provide enough. - TypePack expectedVariableTypes = extendTypePack(arena, builtinTypes, iterFtv->retTypes, variableTypes.size()); - if (expectedVariableTypes.head.size() < variableTypes.size()) - { - if (isMm) - reportError( - GenericError{"__iter metamethod's next() function does not return enough values"}, getLocation(forInStatement->values) - ); - else - reportError(GenericError{"next() does not return enough values"}, forInStatement->values.data[0]->location); - } - - for (size_t i = 0; i < std::min(expectedVariableTypes.head.size(), variableTypes.size()); ++i) - testIsSubtype(variableTypes[i], expectedVariableTypes.head[i], forInStatement->vars.data[i]->location); - - // nextFn is going to be invoked with (arrayTy, startIndexTy) - - // It will be passed two arguments on every iteration save the - // first. + if (isMm) + reportError(GenericError{"__iter metamethod's next() function does not return enough values"}, getLocation(forInStatement->values)); + else + reportError(GenericError{"next() does not return enough values"}, forInStatement->values.data[0]->location); + } - // It may be invoked with 0 or 1 argument on the first iteration. - // This depends on the types in iterateePack and therefore - // iteratorTypes. + for (size_t i = 0; i < std::min(expectedVariableTypes.head.size(), variableTypes.size()); ++i) + testIsSubtype(variableTypes[i], expectedVariableTypes.head[i], forInStatement->vars.data[i]->location); - // If the iteratee is an error type, then we can't really say anything else about iteration over it. - // After all, it _could've_ been a table. - if (get(follow(flattenPack(iterFtv->argTypes)))) - return; + // nextFn is going to be invoked with (arrayTy, startIndexTy) - // If iteratorTypes is too short to be a valid call to nextFn, we have to report a count mismatch error. - // If 2 is too short to be a valid call to nextFn, we have to report a count mismatch error. - // If 2 is too long to be a valid call to nextFn, we have to report a count mismatch error. - auto [minCount, maxCount] = getParameterExtents(TxnLog::empty(), iterFtv->argTypes, /*includeHiddenVariadics*/ true); + // It will be passed two arguments on every iteration save the + // first. - TypePack flattenedArgTypes = extendTypePack(arena, builtinTypes, iterFtv->argTypes, 2); - size_t firstIterationArgCount = iterTys.empty() ? 0 : iterTys.size() - 1; - size_t actualArgCount = expectedVariableTypes.head.size(); - if (firstIterationArgCount < minCount) - { - if (isMm) - reportError(GenericError{"__iter metamethod must return (next[, table[, state]])"}, getLocation(forInStatement->values)); - else - reportError(CountMismatch{2, std::nullopt, firstIterationArgCount, CountMismatch::Arg}, forInStatement->values.data[0]->location); - } + // It may be invoked with 0 or 1 argument on the first iteration. + // This depends on the types in iterateePack and therefore + // iteratorTypes. - else if (actualArgCount < minCount) - { - if (isMm) - reportError(GenericError{"__iter metamethod must return (next[, table[, state]])"}, getLocation(forInStatement->values)); - else - reportError(CountMismatch{2, std::nullopt, firstIterationArgCount, CountMismatch::Arg}, forInStatement->values.data[0]->location); - } + // If the iteratee is an error type, then we can't really say anything else about iteration over it. + // After all, it _could've_ been a table. + if (get(follow(flattenPack(iterFtv->argTypes)))) + return; + // If iteratorTypes is too short to be a valid call to nextFn, we have to report a count mismatch error. + // If 2 is too short to be a valid call to nextFn, we have to report a count mismatch error. + // If 2 is too long to be a valid call to nextFn, we have to report a count mismatch error. + auto [minCount, maxCount] = getParameterExtents(TxnLog::empty(), iterFtv->argTypes, /*includeHiddenVariadics*/ true); - if (iterTys.size() >= 2 && flattenedArgTypes.head.size() > 0) - { - size_t valueIndex = forInStatement->values.size > 1 ? 1 : 0; - testIsSubtype(iterTys[1], flattenedArgTypes.head[0], forInStatement->values.data[valueIndex]->location); - } + TypePack flattenedArgTypes = extendTypePack(arena, builtinTypes, iterFtv->argTypes, 2); + size_t firstIterationArgCount = iterTys.empty() ? 0 : iterTys.size() - 1; + size_t actualArgCount = expectedVariableTypes.head.size(); + if (firstIterationArgCount < minCount) + { + if (isMm) + reportError(GenericError{"__iter metamethod must return (next[, table[, state]])"}, getLocation(forInStatement->values)); + else + reportError(CountMismatch{2, std::nullopt, firstIterationArgCount, CountMismatch::Arg}, forInStatement->values.data[0]->location); + } - if (iterTys.size() == 3 && flattenedArgTypes.head.size() > 1) - { - size_t valueIndex = forInStatement->values.size > 2 ? 2 : 0; - testIsSubtype(iterTys[2], flattenedArgTypes.head[1], forInStatement->values.data[valueIndex]->location); - } - }; - - std::shared_ptr iteratorNorm = normalizer.normalize(iteratorTy); - - if (!iteratorNorm) - reportError(NormalizationTooComplex{}, firstValue->location); - - /* - * If the first iterator argument is a function - * * There must be 1 to 3 iterator arguments. Name them (nextTy, - * arrayTy, startIndexTy) - * * The return type of nextTy() must correspond to the variables' - * types and counts. HOWEVER the first iterator will never be nil. - * * The first return value of nextTy must be compatible with - * startIndexTy. - * * The first argument to nextTy() must be compatible with arrayTy if - * present. nil if not. - * * The second argument to nextTy() must be compatible with - * startIndexTy if it is present. Else, it must be compatible with - * nil. - * * nextTy() must be callable with only 2 arguments. - */ - if (const FunctionType* nextFn = get(iteratorTy)) - { - checkFunction(nextFn, iteratorTypes.head, false); - } - else if (const TableType* ttv = get(iteratorTy)) - { - if ((forInStatement->vars.size == 1 || forInStatement->vars.size == 2) && ttv->indexer) - { - testIsSubtype(variableTypes[0], ttv->indexer->indexType, forInStatement->vars.data[0]->location); - if (variableTypes.size() == 2) - testIsSubtype(variableTypes[1], ttv->indexer->indexResultType, forInStatement->vars.data[1]->location); - } + else if (actualArgCount < minCount) + { + if (isMm) + reportError(GenericError{"__iter metamethod must return (next[, table[, state]])"}, getLocation(forInStatement->values)); else - reportError(GenericError{"Cannot iterate over a table without indexer"}, forInStatement->values.data[0]->location); + reportError(CountMismatch{2, std::nullopt, firstIterationArgCount, CountMismatch::Arg}, forInStatement->values.data[0]->location); } - else if (get(iteratorTy) || get(iteratorTy) || get(iteratorTy)) + + + if (iterTys.size() >= 2 && flattenedArgTypes.head.size() > 0) { - // nothing + size_t valueIndex = forInStatement->values.size > 1 ? 1 : 0; + testIsSubtype(iterTys[1], flattenedArgTypes.head[0], forInStatement->values.data[valueIndex]->location); } - else if (isOptional(iteratorTy) && !(iteratorNorm && iteratorNorm->shouldSuppressErrors())) + + if (iterTys.size() == 3 && flattenedArgTypes.head.size() > 1) { - reportError(OptionalValueAccess{iteratorTy}, forInStatement->values.data[0]->location); + size_t valueIndex = forInStatement->values.size > 2 ? 2 : 0; + testIsSubtype(iterTys[2], flattenedArgTypes.head[1], forInStatement->values.data[valueIndex]->location); } - else if (std::optional iterMmTy = - findMetatableEntry(builtinTypes, module->errors, iteratorTy, "__iter", forInStatement->values.data[0]->location)) + }; + + std::shared_ptr iteratorNorm = normalizer.normalize(iteratorTy); + + if (!iteratorNorm) + reportError(NormalizationTooComplex{}, firstValue->location); + + /* + * If the first iterator argument is a function + * * There must be 1 to 3 iterator arguments. Name them (nextTy, + * arrayTy, startIndexTy) + * * The return type of nextTy() must correspond to the variables' + * types and counts. HOWEVER the first iterator will never be nil. + * * The first return value of nextTy must be compatible with + * startIndexTy. + * * The first argument to nextTy() must be compatible with arrayTy if + * present. nil if not. + * * The second argument to nextTy() must be compatible with + * startIndexTy if it is present. Else, it must be compatible with + * nil. + * * nextTy() must be callable with only 2 arguments. + */ + if (const FunctionType* nextFn = get(iteratorTy)) + { + checkFunction(nextFn, iteratorTypes.head, false); + } + else if (const TableType* ttv = get(iteratorTy)) + { + if ((forInStatement->vars.size == 1 || forInStatement->vars.size == 2) && ttv->indexer) { - Instantiation instantiation{TxnLog::empty(), &arena, builtinTypes, TypeLevel{}, scope}; + testIsSubtype(variableTypes[0], ttv->indexer->indexType, forInStatement->vars.data[0]->location); + if (variableTypes.size() == 2) + testIsSubtype(variableTypes[1], ttv->indexer->indexResultType, forInStatement->vars.data[1]->location); + } + else + reportError(GenericError{"Cannot iterate over a table without indexer"}, forInStatement->values.data[0]->location); + } + else if (get(iteratorTy) || get(iteratorTy) || get(iteratorTy)) + { + // nothing + } + else if (isOptional(iteratorTy) && !(iteratorNorm && iteratorNorm->shouldSuppressErrors())) + { + reportError(OptionalValueAccess{iteratorTy}, forInStatement->values.data[0]->location); + } + else if (std::optional iterMmTy = findMetatableEntry(builtinTypes, module->errors, iteratorTy, "__iter", forInStatement->values.data[0]->location)) + { + Instantiation instantiation{TxnLog::empty(), &arena, builtinTypes, TypeLevel{}, scope}; - if (std::optional instantiatedIterMmTy = instantiate(builtinTypes, NotNull{&arena}, limits, scope, *iterMmTy)) + if (std::optional instantiatedIterMmTy = instantiate(builtinTypes, NotNull{&arena}, limits, scope, *iterMmTy)) + { + if (const FunctionType* iterMmFtv = get(*instantiatedIterMmTy)) { - if (const FunctionType* iterMmFtv = get(*instantiatedIterMmTy)) - { - TypePackId argPack = arena.addTypePack({iteratorTy}); - testIsSubtype(argPack, iterMmFtv->argTypes, forInStatement->values.data[0]->location); + TypePackId argPack = arena.addTypePack({iteratorTy}); + testIsSubtype(argPack, iterMmFtv->argTypes, forInStatement->values.data[0]->location); - TypePack mmIteratorTypes = extendTypePack(arena, builtinTypes, iterMmFtv->retTypes, 3); + TypePack mmIteratorTypes = extendTypePack(arena, builtinTypes, iterMmFtv->retTypes, 3); - if (mmIteratorTypes.head.size() == 0) - { - reportError(GenericError{"__iter must return at least one value"}, forInStatement->values.data[0]->location); - return; - } + if (mmIteratorTypes.head.size() == 0) + { + reportError(GenericError{"__iter must return at least one value"}, forInStatement->values.data[0]->location); + return; + } - TypeId nextFn = follow(mmIteratorTypes.head[0]); + TypeId nextFn = follow(mmIteratorTypes.head[0]); - if (std::optional instantiatedNextFn = instantiation.substitute(nextFn)) - { - std::vector instantiatedIteratorTypes = mmIteratorTypes.head; - instantiatedIteratorTypes[0] = *instantiatedNextFn; + if (std::optional instantiatedNextFn = instantiation.substitute(nextFn)) + { + std::vector instantiatedIteratorTypes = mmIteratorTypes.head; + instantiatedIteratorTypes[0] = *instantiatedNextFn; - if (const FunctionType* nextFtv = get(*instantiatedNextFn)) - { - checkFunction(nextFtv, instantiatedIteratorTypes, true); - } - else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *instantiatedNextFn)) - { - reportError(CannotCallNonFunction{*instantiatedNextFn}, forInStatement->values.data[0]->location); - } + if (const FunctionType* nextFtv = get(*instantiatedNextFn)) + { + checkFunction(nextFtv, instantiatedIteratorTypes, true); } - else + else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *instantiatedNextFn)) { - reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location); + reportError(CannotCallNonFunction{*instantiatedNextFn}, forInStatement->values.data[0]->location); } } - else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *iterMmTy)) + else { - // TODO: This will not tell the user that this is because the - // metamethod isn't callable. This is not ideal, and we should - // improve this error message. - - // TODO: This will also not handle intersections of functions or - // callable tables (which are supported by the runtime). - reportError(CannotCallNonFunction{*iterMmTy}, forInStatement->values.data[0]->location); + reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location); } } - else + else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *iterMmTy)) { - reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location); + // TODO: This will not tell the user that this is because the + // metamethod isn't callable. This is not ideal, and we should + // improve this error message. + + // TODO: This will also not handle intersections of functions or + // callable tables (which are supported by the runtime). + reportError(CannotCallNonFunction{*iterMmTy}, forInStatement->values.data[0]->location); } } - else if (iteratorNorm && iteratorNorm->hasTables()) - { - // Ok. All tables can be iterated. - } - else if (!iteratorNorm || !iteratorNorm->shouldSuppressErrors()) + else { - reportError(CannotCallNonFunction{iteratorTy}, forInStatement->values.data[0]->location); + reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location); } } - - std::optional getBindingType(AstExpr* expr) + else if (iteratorNorm && iteratorNorm->hasTables()) { - if (auto localExpr = expr->as()) - { - Scope* s = stack.back(); - return s->lookup(localExpr->local); - } - else if (auto globalExpr = expr->as()) - { - Scope* s = stack.back(); - return s->lookup(globalExpr->name); - } - else - return std::nullopt; + // Ok. All tables can be iterated. + } + else if (!iteratorNorm || !iteratorNorm->shouldSuppressErrors()) + { + reportError(CannotCallNonFunction{iteratorTy}, forInStatement->values.data[0]->location); } +} - // this should only be called if the type of `lhs` is `never`. - void reportErrorsFromAssigningToNever(AstExpr* lhs, TypeId rhsType) +std::optional TypeChecker2::getBindingType(AstExpr* expr) +{ + if (auto localExpr = expr->as()) + { + Scope* s = stack.back(); + return s->lookup(localExpr->local); + } + else if (auto globalExpr = expr->as()) { + Scope* s = stack.back(); + return s->lookup(globalExpr->name); + } + else + return std::nullopt; +} - if (auto indexName = lhs->as()) - { - TypeId indexedType = lookupType(indexName->expr); +void TypeChecker2::reportErrorsFromAssigningToNever(AstExpr* lhs, TypeId rhsType) +{ - // if it's already never, I don't think we have anything to do here. - if (get(indexedType)) - return; + if (auto indexName = lhs->as()) + { + TypeId indexedType = lookupType(indexName->expr); - std::string prop = indexName->index.value; + // if it's already never, I don't think we have anything to do here. + if (get(indexedType)) + return; - std::shared_ptr norm = normalizer.normalize(indexedType); - if (!norm) - { - reportError(NormalizationTooComplex{}, lhs->location); - return; - } + std::string prop = indexName->index.value; + + std::shared_ptr norm = normalizer.normalize(indexedType); + if (!norm) + { + reportError(NormalizationTooComplex{}, lhs->location); + return; + } - // if the type is error suppressing, we don't actually have any work left to do. - if (norm->shouldSuppressErrors()) - return; + // if the type is error suppressing, we don't actually have any work left to do. + if (norm->shouldSuppressErrors()) + return; - const auto propTypes = lookupProp(norm.get(), prop, ValueContext::LValue, lhs->location, builtinTypes->stringType, module->errors); + const auto propTypes = lookupProp(norm.get(), prop, ValueContext::LValue, lhs->location, builtinTypes->stringType, module->errors); - reportError(CannotAssignToNever{rhsType, propTypes.typesOfProp, CannotAssignToNever::Reason::PropertyNarrowed}, lhs->location); - } + reportError(CannotAssignToNever{rhsType, propTypes.typesOfProp, CannotAssignToNever::Reason::PropertyNarrowed}, lhs->location); } +} - void visit(AstStatAssign* assign) - { - size_t count = std::min(assign->vars.size, assign->values.size); +void TypeChecker2::visit(AstStatAssign* assign) +{ + size_t count = std::min(assign->vars.size, assign->values.size); - for (size_t i = 0; i < count; ++i) - { - AstExpr* lhs = assign->vars.data[i]; - visit(lhs, ValueContext::LValue); - TypeId lhsType = lookupType(lhs); + for (size_t i = 0; i < count; ++i) + { + AstExpr* lhs = assign->vars.data[i]; + visit(lhs, ValueContext::LValue); + TypeId lhsType = lookupType(lhs); - AstExpr* rhs = assign->values.data[i]; - visit(rhs, ValueContext::RValue); - TypeId rhsType = lookupType(rhs); + AstExpr* rhs = assign->values.data[i]; + visit(rhs, ValueContext::RValue); + TypeId rhsType = lookupType(rhs); - if (get(lhsType)) - { - reportErrorsFromAssigningToNever(lhs, rhsType); - continue; - } + if (get(lhsType)) + { + reportErrorsFromAssigningToNever(lhs, rhsType); + continue; + } - bool ok = testIsSubtype(rhsType, lhsType, rhs->location); + bool ok = testIsSubtype(rhsType, lhsType, rhs->location); - // If rhsType bindingType = getBindingType(lhs); - if (bindingType) - testIsSubtype(rhsType, *bindingType, rhs->location); - } + // If rhsType bindingType = getBindingType(lhs); + if (bindingType) + testIsSubtype(rhsType, *bindingType, rhs->location); } } +} - void visit(AstStatCompoundAssign* stat) - { - AstExprBinary fake{stat->location, stat->op, stat->var, stat->value}; - visit(&fake, stat); - - TypeId* resultTy = module->astCompoundAssignResultTypes.find(stat); - LUAU_ASSERT(resultTy); - TypeId varTy = lookupType(stat->var); +void TypeChecker2::visit(AstStatCompoundAssign* stat) +{ + AstExprBinary fake{stat->location, stat->op, stat->var, stat->value}; + visit(&fake, stat); - testIsSubtype(*resultTy, varTy, stat->location); - } + TypeId* resultTy = module->astCompoundAssignResultTypes.find(stat); + LUAU_ASSERT(resultTy); + TypeId varTy = lookupType(stat->var); - void visit(AstStatFunction* stat) - { - visit(stat->name, ValueContext::LValue); - visit(stat->func); - } + testIsSubtype(*resultTy, varTy, stat->location); +} - void visit(AstStatLocalFunction* stat) - { - visit(stat->func); - } +void TypeChecker2::visit(AstStatFunction* stat) +{ + visit(stat->name, ValueContext::LValue); + visit(stat->func); +} - void visit(const AstTypeList* typeList) - { - for (AstType* ty : typeList->types) - visit(ty); +void TypeChecker2::visit(AstStatLocalFunction* stat) +{ + visit(stat->func); +} - if (typeList->tailType) - visit(typeList->tailType); - } +void TypeChecker2::visit(const AstTypeList* typeList) +{ + for (AstType* ty : typeList->types) + visit(ty); - void visit(AstStatTypeAlias* stat) - { - visitGenerics(stat->generics, stat->genericPacks); - visit(stat->type); - } + if (typeList->tailType) + visit(typeList->tailType); +} - void visit(AstStatTypeFunction* stat) - { - // TODO: add type checking for user-defined type functions +void TypeChecker2::visit(AstStatTypeAlias* stat) +{ + visitGenerics(stat->generics, stat->genericPacks); + visit(stat->type); +} - reportError(TypeError{stat->location, GenericError{"This syntax is not supported"}}); - } +void TypeChecker2::visit(AstStatTypeFunction* stat) +{ + // TODO: add type checking for user-defined type functions - void visit(AstTypeList types) - { - for (AstType* type : types.types) - visit(type); - if (types.tailType) - visit(types.tailType); - } + reportError(TypeError{stat->location, GenericError{"This syntax is not supported"}}); +} - void visit(AstStatDeclareFunction* stat) - { - visitGenerics(stat->generics, stat->genericPacks); - visit(stat->params); - visit(stat->retTypes); - } +void TypeChecker2::visit(AstTypeList types) +{ + for (AstType* type : types.types) + visit(type); + if (types.tailType) + visit(types.tailType); +} - void visit(AstStatDeclareGlobal* stat) - { - visit(stat->type); - } +void TypeChecker2::visit(AstStatDeclareFunction* stat) +{ + visitGenerics(stat->generics, stat->genericPacks); + visit(stat->params); + visit(stat->retTypes); +} - void visit(AstStatDeclareClass* stat) - { - for (const AstDeclaredClassProp& prop : stat->props) - visit(prop.ty); - } +void TypeChecker2::visit(AstStatDeclareGlobal* stat) +{ + visit(stat->type); +} - void visit(AstStatError* stat) - { - for (AstExpr* expr : stat->expressions) - visit(expr, ValueContext::RValue); +void TypeChecker2::visit(AstStatDeclareClass* stat) +{ + for (const AstDeclaredClassProp& prop : stat->props) + visit(prop.ty); +} - for (AstStat* s : stat->statements) - visit(s); - } +void TypeChecker2::visit(AstStatError* stat) +{ + for (AstExpr* expr : stat->expressions) + visit(expr, ValueContext::RValue); - void visit(AstExpr* expr, ValueContext context) - { - auto StackPusher = pushStack(expr); + for (AstStat* s : stat->statements) + visit(s); +} - if (auto e = expr->as()) - return visit(e, context); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e, context); - else if (auto e = expr->as()) - return visit(e, context); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - { - visit(e); - return; - } - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else if (auto e = expr->as()) - return visit(e); - else - LUAU_ASSERT(!"TypeChecker2 encountered an unknown expression type"); - } +void TypeChecker2::visit(AstExpr* expr, ValueContext context) +{ + auto StackPusher = pushStack(expr); + + if (auto e = expr->as()) + return visit(e, context); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e, context); + else if (auto e = expr->as()) + return visit(e, context); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + { + visit(e); + return; + } + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else if (auto e = expr->as()) + return visit(e); + else + LUAU_ASSERT(!"TypeChecker2 encountered an unknown expression type"); +} - void visit(AstExprGroup* expr, ValueContext context) - { - visit(expr->expr, context); - } +void TypeChecker2::visit(AstExprGroup* expr, ValueContext context) +{ + visit(expr->expr, context); +} - void visit(AstExprConstantNil* expr) - { +void TypeChecker2::visit(AstExprConstantNil* expr) +{ #if defined(LUAU_ENABLE_ASSERT) - TypeId actualType = lookupType(expr); - TypeId expectedType = builtinTypes->nilType; + TypeId actualType = lookupType(expr); + TypeId expectedType = builtinTypes->nilType; + NotNull scope{findInnermostScope(expr->location)}; - SubtypingResult r = subtyping->isSubtype(actualType, expectedType); - LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, actualType)); + SubtypingResult r = subtyping->isSubtype(actualType, expectedType, scope); + LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, actualType)); #endif - } +} - void visit(AstExprConstantBool* expr) - { - // booleans use specialized inference logic for singleton types, which can lead to real type errors here. +void TypeChecker2::visit(AstExprConstantBool* expr) +{ + // booleans use specialized inference logic for singleton types, which can lead to real type errors here. - const TypeId bestType = expr->value ? builtinTypes->trueType : builtinTypes->falseType; - const TypeId inferredType = lookupType(expr); + const TypeId bestType = expr->value ? builtinTypes->trueType : builtinTypes->falseType; + const TypeId inferredType = lookupType(expr); + NotNull scope{findInnermostScope(expr->location)}; - const SubtypingResult r = subtyping->isSubtype(bestType, inferredType); - if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType)) - reportError(TypeMismatch{inferredType, bestType}, expr->location); - } + const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope); + if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType)) + reportError(TypeMismatch{inferredType, bestType}, expr->location); +} - void visit(AstExprConstantNumber* expr) - { +void TypeChecker2::visit(AstExprConstantNumber* expr) +{ #if defined(LUAU_ENABLE_ASSERT) - const TypeId bestType = builtinTypes->numberType; - const TypeId inferredType = lookupType(expr); + const TypeId bestType = builtinTypes->numberType; + const TypeId inferredType = lookupType(expr); + NotNull scope{findInnermostScope(expr->location)}; - const SubtypingResult r = subtyping->isSubtype(bestType, inferredType); - LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, inferredType)); + const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope); + LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, inferredType)); #endif - } - - void visit(AstExprConstantString* expr) - { - // strings use specialized inference logic for singleton types, which can lead to real type errors here. +} - const TypeId bestType = module->internalTypes.addType(SingletonType{StringSingleton{std::string{expr->value.data, expr->value.size}}}); - const TypeId inferredType = lookupType(expr); +void TypeChecker2::visit(AstExprConstantString* expr) +{ + // strings use specialized inference logic for singleton types, which can lead to real type errors here. - const SubtypingResult r = subtyping->isSubtype(bestType, inferredType); - if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType)) - reportError(TypeMismatch{inferredType, bestType}, expr->location); - } + const TypeId bestType = module->internalTypes.addType(SingletonType{StringSingleton{std::string{expr->value.data, expr->value.size}}}); + const TypeId inferredType = lookupType(expr); + NotNull scope{findInnermostScope(expr->location)}; - void visit(AstExprLocal* expr) - { - // TODO! - } + const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope); + if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType)) + reportError(TypeMismatch{inferredType, bestType}, expr->location); +} - void visit(AstExprGlobal* expr) - { - NotNull scope = stack.back(); - if (!scope->lookup(expr->name)) - reportError(UnknownSymbol{expr->name.value, UnknownSymbol::Binding}, expr->location); - } +void TypeChecker2::visit(AstExprLocal* expr) +{ + // TODO! +} - void visit(AstExprVarargs* expr) - { - // TODO! - } +void TypeChecker2::visit(AstExprGlobal* expr) +{ + NotNull scope = stack.back(); + if (!scope->lookup(expr->name)) + reportError(UnknownSymbol{expr->name.value, UnknownSymbol::Binding}, expr->location); +} - // Note: this is intentionally separated from `visit(AstExprCall*)` for stack allocation purposes. - void visitCall(AstExprCall* call) - { - TypePack args; - std::vector argExprs; - argExprs.reserve(call->args.size + 1); +void TypeChecker2::visit(AstExprVarargs* expr) +{ + // TODO! +} - TypeId* originalCallTy = module->astOriginalCallTypes.find(call); - TypeId* selectedOverloadTy = module->astOverloadResolvedTypes.find(call); - if (!originalCallTy) - return; +void TypeChecker2::visitCall(AstExprCall* call) +{ + TypePack args; + std::vector argExprs; + NotNull scope{findInnermostScope(call->location)}; + argExprs.reserve(call->args.size + 1); - TypeId fnTy = follow(*originalCallTy); + TypeId* originalCallTy = module->astOriginalCallTypes.find(call->func); + TypeId* selectedOverloadTy = module->astOverloadResolvedTypes.find(call); + if (!originalCallTy) + return; + TypeId fnTy = follow(*originalCallTy); - if (get(fnTy) || get(fnTy) || get(fnTy)) - return; - else if (isOptional(fnTy)) - { - switch (shouldSuppressErrors(NotNull{&normalizer}, fnTy)) - { - case ErrorSuppression::Suppress: - break; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, call->func->location); - // fallthrough intentional - case ErrorSuppression::DoNotSuppress: - reportError(OptionalValueAccess{fnTy}, call->func->location); - } - return; - } - if (selectedOverloadTy) + if (get(fnTy) || get(fnTy) || get(fnTy)) + return; + else if (isOptional(fnTy)) + { + switch (shouldSuppressErrors(NotNull{&normalizer}, fnTy)) { - SubtypingResult result = subtyping->isSubtype(*originalCallTy, *selectedOverloadTy); - if (result.isSubtype) - fnTy = follow(*selectedOverloadTy); - - if (result.normalizationTooComplex) - { - reportError(NormalizationTooComplex{}, call->func->location); - return; - } + case ErrorSuppression::Suppress: + break; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, call->func->location); + [[fallthrough]]; + case ErrorSuppression::DoNotSuppress: + reportError(OptionalValueAccess{fnTy}, call->func->location); } + return; + } - if (call->self) - { - AstExprIndexName* indexExpr = call->func->as(); - if (!indexExpr) - ice->ice("method call expression has no 'self'"); - - args.head.push_back(lookupType(indexExpr->expr)); - argExprs.push_back(indexExpr->expr); - } + if (selectedOverloadTy) + { + SubtypingResult result = subtyping->isSubtype(*originalCallTy, *selectedOverloadTy, scope); + if (result.isSubtype) + fnTy = follow(*selectedOverloadTy); - for (size_t i = 0; i < call->args.size; ++i) + if (result.normalizationTooComplex) { - AstExpr* arg = call->args.data[i]; - argExprs.push_back(arg); - TypeId* argTy = module->astTypes.find(arg); - if (argTy) - args.head.push_back(*argTy); - else if (i == call->args.size - 1) - { - if (auto argTail = module->astTypePacks.find(arg)) - { - auto [head, tail] = flatten(*argTail); - args.head.insert(args.head.end(), head.begin(), head.end()); - args.tail = tail; - } - else - args.tail = builtinTypes->anyTypePack; - } - else - args.head.push_back(builtinTypes->anyType); + reportError(NormalizationTooComplex{}, call->func->location); + return; } + } + if (call->self) + { + AstExprIndexName* indexExpr = call->func->as(); + if (!indexExpr) + ice->ice("method call expression has no 'self'"); + args.head.push_back(lookupType(indexExpr->expr)); + argExprs.push_back(indexExpr->expr); + } - OverloadResolver resolver{ - builtinTypes, - NotNull{&module->internalTypes}, - NotNull{&normalizer}, - NotNull{stack.back()}, - ice, - limits, - call->location, - }; - resolver.resolve(fnTy, &args, call->func, &argExprs); - - auto norm = normalizer.normalize(fnTy); - if (!norm) - reportError(NormalizationTooComplex{}, call->func->location); - auto isInhabited = normalizer.isInhabited(norm.get()); - if (isInhabited == NormalizationResult::HitLimits) - reportError(NormalizationTooComplex{}, call->func->location); - - if (norm && norm->shouldSuppressErrors()) - return; // error suppressing function type! - else if (!resolver.ok.empty()) - return; // We found a call that works, so this is ok. - else if (!norm || isInhabited == NormalizationResult::False) - return; // Ok. Calling an uninhabited type is no-op. - else if (!resolver.nonviableOverloads.empty()) + for (size_t i = 0; i < call->args.size; ++i) + { + AstExpr* arg = call->args.data[i]; + argExprs.push_back(arg); + TypeId* argTy = module->astTypes.find(arg); + if (argTy) + args.head.push_back(*argTy); + else if (i == call->args.size - 1) { - if (resolver.nonviableOverloads.size() == 1 && !isErrorSuppressing(call->func->location, resolver.nonviableOverloads.front().first)) - reportErrors(resolver.nonviableOverloads.front().second); - else + if (auto argTail = module->astTypePacks.find(arg)) { - std::string s = "None of the overloads for function that accept "; - s += std::to_string(args.head.size()); - s += " arguments are compatible."; - reportError(GenericError{std::move(s)}, call->location); + auto [head, tail] = flatten(*argTail); + args.head.insert(args.head.end(), head.begin(), head.end()); + args.tail = tail; } - } - else if (!resolver.arityMismatches.empty()) - { - if (resolver.arityMismatches.size() == 1) - reportErrors(resolver.arityMismatches.front().second); else - { - std::string s = "No overload for function accepts "; - s += std::to_string(args.head.size()); - s += " arguments."; - reportError(GenericError{std::move(s)}, call->location); - } + args.tail = builtinTypes->anyTypePack; } - else if (!resolver.nonFunctions.empty()) - reportError(CannotCallNonFunction{fnTy}, call->func->location); else - LUAU_ASSERT(!"Generating the best possible error from this function call resolution was inexhaustive?"); + args.head.push_back(builtinTypes->anyType); + } - if (resolver.nonviableOverloads.size() <= 1 && resolver.arityMismatches.size() <= 1) + TypePackId argsTp = module->internalTypes.addTypePack(args); + if (auto ftv = get(follow(*originalCallTy))) + { + if (ftv->dcrMagicTypeCheck) + { + ftv->dcrMagicTypeCheck(MagicFunctionTypeCheckContext{NotNull{this}, builtinTypes, call, argsTp, scope}); return; + } + } - std::string s = "Available overloads: "; - std::vector overloads; - if (resolver.nonviableOverloads.empty()) + OverloadResolver resolver{ + builtinTypes, + NotNull{&module->internalTypes}, + NotNull{&normalizer}, + NotNull{stack.back()}, + ice, + limits, + call->location, + }; + resolver.resolve(fnTy, &args, call->func, &argExprs); + + auto norm = normalizer.normalize(fnTy); + if (!norm) + reportError(NormalizationTooComplex{}, call->func->location); + auto isInhabited = normalizer.isInhabited(norm.get()); + if (isInhabited == NormalizationResult::HitLimits) + reportError(NormalizationTooComplex{}, call->func->location); + + if (norm && norm->shouldSuppressErrors()) + return; // error suppressing function type! + else if (!resolver.ok.empty()) + return; // We found a call that works, so this is ok. + else if (!norm || isInhabited == NormalizationResult::False) + return; // Ok. Calling an uninhabited type is no-op. + else if (!resolver.nonviableOverloads.empty()) + { + if (resolver.nonviableOverloads.size() == 1 && !isErrorSuppressing(call->func->location, resolver.nonviableOverloads.front().first)) + reportErrors(resolver.nonviableOverloads.front().second); + else { - for (const auto& [ty, p] : resolver.resolution) - { - if (p.first == OverloadResolver::TypeIsNotAFunction) - continue; - - overloads.push_back(ty); - } + std::string s = "None of the overloads for function that accept "; + s += std::to_string(args.head.size()); + s += " arguments are compatible."; + reportError(GenericError{std::move(s)}, call->location); } + } + else if (!resolver.arityMismatches.empty()) + { + if (resolver.arityMismatches.size() == 1) + reportErrors(resolver.arityMismatches.front().second); else { - for (const auto& [ty, _] : resolver.nonviableOverloads) - overloads.push_back(ty); + std::string s = "No overload for function accepts "; + s += std::to_string(args.head.size()); + s += " arguments."; + reportError(GenericError{std::move(s)}, call->location); } + } + else if (!resolver.nonFunctions.empty()) + reportError(CannotCallNonFunction{fnTy}, call->func->location); + else + LUAU_ASSERT(!"Generating the best possible error from this function call resolution was inexhaustive?"); - if (overloads.size() <= 1) - return; + if (resolver.nonviableOverloads.size() <= 1 && resolver.arityMismatches.size() <= 1) + return; + + std::string s = "Available overloads: "; - for (size_t i = 0; i < overloads.size(); ++i) + std::vector overloads; + if (resolver.nonviableOverloads.empty()) + { + for (const auto& [ty, p] : resolver.resolution) { - if (i > 0) - s += (i == overloads.size() - 1) ? "; and " : "; "; + if (p.first == OverloadResolver::TypeIsNotAFunction) + continue; - s += toString(overloads[i]); + overloads.push_back(ty); } - - reportError(ExtraInformation{std::move(s)}, call->func->location); } - - void visit(AstExprCall* call) + else { - visit(call->func, ValueContext::RValue); + for (const auto& [ty, _] : resolver.nonviableOverloads) + overloads.push_back(ty); + } + + if (overloads.size() <= 1) + return; - for (AstExpr* arg : call->args) - visit(arg, ValueContext::RValue); + for (size_t i = 0; i < overloads.size(); ++i) + { + if (i > 0) + s += (i == overloads.size() - 1) ? "; and " : "; "; - visitCall(call); + s += toString(overloads[i]); } - std::optional tryStripUnionFromNil(TypeId ty) - { - if (const UnionType* utv = get(ty)) - { - if (!std::any_of(begin(utv), end(utv), isNil)) - return ty; + reportError(ExtraInformation{std::move(s)}, call->func->location); +} - std::vector result; +void TypeChecker2::visit(AstExprCall* call) +{ + visit(call->func, ValueContext::RValue); - for (TypeId option : utv) - { - if (!isNil(option)) - result.push_back(option); - } + for (AstExpr* arg : call->args) + visit(arg, ValueContext::RValue); + + visitCall(call); +} + +std::optional TypeChecker2::tryStripUnionFromNil(TypeId ty) +{ + if (const UnionType* utv = get(ty)) + { + if (!std::any_of(begin(utv), end(utv), isNil)) + return ty; - if (result.empty()) - return std::nullopt; + std::vector result; - return result.size() == 1 ? result[0] : module->internalTypes.addType(UnionType{std::move(result)}); + for (TypeId option : utv) + { + if (!isNil(option)) + result.push_back(option); } - return std::nullopt; + if (result.empty()) + return std::nullopt; + + return result.size() == 1 ? result[0] : module->internalTypes.addType(UnionType{std::move(result)}); } - TypeId stripFromNilAndReport(TypeId ty, const Location& location) + return std::nullopt; +} + +TypeId TypeChecker2::stripFromNilAndReport(TypeId ty, const Location& location) +{ + ty = follow(ty); + + if (auto utv = get(ty)) { - ty = follow(ty); + if (!std::any_of(begin(utv), end(utv), isNil)) + return ty; + } - if (auto utv = get(ty)) + if (std::optional strippedUnion = tryStripUnionFromNil(ty)) + { + switch (shouldSuppressErrors(NotNull{&normalizer}, ty)) { - if (!std::any_of(begin(utv), end(utv), isNil)) - return ty; + case ErrorSuppression::Suppress: + break; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, location); + [[fallthrough]]; + case ErrorSuppression::DoNotSuppress: + reportError(OptionalValueAccess{ty}, location); } - if (std::optional strippedUnion = tryStripUnionFromNil(ty)) - { - switch (shouldSuppressErrors(NotNull{&normalizer}, ty)) - { - case ErrorSuppression::Suppress: - break; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, location); - // fallthrough intentional - case ErrorSuppression::DoNotSuppress: - reportError(OptionalValueAccess{ty}, location); - } + return follow(*strippedUnion); + } - return follow(*strippedUnion); - } + return ty; +} - return ty; - } +void TypeChecker2::visitExprName(AstExpr* expr, Location location, const std::string& propName, ValueContext context, TypeId astIndexExprTy) +{ + visit(expr, ValueContext::RValue); + TypeId leftType = stripFromNilAndReport(lookupType(expr), location); + checkIndexTypeFromType(leftType, propName, context, location, astIndexExprTy); +} + +void TypeChecker2::visit(AstExprIndexName* indexName, ValueContext context) +{ + // If we're indexing like _.foo - foo could either be a prop or a string. + visitExprName(indexName->expr, indexName->location, indexName->index.value, context, builtinTypes->stringType); +} - void visitExprName(AstExpr* expr, Location location, const std::string& propName, ValueContext context, TypeId astIndexExprTy) +void TypeChecker2::indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const MetatableType* metaTable, TypeId exprType, TypeId indexType) +{ + if (auto tt = get(follow(metaTable->table)); tt && tt->indexer) + testIsSubtype(indexType, tt->indexer->indexType, indexExpr->index->location); + else if (auto mt = get(follow(metaTable->table))) + indexExprMetatableHelper(indexExpr, mt, exprType, indexType); + else if (auto tmt = get(follow(metaTable->metatable)); tmt && tmt->indexer) + testIsSubtype(indexType, tmt->indexer->indexType, indexExpr->index->location); + else if (auto mtmt = get(follow(metaTable->metatable))) + indexExprMetatableHelper(indexExpr, mtmt, exprType, indexType); + else { - visit(expr, ValueContext::RValue); - TypeId leftType = stripFromNilAndReport(lookupType(expr), location); - checkIndexTypeFromType(leftType, propName, context, location, astIndexExprTy); + LUAU_ASSERT(tt || get(follow(metaTable->table))); + + reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location); } +} - void visit(AstExprIndexName* indexName, ValueContext context) +void TypeChecker2::visit(AstExprIndexExpr* indexExpr, ValueContext context) +{ + if (auto str = indexExpr->index->as()) { - // If we're indexing like _.foo - foo could either be a prop or a string. - visitExprName(indexName->expr, indexName->location, indexName->index.value, context, builtinTypes->stringType); + TypeId astIndexExprType = lookupType(indexExpr->index); + const std::string stringValue(str->value.data, str->value.size); + visitExprName(indexExpr->expr, indexExpr->location, stringValue, context, astIndexExprType); + return; } - void indexExprMetatableHelper(AstExprIndexExpr* indexExpr, const MetatableType* metaTable, TypeId exprType, TypeId indexType) + visit(indexExpr->expr, ValueContext::RValue); + visit(indexExpr->index, ValueContext::RValue); + + TypeId exprType = follow(lookupType(indexExpr->expr)); + TypeId indexType = follow(lookupType(indexExpr->index)); + + if (auto tt = get(exprType)) { - if (auto tt = get(follow(metaTable->table)); tt && tt->indexer) + if (tt->indexer) testIsSubtype(indexType, tt->indexer->indexType, indexExpr->index->location); - else if (auto mt = get(follow(metaTable->table))) - indexExprMetatableHelper(indexExpr, mt, exprType, indexType); - else if (auto tmt = get(follow(metaTable->metatable)); tmt && tmt->indexer) - testIsSubtype(indexType, tmt->indexer->indexType, indexExpr->index->location); - else if (auto mtmt = get(follow(metaTable->metatable))) - indexExprMetatableHelper(indexExpr, mtmt, exprType, indexType); else - { - LUAU_ASSERT(tt || get(follow(metaTable->table))); - reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location); - } } - - void visit(AstExprIndexExpr* indexExpr, ValueContext context) + else if (auto mt = get(exprType)) + { + return indexExprMetatableHelper(indexExpr, mt, exprType, indexType); + } + else if (auto cls = get(exprType)) + { + if (cls->indexer) + testIsSubtype(indexType, cls->indexer->indexType, indexExpr->index->location); + else + reportError(DynamicPropertyLookupOnClassesUnsafe{exprType}, indexExpr->location); + } + else if (get(exprType) && isOptional(exprType)) { - if (auto str = indexExpr->index->as()) + switch (shouldSuppressErrors(NotNull{&normalizer}, exprType)) { - TypeId astIndexExprType = lookupType(indexExpr->index); - const std::string stringValue(str->value.data, str->value.size); - visitExprName(indexExpr->expr, indexExpr->location, stringValue, context, astIndexExprType); - return; + case ErrorSuppression::Suppress: + break; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, indexExpr->location); + [[fallthrough]]; + case ErrorSuppression::DoNotSuppress: + reportError(OptionalValueAccess{exprType}, indexExpr->location); } + } + else if (auto ut = get(exprType)) + { + // if all of the types are a table type, the union must be a table, and so we shouldn't error. + if (!std::all_of(begin(ut), end(ut), getTableType)) + reportError(NotATable{exprType}, indexExpr->location); + } + else if (auto it = get(exprType)) + { + // if any of the types are a table type, the intersection must be a table, and so we shouldn't error. + if (!std::any_of(begin(it), end(it), getTableType)) + reportError(NotATable{exprType}, indexExpr->location); + } + else if (get(exprType) || isErrorSuppressing(indexExpr->location, exprType)) + { + // Nothing + } + else + reportError(NotATable{exprType}, indexExpr->location); +} - visit(indexExpr->expr, ValueContext::RValue); - visit(indexExpr->index, ValueContext::RValue); +void TypeChecker2::visit(AstExprFunction* fn) +{ + auto StackPusher = pushStack(fn); - TypeId exprType = follow(lookupType(indexExpr->expr)); - TypeId indexType = follow(lookupType(indexExpr->index)); + visitGenerics(fn->generics, fn->genericPacks); - if (auto tt = get(exprType)) - { - if (tt->indexer) - testIsSubtype(indexType, tt->indexer->indexType, indexExpr->index->location); - else - reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location); - } - else if (auto mt = get(exprType)) - { - return indexExprMetatableHelper(indexExpr, mt, exprType, indexType); - } - else if (auto cls = get(exprType)) - { - if (cls->indexer) - testIsSubtype(indexType, cls->indexer->indexType, indexExpr->index->location); - else - reportError(DynamicPropertyLookupOnClassesUnsafe{exprType}, indexExpr->location); - } - else if (get(exprType) && isOptional(exprType)) - { - switch (shouldSuppressErrors(NotNull{&normalizer}, exprType)) - { - case ErrorSuppression::Suppress: - break; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, indexExpr->location); - // fallthrough intentional - case ErrorSuppression::DoNotSuppress: - reportError(OptionalValueAccess{exprType}, indexExpr->location); - } - } - else if (auto exprIntersection = get(exprType)) - { - for (TypeId part : exprIntersection) - { - (void)part; - } - } - else if (get(exprType) || isErrorSuppressing(indexExpr->location, exprType)) - { - // Nothing - } - else - reportError(NotATable{exprType}, indexExpr->location); - } + TypeId inferredFnTy = lookupType(fn); + functionDeclStack.push_back(inferredFnTy); - void visit(AstExprFunction* fn) + std::shared_ptr normalizedFnTy = normalizer.normalize(inferredFnTy); + if (!normalizedFnTy) + { + reportError(CodeTooComplex{}, fn->location); + } + else if (get(normalizedFnTy->errors)) { - auto StackPusher = pushStack(fn); + // Nothing + } + else if (!normalizedFnTy->hasFunctions()) + { + ice->ice("Internal error: Lambda has non-function type " + toString(inferredFnTy), fn->location); + } + else + { + if (1 != normalizedFnTy->functions.parts.size()) + ice->ice("Unexpected: Lambda has unexpected type " + toString(inferredFnTy), fn->location); - visitGenerics(fn->generics, fn->genericPacks); + const FunctionType* inferredFtv = get(normalizedFnTy->functions.parts.front()); + LUAU_ASSERT(inferredFtv); - TypeId inferredFnTy = lookupType(fn); - functionDeclStack.push_back(inferredFnTy); + // There is no way to write an annotation for the self argument, so we + // cannot do anything to check it. + auto argIt = begin(inferredFtv->argTypes); + if (fn->self) + ++argIt; - std::shared_ptr normalizedFnTy = normalizer.normalize(inferredFnTy); - if (!normalizedFnTy) - { - reportError(CodeTooComplex{}, fn->location); - } - else if (get(normalizedFnTy->errors)) - { - // Nothing - } - else if (!normalizedFnTy->hasFunctions()) + for (const auto& arg : fn->args) { - ice->ice("Internal error: Lambda has non-function type " + toString(inferredFnTy), fn->location); - } - else - { - if (1 != normalizedFnTy->functions.parts.size()) - ice->ice("Unexpected: Lambda has unexpected type " + toString(inferredFnTy), fn->location); - - const FunctionType* inferredFtv = get(normalizedFnTy->functions.parts.front()); - LUAU_ASSERT(inferredFtv); + if (argIt == end(inferredFtv->argTypes)) + break; - // There is no way to write an annotation for the self argument, so we - // cannot do anything to check it. - auto argIt = begin(inferredFtv->argTypes); - if (fn->self) - ++argIt; + TypeId inferredArgTy = *argIt; - for (const auto& arg : fn->args) + if (arg->annotation) { - if (argIt == end(inferredFtv->argTypes)) - break; + // we need to typecheck any argument annotations themselves. + visit(arg->annotation); - TypeId inferredArgTy = *argIt; + TypeId annotatedArgTy = lookupAnnotation(arg->annotation); + + testIsSubtype(inferredArgTy, annotatedArgTy, arg->location); + } + // Some Luau constructs can result in an argument type being + // reduced to never by inference. In this case, we want to + // report an error at the function, instead of reporting an + // error at every callsite. + if (is(follow(inferredArgTy))) + { + // If the annotation simplified to never, we don't want to + // even look at contributors. + bool explicitlyNever = false; if (arg->annotation) { - // we need to typecheck any argument annotations themselves. - visit(arg->annotation); - TypeId annotatedArgTy = lookupAnnotation(arg->annotation); - - testIsSubtype(inferredArgTy, annotatedArgTy, arg->location); + explicitlyNever = is(annotatedArgTy); } - // Some Luau constructs can result in an argument type being - // reduced to never by inference. In this case, we want to - // report an error at the function, instead of reporting an - // error at every callsite. - if (is(follow(inferredArgTy))) + // Not following here is deliberate: the contribution map is + // keyed by type pointer, but that type pointer has, at some + // point, been transmuted to a bound type pointing to never. + if (const auto contributors = module->upperBoundContributors.find(inferredArgTy); contributors && !explicitlyNever) { - // If the annotation simplified to never, we don't want to - // even look at contributors. - bool explicitlyNever = false; - if (arg->annotation) - { - TypeId annotatedArgTy = lookupAnnotation(arg->annotation); - explicitlyNever = is(annotatedArgTy); - } - - // Not following here is deliberate: the contribution map is - // keyed by type pointer, but that type pointer has, at some - // point, been transmuted to a bound type pointing to never. - if (const auto contributors = module->upperBoundContributors.find(inferredArgTy); contributors && !explicitlyNever) - { - // It's unfortunate that we can't link error messages - // together. For now, this will work. + // It's unfortunate that we can't link error messages + // together. For now, this will work. + reportError( + GenericError{format( + "Parameter '%s' has been reduced to never. This function is not callable with any possible value.", arg->name.value + )}, + arg->location + ); + for (const auto& [site, component] : *contributors) reportError( - GenericError{format( - "Parameter '%s' has been reduced to never. This function is not callable with any possible value.", arg->name.value - )}, - arg->location + ExtraInformation{ + format("Parameter '%s' is required to be a subtype of '%s' here.", arg->name.value, toString(component).c_str()) + }, + site ); - for (const auto& [site, component] : *contributors) - reportError( - ExtraInformation{ - format("Parameter '%s' is required to be a subtype of '%s' here.", arg->name.value, toString(component).c_str()) - }, - site - ); - } } - - ++argIt; } - // we need to typecheck the vararg annotation, if it exists. - if (fn->vararg && fn->varargAnnotation) - visit(fn->varargAnnotation); - - bool reachesImplicitReturn = getFallthrough(fn->body) != nullptr; - if (reachesImplicitReturn && !allowsNoReturnValues(follow(inferredFtv->retTypes))) - reportError(FunctionExitsWithoutReturning{inferredFtv->retTypes}, getEndLocation(fn)); + ++argIt; } - visit(fn->body); + // we need to typecheck the vararg annotation, if it exists. + if (fn->vararg && fn->varargAnnotation) + visit(fn->varargAnnotation); - // we need to typecheck the return annotation itself, if it exists. - if (fn->returnAnnotation) - visit(*fn->returnAnnotation); + bool reachesImplicitReturn = getFallthrough(fn->body) != nullptr; + if (reachesImplicitReturn && !allowsNoReturnValues(follow(inferredFtv->retTypes))) + reportError(FunctionExitsWithoutReturning{inferredFtv->retTypes}, getEndLocation(fn)); + } + visit(fn->body); - // If the function type has a function annotation, we need to see if we can suggest an annotation - if (normalizedFnTy) - { - const FunctionType* inferredFtv = get(normalizedFnTy->functions.parts.front()); - LUAU_ASSERT(inferredFtv); + // we need to typecheck the return annotation itself, if it exists. + if (fn->returnAnnotation) + visit(*fn->returnAnnotation); + + + // If the function type has a function annotation, we need to see if we can suggest an annotation + if (normalizedFnTy) + { + const FunctionType* inferredFtv = get(normalizedFnTy->functions.parts.front()); + LUAU_ASSERT(inferredFtv); - TypeFunctionReductionGuesser guesser{NotNull{&module->internalTypes}, builtinTypes, NotNull{&normalizer}}; - for (TypeId retTy : inferredFtv->retTypes) + TypeFunctionReductionGuesser guesser{NotNull{&module->internalTypes}, builtinTypes, NotNull{&normalizer}}; + for (TypeId retTy : inferredFtv->retTypes) + { + if (get(follow(retTy))) { - if (get(follow(retTy))) - { - TypeFunctionReductionGuessResult result = guesser.guessTypeFunctionReductionForFunctionExpr(*fn, inferredFtv, retTy); - if (result.shouldRecommendAnnotation && !get(result.guessedReturnType)) - reportError( - ExplicitFunctionAnnotationRecommended{std::move(result.guessedFunctionAnnotations), result.guessedReturnType}, - fn->location - ); - } + TypeFunctionReductionGuessResult result = guesser.guessTypeFunctionReductionForFunctionExpr(*fn, inferredFtv, retTy); + if (result.shouldRecommendAnnotation && !get(result.guessedReturnType)) + reportError( + ExplicitFunctionAnnotationRecommended{std::move(result.guessedFunctionAnnotations), result.guessedReturnType}, fn->location + ); } } - - functionDeclStack.pop_back(); } - void visit(AstExprTable* expr) + functionDeclStack.pop_back(); +} + +void TypeChecker2::visit(AstExprTable* expr) +{ + // TODO! + for (const AstExprTable::Item& item : expr->items) { - // TODO! - for (const AstExprTable::Item& item : expr->items) - { - if (item.key) - visit(item.key, ValueContext::LValue); - visit(item.value, ValueContext::RValue); - } + if (item.key) + visit(item.key, ValueContext::LValue); + visit(item.value, ValueContext::RValue); } +} - void visit(AstExprUnary* expr) - { - visit(expr->expr, ValueContext::RValue); +void TypeChecker2::visit(AstExprUnary* expr) +{ + visit(expr->expr, ValueContext::RValue); - TypeId operandType = lookupType(expr->expr); - TypeId resultType = lookupType(expr); + TypeId operandType = lookupType(expr->expr); + TypeId resultType = lookupType(expr); - if (isErrorSuppressing(expr->expr->location, operandType)) - return; + if (isErrorSuppressing(expr->expr->location, operandType)) + return; - if (auto it = kUnaryOpMetamethods.find(expr->op); it != kUnaryOpMetamethods.end()) + if (auto it = kUnaryOpMetamethods.find(expr->op); it != kUnaryOpMetamethods.end()) + { + std::optional mm = findMetatableEntry(builtinTypes, module->errors, operandType, it->second, expr->location); + if (mm) { - std::optional mm = findMetatableEntry(builtinTypes, module->errors, operandType, it->second, expr->location); - if (mm) + if (const FunctionType* ftv = get(follow(*mm))) { - if (const FunctionType* ftv = get(follow(*mm))) + if (std::optional ret = first(ftv->retTypes)) { - if (std::optional ret = first(ftv->retTypes)) - { - if (expr->op == AstExprUnary::Op::Len) - { - testIsSubtype(follow(*ret), builtinTypes->numberType, expr->location); - } - } - else + if (expr->op == AstExprUnary::Op::Len) { - reportError(GenericError{format("Metamethod '%s' must return a value", it->second)}, expr->location); - } - - std::optional firstArg = first(ftv->argTypes); - if (!firstArg) - { - reportError(GenericError{"__unm metamethod must accept one argument"}, expr->location); - return; + testIsSubtype(follow(*ret), builtinTypes->numberType, expr->location); } - - TypePackId expectedArgs = module->internalTypes.addTypePack({operandType}); - TypePackId expectedRet = module->internalTypes.addTypePack({resultType}); - - TypeId expectedFunction = module->internalTypes.addType(FunctionType{expectedArgs, expectedRet}); - - bool success = testIsSubtype(*mm, expectedFunction, expr->location); - if (!success) - return; + } + else + { + reportError(GenericError{format("Metamethod '%s' must return a value", it->second)}, expr->location); } - return; - } - } + std::optional firstArg = first(ftv->argTypes); + if (!firstArg) + { + reportError(GenericError{"__unm metamethod must accept one argument"}, expr->location); + return; + } - if (expr->op == AstExprUnary::Op::Len) - { - DenseHashSet seen{nullptr}; - int recursionCount = 0; - std::shared_ptr nty = normalizer.normalize(operandType); + TypePackId expectedArgs = module->internalTypes.addTypePack({operandType}); + TypePackId expectedRet = module->internalTypes.addTypePack({resultType}); - if (nty && nty->shouldSuppressErrors()) - return; + TypeId expectedFunction = module->internalTypes.addType(FunctionType{expectedArgs, expectedRet}); - switch (normalizer.isInhabited(nty.get())) - { - case NormalizationResult::True: - break; - case NormalizationResult::False: - return; - case NormalizationResult::HitLimits: - reportError(NormalizationTooComplex{}, expr->location); + bool success = testIsSubtype(*mm, expectedFunction, expr->location); + if (!success) return; } - if (!hasLength(operandType, seen, &recursionCount)) - { - if (isOptional(operandType)) - reportError(OptionalValueAccess{operandType}, expr->location); - else - reportError(NotATable{operandType}, expr->location); - } - } - else if (expr->op == AstExprUnary::Op::Minus) - { - testIsSubtype(operandType, builtinTypes->numberType, expr->location); - } - else if (expr->op == AstExprUnary::Op::Not) - { - } - else - { - LUAU_ASSERT(!"Unhandled unary operator"); + return; } } - TypeId visit(AstExprBinary* expr, AstNode* overrideKey = nullptr) + if (expr->op == AstExprUnary::Op::Len) { - visit(expr->left, ValueContext::RValue); - visit(expr->right, ValueContext::RValue); + DenseHashSet seen{nullptr}; + int recursionCount = 0; + std::shared_ptr nty = normalizer.normalize(operandType); - NotNull scope = stack.back(); - - bool isEquality = expr->op == AstExprBinary::Op::CompareEq || expr->op == AstExprBinary::Op::CompareNe; - bool isComparison = expr->op >= AstExprBinary::Op::CompareEq && expr->op <= AstExprBinary::Op::CompareGe; - bool isLogical = expr->op == AstExprBinary::Op::And || expr->op == AstExprBinary::Op::Or; + if (nty && nty->shouldSuppressErrors()) + return; - TypeId leftType = follow(lookupType(expr->left)); - TypeId rightType = follow(lookupType(expr->right)); - TypeId expectedResult = follow(lookupType(expr)); - - if (get(expectedResult)) + switch (normalizer.isInhabited(nty.get())) { - checkForInternalTypeFunction(expectedResult, expr->location); - return expectedResult; + case NormalizationResult::True: + break; + case NormalizationResult::False: + return; + case NormalizationResult::HitLimits: + reportError(NormalizationTooComplex{}, expr->location); + return; } - if (expr->op == AstExprBinary::Op::Or) + if (!hasLength(operandType, seen, &recursionCount)) { - leftType = stripNil(builtinTypes, module->internalTypes, leftType); + if (isOptional(operandType)) + reportError(OptionalValueAccess{operandType}, expr->location); + else + reportError(NotATable{operandType}, expr->location); } + } + else if (expr->op == AstExprUnary::Op::Minus) + { + testIsSubtype(operandType, builtinTypes->numberType, expr->location); + } + else if (expr->op == AstExprUnary::Op::Not) + { + } + else + { + LUAU_ASSERT(!"Unhandled unary operator"); + } +} - std::shared_ptr normLeft = normalizer.normalize(leftType); - std::shared_ptr normRight = normalizer.normalize(rightType); +TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) +{ + visit(expr->left, ValueContext::RValue); + visit(expr->right, ValueContext::RValue); - bool isStringOperation = - (normLeft ? normLeft->isSubtypeOfString() : isString(leftType)) && (normRight ? normRight->isSubtypeOfString() : isString(rightType)); - leftType = follow(leftType); - if (get(leftType) || get(leftType) || get(leftType)) - return leftType; - else if (get(rightType) || get(rightType) || get(rightType)) - return rightType; - else if ((normLeft && normLeft->shouldSuppressErrors()) || (normRight && normRight->shouldSuppressErrors())) - return builtinTypes->anyType; // we can't say anything better if it's error suppressing but not any or error alone. + NotNull scope = stack.back(); - if ((get(leftType) || get(leftType) || get(leftType)) && !isEquality && !isLogical) - { - auto name = getIdentifierOfBaseVar(expr->left); - reportError( - CannotInferBinaryOperation{ - expr->op, name, isComparison ? CannotInferBinaryOperation::OpKind::Comparison : CannotInferBinaryOperation::OpKind::Operation - }, - expr->location - ); - return leftType; - } + bool isEquality = expr->op == AstExprBinary::Op::CompareEq || expr->op == AstExprBinary::Op::CompareNe; + bool isComparison = expr->op >= AstExprBinary::Op::CompareEq && expr->op <= AstExprBinary::Op::CompareGe; + bool isLogical = expr->op == AstExprBinary::Op::And || expr->op == AstExprBinary::Op::Or; - NormalizationResult typesHaveIntersection = normalizer.isIntersectionInhabited(leftType, rightType); - if (auto it = kBinaryOpMetamethods.find(expr->op); it != kBinaryOpMetamethods.end()) - { - std::optional leftMt = getMetatable(leftType, builtinTypes); - std::optional rightMt = getMetatable(rightType, builtinTypes); - bool matches = leftMt == rightMt; + TypeId leftType = follow(lookupType(expr->left)); + TypeId rightType = follow(lookupType(expr->right)); + TypeId expectedResult = follow(lookupType(expr)); + + if (get(expectedResult)) + { + checkForInternalTypeFunction(expectedResult, expr->location); + return expectedResult; + } + + if (expr->op == AstExprBinary::Op::Or) + { + leftType = stripNil(builtinTypes, module->internalTypes, leftType); + } + + std::shared_ptr normLeft = normalizer.normalize(leftType); + std::shared_ptr normRight = normalizer.normalize(rightType); + + bool isStringOperation = + (normLeft ? normLeft->isSubtypeOfString() : isString(leftType)) && (normRight ? normRight->isSubtypeOfString() : isString(rightType)); + leftType = follow(leftType); + if (get(leftType) || get(leftType) || get(leftType)) + return leftType; + else if (get(rightType) || get(rightType) || get(rightType)) + return rightType; + else if ((normLeft && normLeft->shouldSuppressErrors()) || (normRight && normRight->shouldSuppressErrors())) + return builtinTypes->anyType; // we can't say anything better if it's error suppressing but not any or error alone. + + if ((get(leftType) || get(leftType) || get(leftType)) && !isEquality && !isLogical) + { + auto name = getIdentifierOfBaseVar(expr->left); + reportError( + CannotInferBinaryOperation{ + expr->op, name, isComparison ? CannotInferBinaryOperation::OpKind::Comparison : CannotInferBinaryOperation::OpKind::Operation + }, + expr->location + ); + return leftType; + } + + NormalizationResult typesHaveIntersection = normalizer.isIntersectionInhabited(leftType, rightType); + if (auto it = kBinaryOpMetamethods.find(expr->op); it != kBinaryOpMetamethods.end()) + { + std::optional leftMt = getMetatable(leftType, builtinTypes); + std::optional rightMt = getMetatable(rightType, builtinTypes); + bool matches = leftMt == rightMt; - if (isEquality && !matches) + if (isEquality && !matches) + { + auto testUnion = [&matches, builtinTypes = this->builtinTypes](const UnionType* utv, std::optional otherMt) { - auto testUnion = [&matches, builtinTypes = this->builtinTypes](const UnionType* utv, std::optional otherMt) + for (TypeId option : utv) { - for (TypeId option : utv) + if (getMetatable(follow(option), builtinTypes) == otherMt) { - if (getMetatable(follow(option), builtinTypes) == otherMt) - { - matches = true; - break; - } + matches = true; + break; } - }; - - if (const UnionType* utv = get(leftType); utv && rightMt) - { - testUnion(utv, rightMt); } + }; - if (const UnionType* utv = get(rightType); utv && leftMt && !matches) - { - testUnion(utv, leftMt); - } - } - - // If we're working with things that are not tables, the metatable comparisons above are a little excessive - // It's ok for one type to have a meta table and the other to not. In that case, we should fall back on - // checking if the intersection of the types is inhabited. If `typesHaveIntersection` failed due to limits, - // TODO: Maybe add more checks here (e.g. for functions, classes, etc) - if (!(get(leftType) || get(rightType))) - if (!leftMt.has_value() || !rightMt.has_value()) - matches = matches || typesHaveIntersection != NormalizationResult::False; - - if (!matches && isComparison) + if (const UnionType* utv = get(leftType); utv && rightMt) { - reportError( - GenericError{format( - "Types %s and %s cannot be compared with %s because they do not have the same metatable", - toString(leftType).c_str(), - toString(rightType).c_str(), - toString(expr->op).c_str() - )}, - expr->location - ); - - return builtinTypes->errorRecoveryType(); + testUnion(utv, rightMt); } - std::optional mm; - if (std::optional leftMm = findMetatableEntry(builtinTypes, module->errors, leftType, it->second, expr->left->location)) - mm = leftMm; - else if (std::optional rightMm = findMetatableEntry(builtinTypes, module->errors, rightType, it->second, expr->right->location)) + if (const UnionType* utv = get(rightType); utv && leftMt && !matches) { - mm = rightMm; - std::swap(leftType, rightType); + testUnion(utv, leftMt); } + } - if (mm) - { - AstNode* key = expr; - if (overrideKey != nullptr) - key = overrideKey; + // If we're working with things that are not tables, the metatable comparisons above are a little excessive + // It's ok for one type to have a meta table and the other to not. In that case, we should fall back on + // checking if the intersection of the types is inhabited. If `typesHaveIntersection` failed due to limits, + // TODO: Maybe add more checks here (e.g. for functions, classes, etc) + if (!(get(leftType) || get(rightType))) + if (!leftMt.has_value() || !rightMt.has_value()) + matches = matches || typesHaveIntersection != NormalizationResult::False; - TypeId* selectedOverloadTy = module->astOverloadResolvedTypes.find(key); - if (!selectedOverloadTy) - { - // reportError(CodeTooComplex{}, expr->location); - // was handled by a type function - return expectedResult; - } - - else if (const FunctionType* ftv = get(follow(*selectedOverloadTy))) - { - TypePackId expectedArgs; - // For >= and > we invoke __lt and __le respectively with - // swapped argument ordering. - if (expr->op == AstExprBinary::Op::CompareGe || expr->op == AstExprBinary::Op::CompareGt) - { - expectedArgs = module->internalTypes.addTypePack({rightType, leftType}); - } - else - { - expectedArgs = module->internalTypes.addTypePack({leftType, rightType}); - } + if (!matches && isComparison) + { + reportError( + GenericError{format( + "Types %s and %s cannot be compared with %s because they do not have the same metatable", + toString(leftType).c_str(), + toString(rightType).c_str(), + toString(expr->op).c_str() + )}, + expr->location + ); - TypePackId expectedRets; - if (expr->op == AstExprBinary::CompareEq || expr->op == AstExprBinary::CompareNe || expr->op == AstExprBinary::CompareGe || - expr->op == AstExprBinary::CompareGt || expr->op == AstExprBinary::Op::CompareLe || expr->op == AstExprBinary::Op::CompareLt) - { - expectedRets = module->internalTypes.addTypePack({builtinTypes->booleanType}); - } - else - { - expectedRets = module->internalTypes.addTypePack({module->internalTypes.freshType(scope, TypeLevel{})}); - } + return builtinTypes->errorRecoveryType(); + } - TypeId expectedTy = module->internalTypes.addType(FunctionType(expectedArgs, expectedRets)); + std::optional mm; + if (std::optional leftMm = findMetatableEntry(builtinTypes, module->errors, leftType, it->second, expr->left->location)) + mm = leftMm; + else if (std::optional rightMm = findMetatableEntry(builtinTypes, module->errors, rightType, it->second, expr->right->location)) + { + mm = rightMm; + std::swap(leftType, rightType); + } - testIsSubtype(follow(*mm), expectedTy, expr->location); + if (mm) + { + AstNode* key = expr; + if (overrideKey != nullptr) + key = overrideKey; - std::optional ret = first(ftv->retTypes); - if (ret) - { - if (isComparison) - { - if (!isBoolean(follow(*ret))) - { - reportError(GenericError{format("Metamethod '%s' must return a boolean", it->second)}, expr->location); - } + TypeId* selectedOverloadTy = module->astOverloadResolvedTypes.find(key); + if (!selectedOverloadTy) + { + // reportError(CodeTooComplex{}, expr->location); + // was handled by a type function + return expectedResult; + } - return builtinTypes->booleanType; - } - else - { - return follow(*ret); - } - } - else - { - if (isComparison) - { - reportError(GenericError{format("Metamethod '%s' must return a boolean", it->second)}, expr->location); - } - else - { - reportError(GenericError{format("Metamethod '%s' must return a value", it->second)}, expr->location); - } + else if (const FunctionType* ftv = get(follow(*selectedOverloadTy))) + { + TypePackId expectedArgs; + // For >= and > we invoke __lt and __le respectively with + // swapped argument ordering. + if (expr->op == AstExprBinary::Op::CompareGe || expr->op == AstExprBinary::Op::CompareGt) + { + expectedArgs = module->internalTypes.addTypePack({rightType, leftType}); + } + else + { + expectedArgs = module->internalTypes.addTypePack({leftType, rightType}); + } - return builtinTypes->errorRecoveryType(); - } + TypePackId expectedRets; + if (expr->op == AstExprBinary::CompareEq || expr->op == AstExprBinary::CompareNe || expr->op == AstExprBinary::CompareGe || + expr->op == AstExprBinary::CompareGt || expr->op == AstExprBinary::Op::CompareLe || expr->op == AstExprBinary::Op::CompareLt) + { + expectedRets = module->internalTypes.addTypePack({builtinTypes->booleanType}); } else { - reportError(CannotCallNonFunction{*mm}, expr->location); + expectedRets = module->internalTypes.addTypePack({module->internalTypes.freshType(scope, TypeLevel{})}); } - return builtinTypes->errorRecoveryType(); - } - // If this is a string comparison, or a concatenation of strings, we - // want to fall through to primitive behavior. - else if (!isEquality && !(isStringOperation && (expr->op == AstExprBinary::Op::Concat || isComparison))) - { - if ((leftMt && !isString(leftType)) || (rightMt && !isString(rightType))) + TypeId expectedTy = module->internalTypes.addType(FunctionType(expectedArgs, expectedRets)); + + testIsSubtype(follow(*mm), expectedTy, expr->location); + + std::optional ret = first(ftv->retTypes); + if (ret) { if (isComparison) { - reportError( - GenericError{format( - "Types '%s' and '%s' cannot be compared with %s because neither type's metatable has a '%s' metamethod", - toString(leftType).c_str(), - toString(rightType).c_str(), - toString(expr->op).c_str(), - it->second - )}, - expr->location - ); + if (!isBoolean(follow(*ret))) + { + reportError(GenericError{format("Metamethod '%s' must return a boolean", it->second)}, expr->location); + } + + return builtinTypes->booleanType; } else { - reportError( - GenericError{format( - "Operator %s is not applicable for '%s' and '%s' because neither type's metatable has a '%s' metamethod", - toString(expr->op).c_str(), - toString(leftType).c_str(), - toString(rightType).c_str(), - it->second - )}, - expr->location - ); + return follow(*ret); } - - return builtinTypes->errorRecoveryType(); } - else if (!leftMt && !rightMt && (get(leftType) || get(rightType))) + else { if (isComparison) { - reportError( - GenericError{format( - "Types '%s' and '%s' cannot be compared with %s because neither type has a metatable", - toString(leftType).c_str(), - toString(rightType).c_str(), - toString(expr->op).c_str() - )}, - expr->location - ); + reportError(GenericError{format("Metamethod '%s' must return a boolean", it->second)}, expr->location); } else { - reportError( - GenericError{format( - "Operator %s is not applicable for '%s' and '%s' because neither type has a metatable", - toString(expr->op).c_str(), - toString(leftType).c_str(), - toString(rightType).c_str() - )}, - expr->location - ); + reportError(GenericError{format("Metamethod '%s' must return a value", it->second)}, expr->location); } return builtinTypes->errorRecoveryType(); } } - } - - switch (expr->op) - { - case AstExprBinary::Op::Add: - case AstExprBinary::Op::Sub: - case AstExprBinary::Op::Mul: - case AstExprBinary::Op::Div: - case AstExprBinary::Op::FloorDiv: - case AstExprBinary::Op::Pow: - case AstExprBinary::Op::Mod: - testIsSubtype(leftType, builtinTypes->numberType, expr->left->location); - testIsSubtype(rightType, builtinTypes->numberType, expr->right->location); - - return builtinTypes->numberType; - case AstExprBinary::Op::Concat: - testIsSubtype(leftType, builtinTypes->stringType, expr->left->location); - testIsSubtype(rightType, builtinTypes->stringType, expr->right->location); - - return builtinTypes->stringType; - case AstExprBinary::Op::CompareGe: - case AstExprBinary::Op::CompareGt: - case AstExprBinary::Op::CompareLe: - case AstExprBinary::Op::CompareLt: - { - if (normLeft && normLeft->shouldSuppressErrors()) - return builtinTypes->booleanType; - - // if we're comparing against an uninhabited type, it's unobservable that the comparison did not run - if (normLeft && normalizer.isInhabited(normLeft.get()) == NormalizationResult::False) - return builtinTypes->booleanType; - - if (normLeft && normLeft->isExactlyNumber()) - { - testIsSubtype(rightType, builtinTypes->numberType, expr->right->location); - return builtinTypes->booleanType; - } - - if (normLeft && normLeft->isSubtypeOfString()) + else { - testIsSubtype(rightType, builtinTypes->stringType, expr->right->location); - return builtinTypes->booleanType; + reportError(CannotCallNonFunction{*mm}, expr->location); } - reportError( - GenericError{format( - "Types '%s' and '%s' cannot be compared with relational operator %s", - toString(leftType).c_str(), - toString(rightType).c_str(), - toString(expr->op).c_str() - )}, - expr->location - ); return builtinTypes->errorRecoveryType(); } + // If this is a string comparison, or a concatenation of strings, we + // want to fall through to primitive behavior. + else if (!isEquality && !(isStringOperation && (expr->op == AstExprBinary::Op::Concat || isComparison))) + { + if ((leftMt && !isString(leftType)) || (rightMt && !isString(rightType))) + { + if (isComparison) + { + reportError( + GenericError{format( + "Types '%s' and '%s' cannot be compared with %s because neither type's metatable has a '%s' metamethod", + toString(leftType).c_str(), + toString(rightType).c_str(), + toString(expr->op).c_str(), + it->second + )}, + expr->location + ); + } + else + { + reportError( + GenericError{format( + "Operator %s is not applicable for '%s' and '%s' because neither type's metatable has a '%s' metamethod", + toString(expr->op).c_str(), + toString(leftType).c_str(), + toString(rightType).c_str(), + it->second + )}, + expr->location + ); + } - case AstExprBinary::Op::And: - case AstExprBinary::Op::Or: - case AstExprBinary::Op::CompareEq: - case AstExprBinary::Op::CompareNe: - // Ugly case: we don't care about this possibility, because a - // compound assignment will never exist with one of these operators. - return builtinTypes->anyType; - default: - // Unhandled AstExprBinary::Op possibility. - LUAU_ASSERT(false); - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorRecoveryType(); + } + else if (!leftMt && !rightMt && (get(leftType) || get(rightType))) + { + if (isComparison) + { + reportError( + GenericError{format( + "Types '%s' and '%s' cannot be compared with %s because neither type has a metatable", + toString(leftType).c_str(), + toString(rightType).c_str(), + toString(expr->op).c_str() + )}, + expr->location + ); + } + else + { + reportError( + GenericError{format( + "Operator %s is not applicable for '%s' and '%s' because neither type has a metatable", + toString(expr->op).c_str(), + toString(leftType).c_str(), + toString(rightType).c_str() + )}, + expr->location + ); + } + + return builtinTypes->errorRecoveryType(); + } } } - void visit(AstExprTypeAssertion* expr) + switch (expr->op) { - visit(expr->expr, ValueContext::RValue); - visit(expr->annotation); + case AstExprBinary::Op::Add: + case AstExprBinary::Op::Sub: + case AstExprBinary::Op::Mul: + case AstExprBinary::Op::Div: + case AstExprBinary::Op::FloorDiv: + case AstExprBinary::Op::Pow: + case AstExprBinary::Op::Mod: + testIsSubtype(leftType, builtinTypes->numberType, expr->left->location); + testIsSubtype(rightType, builtinTypes->numberType, expr->right->location); - TypeId annotationType = lookupAnnotation(expr->annotation); - TypeId computedType = lookupType(expr->expr); + return builtinTypes->numberType; + case AstExprBinary::Op::Concat: + testIsSubtype(leftType, builtinTypes->stringType, expr->left->location); + testIsSubtype(rightType, builtinTypes->stringType, expr->right->location); - switch (shouldSuppressErrors(NotNull{&normalizer}, computedType).orElse(shouldSuppressErrors(NotNull{&normalizer}, annotationType))) - { - case ErrorSuppression::Suppress: - return; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, expr->location); - return; - case ErrorSuppression::DoNotSuppress: - break; - } + return builtinTypes->stringType; + case AstExprBinary::Op::CompareGe: + case AstExprBinary::Op::CompareGt: + case AstExprBinary::Op::CompareLe: + case AstExprBinary::Op::CompareLt: + { + if (normLeft && normLeft->shouldSuppressErrors()) + return builtinTypes->booleanType; + + // if we're comparing against an uninhabited type, it's unobservable that the comparison did not run + if (normLeft && normalizer.isInhabited(normLeft.get()) == NormalizationResult::False) + return builtinTypes->booleanType; - switch (normalizer.isInhabited(computedType)) + if (normLeft && normLeft->isExactlyNumber()) { - case NormalizationResult::True: - break; - case NormalizationResult::False: - return; - case NormalizationResult::HitLimits: - reportError(NormalizationTooComplex{}, expr->location); - return; + testIsSubtype(rightType, builtinTypes->numberType, expr->right->location); + return builtinTypes->booleanType; } - switch (normalizer.isIntersectionInhabited(computedType, annotationType)) + if (normLeft && normLeft->isSubtypeOfString()) { - case NormalizationResult::True: - return; - case NormalizationResult::False: - reportError(TypesAreUnrelated{computedType, annotationType}, expr->location); - break; - case NormalizationResult::HitLimits: - reportError(NormalizationTooComplex{}, expr->location); - break; - } + testIsSubtype(rightType, builtinTypes->stringType, expr->right->location); + return builtinTypes->booleanType; + } + + reportError( + GenericError{format( + "Types '%s' and '%s' cannot be compared with relational operator %s", + toString(leftType).c_str(), + toString(rightType).c_str(), + toString(expr->op).c_str() + )}, + expr->location + ); + return builtinTypes->errorRecoveryType(); + } + + case AstExprBinary::Op::And: + case AstExprBinary::Op::Or: + case AstExprBinary::Op::CompareEq: + case AstExprBinary::Op::CompareNe: + // Ugly case: we don't care about this possibility, because a + // compound assignment will never exist with one of these operators. + return builtinTypes->anyType; + default: + // Unhandled AstExprBinary::Op possibility. + LUAU_ASSERT(false); + return builtinTypes->errorRecoveryType(); } +} - void visit(AstExprIfElse* expr) +void TypeChecker2::visit(AstExprTypeAssertion* expr) +{ + visit(expr->expr, ValueContext::RValue); + visit(expr->annotation); + + TypeId annotationType = lookupAnnotation(expr->annotation); + TypeId computedType = lookupType(expr->expr); + + switch (shouldSuppressErrors(NotNull{&normalizer}, computedType).orElse(shouldSuppressErrors(NotNull{&normalizer}, annotationType))) { - // TODO! - visit(expr->condition, ValueContext::RValue); - visit(expr->trueExpr, ValueContext::RValue); - visit(expr->falseExpr, ValueContext::RValue); + case ErrorSuppression::Suppress: + return; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, expr->location); + return; + case ErrorSuppression::DoNotSuppress: + break; } - void visit(AstExprInterpString* interpString) + switch (normalizer.isInhabited(computedType)) { - for (AstExpr* expr : interpString->expressions) - visit(expr, ValueContext::RValue); + case NormalizationResult::True: + break; + case NormalizationResult::False: + return; + case NormalizationResult::HitLimits: + reportError(NormalizationTooComplex{}, expr->location); + return; } - void visit(AstExprError* expr) + switch (normalizer.isIntersectionInhabited(computedType, annotationType)) { - // TODO! - for (AstExpr* e : expr->expressions) - visit(e, ValueContext::RValue); + case NormalizationResult::True: + return; + case NormalizationResult::False: + reportError(TypesAreUnrelated{computedType, annotationType}, expr->location); + break; + case NormalizationResult::HitLimits: + reportError(NormalizationTooComplex{}, expr->location); + break; } +} - /** Extract a TypeId for the first type of the provided pack. - * - * Note that this may require modifying some types. I hope this doesn't cause problems! - */ - TypeId flattenPack(TypePackId pack) - { - pack = follow(pack); +void TypeChecker2::visit(AstExprIfElse* expr) +{ + // TODO! + visit(expr->condition, ValueContext::RValue); + visit(expr->trueExpr, ValueContext::RValue); + visit(expr->falseExpr, ValueContext::RValue); +} - if (auto fst = first(pack, /*ignoreHiddenVariadics*/ false)) - return *fst; - else if (auto ftp = get(pack)) - { - TypeId result = module->internalTypes.addType(FreeType{ftp->scope}); - TypePackId freeTail = module->internalTypes.addTypePack(FreeTypePack{ftp->scope}); +void TypeChecker2::visit(AstExprInterpString* interpString) +{ + for (AstExpr* expr : interpString->expressions) + visit(expr, ValueContext::RValue); +} - TypePack* resultPack = emplaceTypePack(asMutable(pack)); - resultPack->head.assign(1, result); - resultPack->tail = freeTail; +void TypeChecker2::visit(AstExprError* expr) +{ + // TODO! + for (AstExpr* e : expr->expressions) + visit(e, ValueContext::RValue); +} - return result; - } - else if (get(pack)) - return builtinTypes->errorRecoveryType(); - else if (finite(pack) && size(pack) == 0) - return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` - else - ice->ice("flattenPack got a weird pack!"); - } +TypeId TypeChecker2::flattenPack(TypePackId pack) +{ + pack = follow(pack); - void visitGenerics(AstArray generics, AstArray genericPacks) + if (auto fst = first(pack, /*ignoreHiddenVariadics*/ false)) + return *fst; + else if (auto ftp = get(pack)) { - DenseHashSet seen{AstName{}}; + TypeId result = module->internalTypes.addType(FreeType{ftp->scope}); + TypePackId freeTail = module->internalTypes.addTypePack(FreeTypePack{ftp->scope}); - for (const auto& g : generics) - { - if (seen.contains(g.name)) - reportError(DuplicateGenericParameter{g.name.value}, g.location); - else - seen.insert(g.name); + TypePack* resultPack = emplaceTypePack(asMutable(pack)); + resultPack->head.assign(1, result); + resultPack->tail = freeTail; - if (g.defaultValue) - visit(g.defaultValue); - } + return result; + } + else if (get(pack)) + return builtinTypes->errorRecoveryType(); + else if (finite(pack) && size(pack) == 0) + return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` + else + ice->ice("flattenPack got a weird pack!"); +} - for (const auto& g : genericPacks) - { - if (seen.contains(g.name)) - reportError(DuplicateGenericParameter{g.name.value}, g.location); - else - seen.insert(g.name); +void TypeChecker2::visitGenerics(AstArray generics, AstArray genericPacks) +{ + DenseHashSet seen{AstName{}}; - if (g.defaultValue) - visit(g.defaultValue); - } + for (const auto& g : generics) + { + if (seen.contains(g.name)) + reportError(DuplicateGenericParameter{g.name.value}, g.location); + else + seen.insert(g.name); + + if (g.defaultValue) + visit(g.defaultValue); } - void visit(AstType* ty) + for (const auto& g : genericPacks) { - TypeId* resolvedTy = module->astResolvedTypes.find(ty); - if (resolvedTy) - checkForTypeFunctionInhabitance(follow(*resolvedTy), ty->location); + if (seen.contains(g.name)) + reportError(DuplicateGenericParameter{g.name.value}, g.location); + else + seen.insert(g.name); - if (auto t = ty->as()) - return visit(t); - else if (auto t = ty->as()) - return visit(t); - else if (auto t = ty->as()) - return visit(t); - else if (auto t = ty->as()) - return visit(t); - else if (auto t = ty->as()) - return visit(t); - else if (auto t = ty->as()) - return visit(t); + if (g.defaultValue) + visit(g.defaultValue); } +} - void visit(AstTypeReference* ty) - { - // No further validation is necessary in this case. The main logic for - // _luau_print is contained in lookupAnnotation. - if (FFlag::DebugLuauMagicTypes && ty->name == "_luau_print") - return; +void TypeChecker2::visit(AstType* ty) +{ + TypeId* resolvedTy = module->astResolvedTypes.find(ty); + if (resolvedTy) + checkForTypeFunctionInhabitance(follow(*resolvedTy), ty->location); + + if (auto t = ty->as()) + return visit(t); + else if (auto t = ty->as()) + return visit(t); + else if (auto t = ty->as()) + return visit(t); + else if (auto t = ty->as()) + return visit(t); + else if (auto t = ty->as()) + return visit(t); + else if (auto t = ty->as()) + return visit(t); +} - for (const AstTypeOrPack& param : ty->parameters) - { - if (param.type) - visit(param.type); - else - visit(param.typePack); - } +void TypeChecker2::visit(AstTypeReference* ty) +{ + // No further validation is necessary in this case. The main logic for + // _luau_print is contained in lookupAnnotation. + if (FFlag::DebugLuauMagicTypes && ty->name == "_luau_print") + return; + + for (const AstTypeOrPack& param : ty->parameters) + { + if (param.type) + visit(param.type); + else + visit(param.typePack); + } - Scope* scope = findInnermostScope(ty->location); - LUAU_ASSERT(scope); + Scope* scope = findInnermostScope(ty->location); + LUAU_ASSERT(scope); - std::optional alias = - (ty->prefix) ? scope->lookupImportedType(ty->prefix->value, ty->name.value) : scope->lookupType(ty->name.value); + std::optional alias = (ty->prefix) ? scope->lookupImportedType(ty->prefix->value, ty->name.value) : scope->lookupType(ty->name.value); - if (alias.has_value()) - { - size_t typesRequired = alias->typeParams.size(); - size_t packsRequired = alias->typePackParams.size(); + if (alias.has_value()) + { + size_t typesRequired = alias->typeParams.size(); + size_t packsRequired = alias->typePackParams.size(); - bool hasDefaultTypes = std::any_of( - alias->typeParams.begin(), - alias->typeParams.end(), - [](auto&& el) - { - return el.defaultValue.has_value(); - } - ); + bool hasDefaultTypes = std::any_of( + alias->typeParams.begin(), + alias->typeParams.end(), + [](auto&& el) + { + return el.defaultValue.has_value(); + } + ); - bool hasDefaultPacks = std::any_of( - alias->typePackParams.begin(), - alias->typePackParams.end(), - [](auto&& el) - { - return el.defaultValue.has_value(); - } - ); + bool hasDefaultPacks = std::any_of( + alias->typePackParams.begin(), + alias->typePackParams.end(), + [](auto&& el) + { + return el.defaultValue.has_value(); + } + ); - if (!ty->hasParameterList) + if (!ty->hasParameterList) + { + if ((!alias->typeParams.empty() && !hasDefaultTypes) || (!alias->typePackParams.empty() && !hasDefaultPacks)) { - if ((!alias->typeParams.empty() && !hasDefaultTypes) || (!alias->typePackParams.empty() && !hasDefaultPacks)) - { - reportError(GenericError{"Type parameter list is required"}, ty->location); - } + reportError(GenericError{"Type parameter list is required"}, ty->location); } + } - size_t typesProvided = 0; - size_t extraTypes = 0; - size_t packsProvided = 0; + size_t typesProvided = 0; + size_t extraTypes = 0; + size_t packsProvided = 0; - for (const AstTypeOrPack& p : ty->parameters) + for (const AstTypeOrPack& p : ty->parameters) + { + if (p.type) { - if (p.type) + if (packsProvided != 0) { - if (packsProvided != 0) - { - reportError(GenericError{"Type parameters must come before type pack parameters"}, ty->location); - continue; - } + reportError(GenericError{"Type parameters must come before type pack parameters"}, ty->location); + continue; + } - if (typesProvided < typesRequired) - { - typesProvided += 1; - } - else - { - extraTypes += 1; - } + if (typesProvided < typesRequired) + { + typesProvided += 1; } - else if (p.typePack) + else { - std::optional tp = lookupPackAnnotation(p.typePack); - if (!tp.has_value()) - continue; - - if (typesProvided < typesRequired && size(*tp) == 1 && finite(*tp) && first(*tp)) - { - typesProvided += 1; - } - else - { - packsProvided += 1; - } + extraTypes += 1; } } - - if (extraTypes != 0 && packsProvided == 0) + else if (p.typePack) { - // Extra types are only collected into a pack if a pack is expected - if (packsRequired != 0) - packsProvided += 1; - else - typesProvided += extraTypes; - } + std::optional tp = lookupPackAnnotation(p.typePack); + if (!tp.has_value()) + continue; - for (size_t i = typesProvided; i < typesRequired; ++i) - { - if (alias->typeParams[i].defaultValue) + if (typesProvided < typesRequired && size(*tp) == 1 && finite(*tp) && first(*tp)) { typesProvided += 1; } - } - - for (size_t i = packsProvided; i < packsRequired; ++i) - { - if (alias->typePackParams[i].defaultValue) + else { packsProvided += 1; } } + } - if (extraTypes == 0 && packsProvided + 1 == packsRequired) - { + if (extraTypes != 0 && packsProvided == 0) + { + // Extra types are only collected into a pack if a pack is expected + if (packsRequired != 0) packsProvided += 1; - } + else + typesProvided += extraTypes; + } - if (typesProvided != typesRequired || packsProvided != packsRequired) + for (size_t i = typesProvided; i < typesRequired; ++i) + { + if (alias->typeParams[i].defaultValue) { - reportError( - IncorrectGenericParameterCount{ - /* name */ ty->name.value, - /* typeFun */ *alias, - /* actualParameters */ typesProvided, - /* actualPackParameters */ packsProvided, - }, - ty->location - ); + typesProvided += 1; } } - else + + for (size_t i = packsProvided; i < packsRequired; ++i) { - if (scope->lookupPack(ty->name.value)) + if (alias->typePackParams[i].defaultValue) { - reportError( - SwappedGenericTypeParameter{ - ty->name.value, - SwappedGenericTypeParameter::Kind::Type, - }, - ty->location - ); - } - else - { - std::string symbol = ""; - if (ty->prefix) - { - symbol += (*(ty->prefix)).value; - symbol += "."; - } - symbol += ty->name.value; - - reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location); + packsProvided += 1; } } - } - - void visit(AstTypeTable* table) - { - // TODO! - for (const AstTableProp& prop : table->props) - visit(prop.type); + if (extraTypes == 0 && packsProvided + 1 == packsRequired) + { + packsProvided += 1; + } - if (table->indexer) + if (typesProvided != typesRequired || packsProvided != packsRequired) { - visit(table->indexer->indexType); - visit(table->indexer->resultType); + reportError( + IncorrectGenericParameterCount{ + /* name */ ty->name.value, + /* typeFun */ *alias, + /* actualParameters */ typesProvided, + /* actualPackParameters */ packsProvided, + }, + ty->location + ); } } - - void visit(AstTypeFunction* ty) + else { - visitGenerics(ty->generics, ty->genericPacks); - visit(ty->argTypes); - visit(ty->returnTypes); - } + if (scope->lookupPack(ty->name.value)) + { + reportError( + SwappedGenericTypeParameter{ + ty->name.value, + SwappedGenericTypeParameter::Kind::Type, + }, + ty->location + ); + } + else + { + std::string symbol = ""; + if (ty->prefix) + { + symbol += (*(ty->prefix)).value; + symbol += "."; + } + symbol += ty->name.value; - void visit(AstTypeTypeof* ty) - { - visit(ty->expr, ValueContext::RValue); + reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location); + } } +} - void visit(AstTypeUnion* ty) - { - // TODO! - for (AstType* type : ty->types) - visit(type); - } +void TypeChecker2::visit(AstTypeTable* table) +{ + // TODO! - void visit(AstTypeIntersection* ty) - { - // TODO! - for (AstType* type : ty->types) - visit(type); - } + for (const AstTableProp& prop : table->props) + visit(prop.type); - void visit(AstTypePack* pack) + if (table->indexer) { - if (auto p = pack->as()) - return visit(p); - else if (auto p = pack->as()) - return visit(p); - else if (auto p = pack->as()) - return visit(p); + visit(table->indexer->indexType); + visit(table->indexer->resultType); } +} - void visit(AstTypePackExplicit* tp) - { - // TODO! - for (AstType* type : tp->typeList.types) - visit(type); - - if (tp->typeList.tailType) - visit(tp->typeList.tailType); - } +void TypeChecker2::visit(AstTypeFunction* ty) +{ + visitGenerics(ty->generics, ty->genericPacks); + visit(ty->argTypes); + visit(ty->returnTypes); +} - void visit(AstTypePackVariadic* tp) - { - // TODO! - visit(tp->variadicType); - } +void TypeChecker2::visit(AstTypeTypeof* ty) +{ + visit(ty->expr, ValueContext::RValue); +} - void visit(AstTypePackGeneric* tp) - { - Scope* scope = findInnermostScope(tp->location); - LUAU_ASSERT(scope); +void TypeChecker2::visit(AstTypeUnion* ty) +{ + // TODO! + for (AstType* type : ty->types) + visit(type); +} - 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); - } - } - } +void TypeChecker2::visit(AstTypeIntersection* ty) +{ + // TODO! + for (AstType* type : ty->types) + visit(type); +} - struct Reasonings - { - // the list of reasons - std::vector reasons; +void TypeChecker2::visit(AstTypePack* pack) +{ + if (auto p = pack->as()) + return visit(p); + else if (auto p = pack->as()) + return visit(p); + else if (auto p = pack->as()) + return visit(p); +} - // this should be true if _all_ of the reasons have an error suppressing type, and false otherwise. - bool suppressed; +void TypeChecker2::visit(AstTypePackExplicit* tp) +{ + // TODO! + for (AstType* type : tp->typeList.types) + visit(type); - std::string toString() - { - // DenseHashSet ordering is entirely undefined, so we want to - // sort the reasons here to achieve a stable error - // stringification. - std::sort(reasons.begin(), reasons.end()); - std::string allReasons; - bool first = true; - for (const std::string& reason : reasons) - { - if (first) - first = false; - else - allReasons += "\n\t"; + if (tp->typeList.tailType) + visit(tp->typeList.tailType); +} - allReasons += reason; - } +void TypeChecker2::visit(AstTypePackVariadic* tp) +{ + // TODO! + visit(tp->variadicType); +} - return allReasons; - } - }; +void TypeChecker2::visit(AstTypePackGeneric* tp) +{ + Scope* scope = findInnermostScope(tp->location); + LUAU_ASSERT(scope); - template - Reasonings explainReasonings(TID subTy, TID superTy, Location location, const SubtypingResult& r) + std::optional alias = scope->lookupPack(tp->genericName.value); + if (!alias.has_value()) { - if (r.reasoning.empty()) - return {}; - - std::vector reasons; - bool suppressed = true; - for (const SubtypingReasoning& reasoning : r.reasoning) + if (scope->lookupType(tp->genericName.value)) { - if (reasoning.subPath.empty() && reasoning.superPath.empty()) - continue; - - std::optional optSubLeaf = traverse(subTy, reasoning.subPath, builtinTypes); - std::optional optSuperLeaf = traverse(superTy, reasoning.superPath, builtinTypes); + reportError( + SwappedGenericTypeParameter{ + tp->genericName.value, + SwappedGenericTypeParameter::Kind::Pack, + }, + tp->location + ); + } + else + { + reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location); + } + } +} - if (!optSubLeaf || !optSuperLeaf) - ice->ice("Subtyping test returned a reasoning with an invalid path", location); +template +Reasonings TypeChecker2::explainReasonings_(TID subTy, TID superTy, Location location, const SubtypingResult& r) +{ + if (r.reasoning.empty()) + return {}; - const TypeOrPack& subLeaf = *optSubLeaf; - const TypeOrPack& superLeaf = *optSuperLeaf; + std::vector reasons; + bool suppressed = true; + for (const SubtypingReasoning& reasoning : r.reasoning) + { + if (reasoning.subPath.empty() && reasoning.superPath.empty()) + continue; - auto subLeafTy = get(subLeaf); - auto superLeafTy = get(superLeaf); + std::optional optSubLeaf = traverse(subTy, reasoning.subPath, builtinTypes); + std::optional optSuperLeaf = traverse(superTy, reasoning.superPath, builtinTypes); - auto subLeafTp = get(subLeaf); - auto superLeafTp = get(superLeaf); + if (!optSubLeaf || !optSuperLeaf) + ice->ice("Subtyping test returned a reasoning with an invalid path", location); - if (!subLeafTy && !superLeafTy && !subLeafTp && !superLeafTp) - ice->ice("Subtyping test returned a reasoning where one path ends at a type and the other ends at a pack.", location); + const TypeOrPack& subLeaf = *optSubLeaf; + const TypeOrPack& superLeaf = *optSuperLeaf; - std::string relation = "a subtype of"; - if (reasoning.variance == SubtypingVariance::Invariant) - relation = "exactly"; - else if (reasoning.variance == SubtypingVariance::Contravariant) - relation = "a supertype of"; + auto subLeafTy = get(subLeaf); + auto superLeafTy = get(superLeaf); - std::string reason; - if (reasoning.subPath == reasoning.superPath) - reason = "at " + toString(reasoning.subPath) + ", " + toString(subLeaf) + " is not " + relation + " " + toString(superLeaf); - else - reason = "type " + toString(subTy) + toString(reasoning.subPath, /* prefixDot */ true) + " (" + toString(subLeaf) + ") is not " + - relation + " " + toString(superTy) + toString(reasoning.superPath, /* prefixDot */ true) + " (" + toString(superLeaf) + ")"; + auto subLeafTp = get(subLeaf); + auto superLeafTp = get(superLeaf); - reasons.push_back(reason); + if (!subLeafTy && !superLeafTy && !subLeafTp && !superLeafTp) + ice->ice("Subtyping test returned a reasoning where one path ends at a type and the other ends at a pack.", location); - // if we haven't already proved this isn't suppressing, we have to keep checking. - if (suppressed) - { - if (subLeafTy && superLeafTy) - suppressed &= isErrorSuppressing(location, *subLeafTy) || isErrorSuppressing(location, *superLeafTy); - else - suppressed &= isErrorSuppressing(location, *subLeafTp) || isErrorSuppressing(location, *superLeafTp); - } - } + std::string relation = "a subtype of"; + if (reasoning.variance == SubtypingVariance::Invariant) + relation = "exactly"; + else if (reasoning.variance == SubtypingVariance::Contravariant) + relation = "a supertype of"; - return {std::move(reasons), suppressed}; - } + std::string reason; + if (reasoning.subPath == reasoning.superPath) + reason = "at " + toString(reasoning.subPath) + ", " + toString(subLeaf) + " is not " + relation + " " + toString(superLeaf); + else + reason = "type " + toString(subTy) + toString(reasoning.subPath, /* prefixDot */ true) + " (" + toString(subLeaf) + ") is not " + + relation + " " + toString(superTy) + toString(reasoning.superPath, /* prefixDot */ true) + " (" + toString(superLeaf) + ")"; + reasons.push_back(reason); - void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result) - { - switch (shouldSuppressErrors(NotNull{&normalizer}, subTy).orElse(shouldSuppressErrors(NotNull{&normalizer}, superTy))) + // if we haven't already proved this isn't suppressing, we have to keep checking. + if (suppressed) { - case ErrorSuppression::Suppress: - return; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, location); - case ErrorSuppression::DoNotSuppress: - break; + if (subLeafTy && superLeafTy) + suppressed &= isErrorSuppressing(location, *subLeafTy) || isErrorSuppressing(location, *superLeafTy); + else + suppressed &= isErrorSuppressing(location, *subLeafTp) || isErrorSuppressing(location, *superLeafTp); } + } - Reasonings reasonings = explainReasonings(subTy, superTy, location, result); + return {std::move(reasons), suppressed}; +} - if (!reasonings.suppressed) - reportError(TypeMismatch{superTy, subTy, reasonings.toString()}, location); - } +Reasonings TypeChecker2::explainReasonings(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& r) +{ + return explainReasonings_(subTy, superTy, location, r); +} + +Reasonings TypeChecker2::explainReasonings(TypePackId subTp, TypePackId superTp, Location location, const SubtypingResult& r) +{ + return explainReasonings_(subTp, superTp, location, r); +} - void explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result) +void TypeChecker2::explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result) +{ + switch (shouldSuppressErrors(NotNull{&normalizer}, subTy).orElse(shouldSuppressErrors(NotNull{&normalizer}, superTy))) { - switch (shouldSuppressErrors(NotNull{&normalizer}, subTy).orElse(shouldSuppressErrors(NotNull{&normalizer}, superTy))) - { - case ErrorSuppression::Suppress: - return; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, location); - case ErrorSuppression::DoNotSuppress: - break; - } + case ErrorSuppression::Suppress: + return; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, location); + break; + case ErrorSuppression::DoNotSuppress: + break; + } - Reasonings reasonings = explainReasonings(subTy, superTy, location, result); + Reasonings reasonings = explainReasonings(subTy, superTy, location, result); - if (!reasonings.suppressed) - reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location); - } + if (!reasonings.suppressed) + reportError(TypeMismatch{superTy, subTy, reasonings.toString()}, location); +} - bool testIsSubtype(TypeId subTy, TypeId superTy, Location location) +void TypeChecker2::explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result) +{ + switch (shouldSuppressErrors(NotNull{&normalizer}, subTy).orElse(shouldSuppressErrors(NotNull{&normalizer}, superTy))) { - SubtypingResult r = subtyping->isSubtype(subTy, superTy); + case ErrorSuppression::Suppress: + return; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, location); + break; + case ErrorSuppression::DoNotSuppress: + break; + } - if (r.normalizationTooComplex) - reportError(NormalizationTooComplex{}, location); + Reasonings reasonings = explainReasonings(subTy, superTy, location, result); - if (!r.isSubtype) - explainError(subTy, superTy, location, r); + if (!reasonings.suppressed) + reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location); +} - return r.isSubtype; - } +bool TypeChecker2::testIsSubtype(TypeId subTy, TypeId superTy, Location location) +{ + NotNull scope{findInnermostScope(location)}; + SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope); - bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location) - { - SubtypingResult r = subtyping->isSubtype(subTy, superTy); + if (r.normalizationTooComplex) + reportError(NormalizationTooComplex{}, location); - if (r.normalizationTooComplex) - reportError(NormalizationTooComplex{}, location); + if (!r.isSubtype) + explainError(subTy, superTy, location, r); - if (!r.isSubtype) - explainError(subTy, superTy, location, r); + return r.isSubtype; +} - return r.isSubtype; - } +bool TypeChecker2::testIsSubtype(TypePackId subTy, TypePackId superTy, Location location) +{ + NotNull scope{findInnermostScope(location)}; + SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope); - void reportError(TypeErrorData data, const Location& location) - { - if (auto utk = get_if(&data)) - diagnoseMissingTableKey(utk, data); + if (r.normalizationTooComplex) + reportError(NormalizationTooComplex{}, location); - module->errors.emplace_back(location, module->name, std::move(data)); + if (!r.isSubtype) + explainError(subTy, superTy, location, r); - if (logger) - logger->captureTypeCheckError(module->errors.back()); - } + return r.isSubtype; +} - void reportError(TypeError e) - { - reportError(std::move(e.data), e.location); - } +void TypeChecker2::reportError(TypeErrorData data, const Location& location) +{ + if (auto utk = get_if(&data)) + diagnoseMissingTableKey(utk, data); - void reportErrors(ErrorVec errors) - { - for (TypeError e : errors) - reportError(std::move(e)); - } + module->errors.emplace_back(location, module->name, std::move(data)); - struct PropertyTypes - { - // a vector of all the types assigned to the given property. - std::vector typesOfProp; + if (logger) + logger->captureTypeCheckError(module->errors.back()); +} - // a vector of all the types that are missing the given property. - std::vector missingProp; +void TypeChecker2::reportError(TypeError e) +{ + reportError(std::move(e.data), e.location); +} - bool foundOneProp() const - { - return !typesOfProp.empty(); - } +void TypeChecker2::reportErrors(ErrorVec errors) +{ + for (TypeError e : errors) + reportError(std::move(e)); +} - bool noneMissingProp() const - { - return missingProp.empty(); - } +/* A helper for checkIndexTypeFromType. + * + * Returns a pair: + * * A boolean indicating that at least one of the constituent types + * contains the prop, and + * * A vector of types that do not contain the prop. + */ +PropertyTypes TypeChecker2::lookupProp( + const NormalizedType* norm, + const std::string& prop, + ValueContext context, + const Location& location, + TypeId astIndexExprType, + std::vector& errors +) +{ + std::vector typesOfProp; + std::vector typesMissingTheProp; - bool foundMissingProp() const - { - return !missingProp.empty(); - } - }; + // this is `false` if we ever hit the resource limits during any of our uses of `fetch`. + bool normValid = true; - /* A helper for checkIndexTypeFromType. - * - * Returns a pair: - * * A boolean indicating that at least one of the constituent types - * contains the prop, and - * * A vector of types that do not contain the prop. - */ - PropertyTypes lookupProp( - const NormalizedType* norm, - const std::string& prop, - ValueContext context, - const Location& location, - TypeId astIndexExprType, - std::vector& errors - ) + auto fetch = [&](TypeId ty) { - std::vector typesOfProp; - std::vector typesMissingTheProp; + NormalizationResult result = normalizer.isInhabited(ty); + if (result == NormalizationResult::HitLimits) + normValid = false; + if (result != NormalizationResult::True) + return; - // this is `false` if we ever hit the resource limits during any of our uses of `fetch`. - bool normValid = true; + DenseHashSet seen{nullptr}; + PropertyType res = hasIndexTypeFromType(ty, prop, context, location, seen, astIndexExprType, errors); - auto fetch = [&](TypeId ty) + if (res.present == NormalizationResult::HitLimits) { - NormalizationResult result = normalizer.isInhabited(ty); - if (result == NormalizationResult::HitLimits) - normValid = false; - if (result != NormalizationResult::True) - return; - - DenseHashSet seen{nullptr}; - PropertyType res = hasIndexTypeFromType(ty, prop, context, location, seen, astIndexExprType, errors); - - if (res.present == NormalizationResult::HitLimits) - { - normValid = false; - return; - } + normValid = false; + return; + } - if (res.present == NormalizationResult::True && res.result) - typesOfProp.emplace_back(*res.result); + if (res.present == NormalizationResult::True && res.result) + typesOfProp.emplace_back(*res.result); - if (res.present == NormalizationResult::False) - typesMissingTheProp.push_back(ty); - }; + if (res.present == NormalizationResult::False) + typesMissingTheProp.push_back(ty); + }; - if (normValid) - fetch(norm->tops); - if (normValid) - fetch(norm->booleans); + if (normValid) + fetch(norm->tops); + if (normValid) + fetch(norm->booleans); - if (normValid) + if (normValid) + { + for (const auto& [ty, _negations] : norm->classes.classes) { - for (const auto& [ty, _negations] : norm->classes.classes) - { - fetch(ty); + fetch(ty); - if (!normValid) - break; - } + if (!normValid) + break; } + } - if (normValid) - fetch(norm->errors); - if (normValid) - fetch(norm->nils); - if (normValid) - fetch(norm->numbers); - if (normValid && !norm->strings.isNever()) - fetch(builtinTypes->stringType); - if (normValid) - fetch(norm->threads); - if (normValid) - fetch(norm->buffers); + if (normValid) + fetch(norm->errors); + if (normValid) + fetch(norm->nils); + if (normValid) + fetch(norm->numbers); + if (normValid && !norm->strings.isNever()) + fetch(builtinTypes->stringType); + if (normValid) + fetch(norm->threads); + if (normValid) + fetch(norm->buffers); - if (normValid) + if (normValid) + { + for (TypeId ty : norm->tables) { - for (TypeId ty : norm->tables) - { - fetch(ty); + fetch(ty); - if (!normValid) - break; - } + if (!normValid) + break; } + } - if (normValid && norm->functions.isTop) - fetch(builtinTypes->functionType); - else if (normValid && !norm->functions.isNever()) + if (normValid && norm->functions.isTop) + fetch(builtinTypes->functionType); + else if (normValid && !norm->functions.isNever()) + { + if (norm->functions.parts.size() == 1) + fetch(norm->functions.parts.front()); + else { - if (norm->functions.parts.size() == 1) - fetch(norm->functions.parts.front()); - else - { - std::vector parts; - parts.insert(parts.end(), norm->functions.parts.begin(), norm->functions.parts.end()); - fetch(module->internalTypes.addType(IntersectionType{std::move(parts)})); - } + std::vector parts; + parts.insert(parts.end(), norm->functions.parts.begin(), norm->functions.parts.end()); + fetch(module->internalTypes.addType(IntersectionType{std::move(parts)})); } + } - if (normValid) + if (normValid) + { + for (const auto& [tyvar, intersect] : norm->tyvars) { - for (const auto& [tyvar, intersect] : norm->tyvars) + if (get(intersect->tops)) { - if (get(intersect->tops)) - { - TypeId ty = normalizer.typeFromNormal(*intersect); - fetch(module->internalTypes.addType(IntersectionType{{tyvar, ty}})); - } - else - fetch(follow(tyvar)); - - if (!normValid) - break; + TypeId ty = normalizer.typeFromNormal(*intersect); + fetch(module->internalTypes.addType(IntersectionType{{tyvar, ty}})); } - } + else + fetch(follow(tyvar)); - return {typesOfProp, typesMissingTheProp}; + if (!normValid) + break; + } } - // If the provided type does not have the named property, report an error. - void checkIndexTypeFromType(TypeId tableTy, const std::string& prop, ValueContext context, const Location& location, TypeId astIndexExprType) + return {typesOfProp, typesMissingTheProp}; +} + + +void TypeChecker2::checkIndexTypeFromType( + TypeId tableTy, + const std::string& prop, + ValueContext context, + const Location& location, + TypeId astIndexExprType +) +{ + std::shared_ptr norm = normalizer.normalize(tableTy); + if (!norm) { - std::shared_ptr norm = normalizer.normalize(tableTy); - if (!norm) - { - reportError(NormalizationTooComplex{}, location); - return; - } + reportError(NormalizationTooComplex{}, location); + return; + } - // if the type is error suppressing, we don't actually have any work left to do. - if (norm->shouldSuppressErrors()) - return; + // if the type is error suppressing, we don't actually have any work left to do. + if (norm->shouldSuppressErrors()) + return; - std::vector dummy; - const auto propTypes = lookupProp(norm.get(), prop, context, location, astIndexExprType, module->errors); + std::vector dummy; + const auto propTypes = lookupProp(norm.get(), prop, context, location, astIndexExprType, module->errors); - if (propTypes.foundMissingProp()) + if (propTypes.foundMissingProp()) + { + if (propTypes.foundOneProp()) + reportError(MissingUnionProperty{tableTy, propTypes.missingProp, prop}, location); + // For class LValues, we don't want to report an extension error, + // because classes come into being with full knowledge of their + // shape. We instead want to report the unknown property error of + // the `else` branch. + else if (context == ValueContext::LValue && !get(tableTy)) { - if (propTypes.foundOneProp()) - reportError(MissingUnionProperty{tableTy, propTypes.missingProp, prop}, location); - // For class LValues, we don't want to report an extension error, - // because classes come into being with full knowledge of their - // shape. We instead want to report the unknown property error of - // the `else` branch. - else if (context == ValueContext::LValue && !get(tableTy)) - { - const auto lvPropTypes = lookupProp(norm.get(), prop, ValueContext::RValue, location, astIndexExprType, dummy); - if (lvPropTypes.foundOneProp() && lvPropTypes.noneMissingProp()) - reportError(PropertyAccessViolation{tableTy, prop, PropertyAccessViolation::CannotWrite}, location); - else if (get(tableTy) || get(tableTy)) - reportError(NotATable{tableTy}, location); - else - reportError(CannotExtendTable{tableTy, CannotExtendTable::Property, prop}, location); - } - else if (context == ValueContext::RValue && !get(tableTy)) - { - const auto rvPropTypes = lookupProp(norm.get(), prop, ValueContext::LValue, location, astIndexExprType, dummy); - if (rvPropTypes.foundOneProp() && rvPropTypes.noneMissingProp()) - reportError(PropertyAccessViolation{tableTy, prop, PropertyAccessViolation::CannotRead}, location); - else - reportError(UnknownProperty{tableTy, prop}, location); - } + const auto lvPropTypes = lookupProp(norm.get(), prop, ValueContext::RValue, location, astIndexExprType, dummy); + if (lvPropTypes.foundOneProp() && lvPropTypes.noneMissingProp()) + reportError(PropertyAccessViolation{tableTy, prop, PropertyAccessViolation::CannotWrite}, location); + else if (get(tableTy) || get(tableTy)) + reportError(NotATable{tableTy}, location); + else + reportError(CannotExtendTable{tableTy, CannotExtendTable::Property, prop}, location); + } + else if (context == ValueContext::RValue && !get(tableTy)) + { + const auto rvPropTypes = lookupProp(norm.get(), prop, ValueContext::LValue, location, astIndexExprType, dummy); + if (rvPropTypes.foundOneProp() && rvPropTypes.noneMissingProp()) + reportError(PropertyAccessViolation{tableTy, prop, PropertyAccessViolation::CannotRead}, location); else reportError(UnknownProperty{tableTy, prop}, location); } + else + reportError(UnknownProperty{tableTy, prop}, location); } +} - struct PropertyType - { - NormalizationResult present; - std::optional result; - }; +PropertyType TypeChecker2::hasIndexTypeFromType( + TypeId ty, + const std::string& prop, + ValueContext context, + const Location& location, + DenseHashSet& seen, + TypeId astIndexExprType, + std::vector& errors +) +{ + // If we have already encountered this type, we must assume that some + // other codepath will do the right thing and signal false if the + // property is not present. + if (seen.contains(ty)) + return {NormalizationResult::True, {}}; + seen.insert(ty); - PropertyType hasIndexTypeFromType( - TypeId ty, - const std::string& prop, - ValueContext context, - const Location& location, - DenseHashSet& seen, - TypeId astIndexExprType, - std::vector& errors - ) + if (get(ty) || get(ty) || get(ty)) + return {NormalizationResult::True, {ty}}; + + if (isString(ty)) { - // If we have already encountered this type, we must assume that some - // other codepath will do the right thing and signal false if the - // property is not present. - if (seen.contains(ty)) - return {NormalizationResult::True, {}}; - seen.insert(ty); + std::optional mtIndex = Luau::findMetatableEntry(builtinTypes, errors, builtinTypes->stringType, "__index", location); + LUAU_ASSERT(mtIndex); + ty = *mtIndex; + } - if (get(ty) || get(ty) || get(ty)) - return {NormalizationResult::True, {ty}}; + if (auto tt = getTableType(ty)) + { + if (auto resTy = findTablePropertyRespectingMeta(builtinTypes, errors, ty, prop, context, location)) + return {NormalizationResult::True, resTy}; - if (isString(ty)) + if (tt->indexer) { - std::optional mtIndex = Luau::findMetatableEntry(builtinTypes, errors, builtinTypes->stringType, "__index", location); - LUAU_ASSERT(mtIndex); - ty = *mtIndex; + TypeId indexType = follow(tt->indexer->indexType); + if (isPrim(indexType, PrimitiveType::String)) + return {NormalizationResult::True, {tt->indexer->indexResultType}}; + // If the indexer looks like { [any] : _} - the prop lookup should be allowed! + else if (get(indexType) || get(indexType)) + return {NormalizationResult::True, {tt->indexer->indexResultType}}; } - if (auto tt = getTableType(ty)) - { - if (auto resTy = findTablePropertyRespectingMeta(builtinTypes, errors, ty, prop, context, location)) - return {NormalizationResult::True, resTy}; - - if (tt->indexer) - { - TypeId indexType = follow(tt->indexer->indexType); - if (isPrim(indexType, PrimitiveType::String)) - return {NormalizationResult::True, {tt->indexer->indexResultType}}; - // If the indexer looks like { [any] : _} - the prop lookup should be allowed! - else if (get(indexType) || get(indexType)) - return {NormalizationResult::True, {tt->indexer->indexResultType}}; - } - - // if we are in a conditional context, we treat the property as present and `unknown` because - // we may be _refining_ `tableTy` to include that property. we will want to revisit this a bit - // in the future once luau has support for exact tables since this only applies when inexact. - return {inConditional(typeContext) ? NormalizationResult::True : NormalizationResult::False, {builtinTypes->unknownType}}; - } - else if (const ClassType* cls = get(ty)) + // if we are in a conditional context, we treat the property as present and `unknown` because + // we may be _refining_ `tableTy` to include that property. we will want to revisit this a bit + // in the future once luau has support for exact tables since this only applies when inexact. + return {inConditional(typeContext) ? NormalizationResult::True : NormalizationResult::False, {builtinTypes->unknownType}}; + } + else if (const ClassType* cls = get(ty)) + { + // If the property doesn't exist on the class, we consult the indexer + // We need to check if the type of the index expression foo (x[foo]) + // is compatible with the indexer's indexType + // Construct the intersection and test inhabitedness! + if (auto property = lookupClassProp(cls, prop)) + return {NormalizationResult::True, context == ValueContext::LValue ? property->writeTy : property->readTy}; + if (cls->indexer) { - // If the property doesn't exist on the class, we consult the indexer - // We need to check if the type of the index expression foo (x[foo]) - // is compatible with the indexer's indexType - // Construct the intersection and test inhabitedness! - if (auto property = lookupClassProp(cls, prop)) - return {NormalizationResult::True, context == ValueContext::LValue ? property->writeTy : property->readTy}; - if (cls->indexer) - { - TypeId inhabitatedTestType = module->internalTypes.addType(IntersectionType{{cls->indexer->indexType, astIndexExprType}}); - return {normalizer.isInhabited(inhabitatedTestType), {cls->indexer->indexResultType}}; - } - return {NormalizationResult::False, {}}; + TypeId inhabitatedTestType = module->internalTypes.addType(IntersectionType{{cls->indexer->indexType, astIndexExprType}}); + return {normalizer.isInhabited(inhabitatedTestType), {cls->indexer->indexResultType}}; } - else if (const UnionType* utv = get(ty)) - { - std::vector parts; - parts.reserve(utv->options.size()); - - for (TypeId part : utv) - { - PropertyType result = hasIndexTypeFromType(part, prop, context, location, seen, astIndexExprType, errors); - - if (result.present != NormalizationResult::True) - return {result.present, {}}; - if (result.result) - parts.emplace_back(*result.result); - } - - if (parts.size() == 0) - return {NormalizationResult::False, {}}; - - if (parts.size() == 1) - return {NormalizationResult::True, {parts[0]}}; - - TypeId propTy; - if (context == ValueContext::LValue) - propTy = module->internalTypes.addType(IntersectionType{parts}); - else - propTy = module->internalTypes.addType(UnionType{parts}); + return {NormalizationResult::False, {}}; + } + else if (const UnionType* utv = get(ty)) + { + std::vector parts; + parts.reserve(utv->options.size()); - return {NormalizationResult::True, propTy}; - } - else if (const IntersectionType* itv = get(ty)) + for (TypeId part : utv) { - for (TypeId part : itv) - { - PropertyType result = hasIndexTypeFromType(part, prop, context, location, seen, astIndexExprType, errors); - if (result.present != NormalizationResult::False) - return result; - } + PropertyType result = hasIndexTypeFromType(part, prop, context, location, seen, astIndexExprType, errors); - return {NormalizationResult::False, {}}; + if (result.present != NormalizationResult::True) + return {result.present, {}}; + if (result.result) + parts.emplace_back(*result.result); } - else if (const PrimitiveType* pt = get(ty)) - return {(inConditional(typeContext) && pt->type == PrimitiveType::Table) ? NormalizationResult::True : NormalizationResult::False, {ty}}; - else + + if (parts.size() == 0) return {NormalizationResult::False, {}}; - } - void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const - { - std::string_view sv(utk->key); - std::set candidates; + if (parts.size() == 1) + return {NormalizationResult::True, {parts[0]}}; - auto accumulate = [&](const TableType::Props& props) - { - for (const auto& [name, ty] : props) - { - if (sv != name && equalsLower(sv, name)) - candidates.insert(name); - } - }; + TypeId propTy; + if (context == ValueContext::LValue) + propTy = module->internalTypes.addType(IntersectionType{parts}); + else + propTy = module->internalTypes.addType(UnionType{parts}); - if (auto ttv = getTableType(utk->table)) - accumulate(ttv->props); - else if (auto ctv = get(follow(utk->table))) + return {NormalizationResult::True, propTy}; + } + else if (const IntersectionType* itv = get(ty)) + { + for (TypeId part : itv) { - while (ctv) - { - accumulate(ctv->props); - - if (!ctv->parent) - break; - - ctv = get(*ctv->parent); - LUAU_ASSERT(ctv); - } + PropertyType result = hasIndexTypeFromType(part, prop, context, location, seen, astIndexExprType, errors); + if (result.present != NormalizationResult::False) + return result; } - if (!candidates.empty()) - data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, candidates}); + return {NormalizationResult::False, {}}; } + else if (const PrimitiveType* pt = get(ty)) + return {(inConditional(typeContext) && pt->type == PrimitiveType::Table) ? NormalizationResult::True : NormalizationResult::False, {ty}}; + else + return {NormalizationResult::False, {}}; +} - bool isErrorSuppressing(Location loc, TypeId ty) - { - switch (shouldSuppressErrors(NotNull{&normalizer}, ty)) - { - case ErrorSuppression::DoNotSuppress: - return false; - case ErrorSuppression::Suppress: - return true; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, loc); - return false; - }; - LUAU_ASSERT(false); - return false; // UNREACHABLE - } - bool isErrorSuppressing(Location loc1, TypeId ty1, Location loc2, TypeId ty2) +void TypeChecker2::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const +{ + std::string_view sv(utk->key); + std::set candidates; + + auto accumulate = [&](const TableType::Props& props) { - return isErrorSuppressing(loc1, ty1) || isErrorSuppressing(loc2, ty2); - } + for (const auto& [name, ty] : props) + { + if (sv != name && equalsLower(sv, name)) + candidates.insert(name); + } + }; - bool isErrorSuppressing(Location loc, TypePackId tp) + if (auto ttv = getTableType(utk->table)) + accumulate(ttv->props); + else if (auto ctv = get(follow(utk->table))) { - switch (shouldSuppressErrors(NotNull{&normalizer}, tp)) + while (ctv) { - case ErrorSuppression::DoNotSuppress: - return false; - case ErrorSuppression::Suppress: - return true; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, loc); - return false; - }; + accumulate(ctv->props); - LUAU_ASSERT(false); - return false; // UNREACHABLE - } + if (!ctv->parent) + break; - bool isErrorSuppressing(Location loc1, TypePackId tp1, Location loc2, TypePackId tp2) - { - return isErrorSuppressing(loc1, tp1) || isErrorSuppressing(loc2, tp2); + ctv = get(*ctv->parent); + LUAU_ASSERT(ctv); + } } -}; -void check( - NotNull builtinTypes, - NotNull unifierState, - NotNull limits, - DcrLogger* logger, - const SourceModule& sourceModule, - Module* module -) + if (!candidates.empty()) + data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, candidates}); +} + +bool TypeChecker2::isErrorSuppressing(Location loc, TypeId ty) { - LUAU_TIMETRACE_SCOPE("check", "Typechecking"); + switch (shouldSuppressErrors(NotNull{&normalizer}, ty)) + { + case ErrorSuppression::DoNotSuppress: + return false; + case ErrorSuppression::Suppress: + return true; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, loc); + return false; + }; - TypeChecker2 typeChecker{builtinTypes, unifierState, limits, logger, &sourceModule, module}; + LUAU_ASSERT(false); + return false; // UNREACHABLE +} - typeChecker.visit(sourceModule.root); +bool TypeChecker2::isErrorSuppressing(Location loc1, TypeId ty1, Location loc2, TypeId ty2) +{ + return isErrorSuppressing(loc1, ty1) || isErrorSuppressing(loc2, ty2); +} - // if the only error we're producing is one about constraint solving being incomplete, we can silence it. - // this means we won't give this warning if types seem totally nonsensical, but there are no other errors. - // this is probably, on the whole, a good decision to not annoy users though. - if (module->errors.size() == 1 && get(module->errors[0])) - module->errors.clear(); +bool TypeChecker2::isErrorSuppressing(Location loc, TypePackId tp) +{ + switch (shouldSuppressErrors(NotNull{&normalizer}, tp)) + { + case ErrorSuppression::DoNotSuppress: + return false; + case ErrorSuppression::Suppress: + return true; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, loc); + return false; + }; - unfreeze(module->interfaceTypes); - copyErrors(module->errors, module->interfaceTypes, builtinTypes); - freeze(module->interfaceTypes); + LUAU_ASSERT(false); + return false; // UNREACHABLE } +bool TypeChecker2::isErrorSuppressing(Location loc1, TypePackId tp1, Location loc2, TypePackId tp2) +{ + return isErrorSuppressing(loc1, tp1) || isErrorSuppressing(loc2, tp2); +} + + } // namespace Luau diff --git a/Dependencies/luau/Luau/Analysis/src/TypeFunction.cpp b/Dependencies/luau/Luau/Analysis/src/TypeFunction.cpp index e8d3764e..9ae57fd1 100644 --- a/Dependencies/luau/Luau/Analysis/src/TypeFunction.cpp +++ b/Dependencies/luau/Luau/Analysis/src/TypeFunction.cpp @@ -701,8 +701,8 @@ TypeFunctionReductionResult lenTypeFunction( if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes)) return {std::nullopt, true, {}, {}}; // occurs check failed - Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope}; - if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance? + Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice}; + if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance? return {std::nullopt, true, {}, {}}; // `len` must return a `number`. @@ -790,8 +790,8 @@ TypeFunctionReductionResult unmTypeFunction( if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes)) return {std::nullopt, true, {}, {}}; // occurs check failed - Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope}; - if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance? + Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice}; + if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance? return {std::nullopt, true, {}, {}}; if (std::optional ret = first(instantiatedMmFtv->retTypes)) @@ -1138,8 +1138,8 @@ TypeFunctionReductionResult concatTypeFunction( if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes)) return {std::nullopt, true, {}, {}}; // occurs check failed - Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope}; - if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance? + Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice}; + if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance? return {std::nullopt, true, {}, {}}; return {ctx->builtins->stringType, false, {}, {}}; @@ -1392,8 +1392,8 @@ static TypeFunctionReductionResult comparisonTypeFunction( if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes)) return {std::nullopt, true, {}, {}}; // occurs check failed - Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope}; - if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance? + Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice}; + if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance? return {std::nullopt, true, {}, {}}; return {ctx->builtins->booleanType, false, {}, {}}; @@ -1536,8 +1536,8 @@ TypeFunctionReductionResult eqTypeFunction( if (!u2.unify(inferredArgPack, instantiatedMmFtv->argTypes)) return {std::nullopt, true, {}, {}}; // occurs check failed - Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice, ctx->scope}; - if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes).isSubtype) // TODO: is this the right variance? + Subtyping subtyping{ctx->builtins, ctx->arena, ctx->normalizer, ctx->ice}; + if (!subtyping.isSubtype(inferredArgPack, instantiatedMmFtv->argTypes, ctx->scope).isSubtype) // TODO: is this the right variance? return {std::nullopt, true, {}, {}}; return {ctx->builtins->booleanType, false, {}, {}}; @@ -1897,7 +1897,30 @@ bool computeKeysOf(TypeId ty, Set& result, DenseHashSet& se return res; } - // this should not be reachable since the type should be a valid tables part from normalization. + if (auto classTy = get(ty)) + { + for (auto [key, _] : classTy->props) + result.insert(key); + + bool res = true; + if (classTy->metatable && !isRaw) + { + // findMetatableEntry demands the ability to emit errors, so we must give it + // the necessary state to do that, even if we intend to just eat the errors. + ErrorVec dummy; + + std::optional mmType = findMetatableEntry(ctx->builtins, dummy, ty, "__index", Location{}); + if (mmType) + res = res && computeKeysOf(*mmType, result, seen, isRaw, ctx); + } + + if (classTy->parent) + res = res && computeKeysOf(follow(*classTy->parent), result, seen, isRaw, ctx); + + return res; + } + + // this should not be reachable since the type should be a valid tables or classes part from normalization. LUAU_ASSERT(false); return false; } @@ -1941,34 +1964,32 @@ TypeFunctionReductionResult keyofFunctionImpl( { LUAU_ASSERT(!normTy->hasTables()); + // seen set for key computation for classes + DenseHashSet seen{{}}; + auto classesIter = normTy->classes.ordering.begin(); auto classesIterEnd = normTy->classes.ordering.end(); - LUAU_ASSERT(classesIter != classesIterEnd); // should be guaranteed by the `hasClasses` check - - auto classTy = get(*classesIter); - if (!classTy) - { - LUAU_ASSERT(false); // this should not be possible according to normalization's spec - return {std::nullopt, true, {}, {}}; - } + LUAU_ASSERT(classesIter != classesIterEnd); // should be guaranteed by the `hasClasses` check earlier - for (auto [key, _] : classTy->props) - keys.insert(key); + // collect all the properties from the first class type + if (!computeKeysOf(*classesIter, keys, seen, isRaw, ctx)) + return {ctx->builtins->stringType, false, {}, {}}; // if it failed, we have a top type! // we need to look at each class to remove any keys that are not common amongst them all while (++classesIter != classesIterEnd) { - auto classTy = get(*classesIter); - if (!classTy) - { - LUAU_ASSERT(false); // this should not be possible according to normalization's spec - return {std::nullopt, true, {}, {}}; - } + seen.clear(); // we'll reuse the same seen set + + Set localKeys{{}}; + + // we can skip to the next class if this one is a top type + if (!computeKeysOf(*classesIter, localKeys, seen, isRaw, ctx)) + continue; for (auto key : keys) { // remove any keys that are not present in each class - if (classTy->props.find(key) == classTy->props.end()) + if (!localKeys.contains(key)) keys.erase(key); } } @@ -2020,6 +2041,12 @@ TypeFunctionReductionResult keyofFunctionImpl( for (std::string key : keys) singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{key}})); + // If there's only one entry, we don't need a UnionType. + // We can take straight take it from the first entry + // because it was added into the type arena already. + if (singletons.size() == 1) + return {singletons.front(), false, {}, {}}; + return {ctx->arena->addType(UnionType{singletons}), false, {}, {}}; } @@ -2179,6 +2206,12 @@ TypeFunctionReductionResult indexFunctionImpl( return {std::nullopt, true, {}, {}}; TypeId indexerTy = follow(typeParams.at(1)); + + if (isPending(indexerTy, ctx->solver)) + { + return {std::nullopt, false, {indexerTy}, {}}; + } + std::shared_ptr indexerNormTy = ctx->normalizer->normalize(indexerTy); // if the indexer failed to normalize, we can't reduce, but know nothing about inhabitance. @@ -2222,6 +2255,19 @@ TypeFunctionReductionResult indexFunctionImpl( if (searchPropsAndIndexer(ty, classTy->props, classTy->indexer, properties, ctx)) continue; // Indexer was found in this class, so we can move on to the next + auto parent = classTy->parent; + bool foundInParent = false; + while (parent && !foundInParent) + { + auto parentClass = get(follow(*parent)); + foundInParent = searchPropsAndIndexer(ty, parentClass->props, parentClass->indexer, properties, ctx); + parent = parentClass->parent; + } + + // we move on to the next type if any of the parents we went through had the property. + if (foundInParent) + continue; + // If code reaches here,that means the property not found -> check in the metatable's __index // findMetatableEntry demands the ability to emit errors, so we must give it diff --git a/Dependencies/luau/Luau/Analysis/src/TypeInfer.cpp b/Dependencies/luau/Luau/Analysis/src/TypeInfer.cpp index c0a43df8..6b2e861d 100644 --- a/Dependencies/luau/Luau/Analysis/src/TypeInfer.cpp +++ b/Dependencies/luau/Luau/Analysis/src/TypeInfer.cpp @@ -34,6 +34,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAGVARIABLE(LuauRemoveBadRelationalOperatorWarning, false) LUAU_FASTFLAGVARIABLE(LuauOkWithIteratingOverTableProperties, false) +LUAU_FASTFLAGVARIABLE(LuauAcceptIndexingTableUnionsIntersections, false) namespace Luau { @@ -2750,7 +2751,7 @@ TypeId TypeChecker::checkRelationalOperation( if (lhsIsAny || rhsIsAny) return booleanType; - // Fallthrough here is intentional + [[fallthrough]]; } case AstExprBinary::CompareLt: case AstExprBinary::CompareGt: @@ -3506,54 +3507,207 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex } } - TableType* exprTable = getMutableTableType(exprType); - - if (!exprTable) + if (FFlag::LuauAcceptIndexingTableUnionsIntersections) { - reportError(TypeError{expr.expr->location, NotATable{exprType}}); - return errorRecoveryType(scope); - } + // We're going to have a whole vector. + std::vector tableTypes{}; + bool isUnion = true; - if (value) - { - const auto& it = exprTable->props.find(value->value.data); - if (it != exprTable->props.end()) + // We'd like for normalization eventually to deal with this sort of thing, but as a tactical affordance, we will + // attempt to deal with _one_ level of unions or intersections. + if (auto exprUnion = get(exprType)) { - return it->second.type(); + tableTypes.reserve(exprUnion->options.size()); + + for (auto option : exprUnion) + { + TableType* optionTable = getMutableTableType(option); + + if (!optionTable) + { + // TODO: we could do better here and report `option` is not a table as reasoning for the error + reportError(TypeError{expr.expr->location, NotATable{exprType}}); + return errorRecoveryType(scope); + } + + tableTypes.push_back(optionTable); + } } - else if ((ctx == ValueContext::LValue && exprTable->state == TableState::Unsealed) || exprTable->state == TableState::Free) + else if (auto exprIntersection = get(exprType)) + { + tableTypes.reserve(exprIntersection->parts.size()); + isUnion = false; + + for (auto part : exprIntersection) + { + TableType* partTable = getMutableTableType(part); + + if (!partTable) + { + // TODO: we could do better here and report `part` is not a table as reasoning for the error + reportError(TypeError{expr.expr->location, NotATable{exprType}}); + return errorRecoveryType(scope); + } + + tableTypes.push_back(partTable); + } + } + else if (auto exprTable = getMutableTableType(exprType)) { - TypeId resultType = freshType(scope); - Property& property = exprTable->props[value->value.data]; - property.setType(resultType); - property.location = expr.index->location; - return resultType; + tableTypes.push_back(exprTable); + } + else + { + reportError(TypeError{expr.expr->location, NotATable{exprType}}); + return errorRecoveryType(scope); } - } - if (exprTable->indexer) - { - const TableIndexer& indexer = *exprTable->indexer; - unify(indexType, indexer.indexType, scope, expr.index->location); - return indexer.indexResultType; - } - else if ((ctx == ValueContext::LValue && exprTable->state == TableState::Unsealed) || exprTable->state == TableState::Free) - { - TypeId indexerType = freshType(exprTable->level); - unify(indexType, indexerType, scope, expr.location); - TypeId indexResultType = freshType(exprTable->level); + if (value) + { + DenseHashSet propTypes{{}}; + + for (auto table : tableTypes) + { + const auto& it = table->props.find(value->value.data); + if (it != table->props.end()) + { + propTypes.insert(it->second.type()); + } + else if ((ctx == ValueContext::LValue && table->state == TableState::Unsealed) || table->state == TableState::Free) + { + TypeId resultType = freshType(scope); + Property& property = table->props[value->value.data]; + property.setType(resultType); + property.location = expr.index->location; + propTypes.insert(resultType); + } + } + + if (propTypes.size() == 1) + return *propTypes.begin(); + + if (!propTypes.empty()) + { + if (isUnion) + { + std::vector options = reduceUnion({propTypes.begin(), propTypes.end()}); + + if (options.empty()) + return neverType; + + if (options.size() == 1) + return options[0]; - exprTable->indexer = TableIndexer{anyIfNonstrict(indexerType), anyIfNonstrict(indexResultType)}; - return indexResultType; + return addType(UnionType{options}); + } + + return addType(IntersectionType{{propTypes.begin(), propTypes.end()}}); + } + } + + DenseHashSet resultTypes{{}}; + + for (auto table : tableTypes) + { + if (table->indexer) + { + const TableIndexer& indexer = *table->indexer; + unify(indexType, indexer.indexType, scope, expr.index->location); + resultTypes.insert(indexer.indexResultType); + } + else if ((ctx == ValueContext::LValue && table->state == TableState::Unsealed) || table->state == TableState::Free) + { + TypeId indexerType = freshType(table->level); + unify(indexType, indexerType, scope, expr.location); + TypeId indexResultType = freshType(table->level); + + table->indexer = TableIndexer{anyIfNonstrict(indexerType), anyIfNonstrict(indexResultType)}; + resultTypes.insert(indexResultType); + } + else + { + /* + * If we use [] indexing to fetch a property from a sealed table that + * has no indexer, we have no idea if it will work so we just return any + * and hope for the best. + */ + + // if this is a union, it's going to be equivalent to `any` no matter what at this point, so we'll just call it done. + if (isUnion) + return anyType; + + resultTypes.insert(anyType); + } + } + + if (resultTypes.size() == 1) + return *resultTypes.begin(); + + if (isUnion) + { + std::vector options = reduceUnion({resultTypes.begin(), resultTypes.end()}); + + if (options.empty()) + return neverType; + + if (options.size() == 1) + return options[0]; + + return addType(UnionType{options}); + } + + return addType(IntersectionType{{resultTypes.begin(), resultTypes.end()}}); } else { - /* - * If we use [] indexing to fetch a property from a sealed table that - * has no indexer, we have no idea if it will work so we just return any - * and hope for the best. - */ - return anyType; + TableType* exprTable = getMutableTableType(exprType); + if (!exprTable) + { + reportError(TypeError{expr.expr->location, NotATable{exprType}}); + return errorRecoveryType(scope); + } + + if (value) + { + const auto& it = exprTable->props.find(value->value.data); + if (it != exprTable->props.end()) + { + return it->second.type(); + } + else if ((ctx == ValueContext::LValue && exprTable->state == TableState::Unsealed) || exprTable->state == TableState::Free) + { + TypeId resultType = freshType(scope); + Property& property = exprTable->props[value->value.data]; + property.setType(resultType); + property.location = expr.index->location; + return resultType; + } + } + + if (exprTable->indexer) + { + const TableIndexer& indexer = *exprTable->indexer; + unify(indexType, indexer.indexType, scope, expr.index->location); + return indexer.indexResultType; + } + else if ((ctx == ValueContext::LValue && exprTable->state == TableState::Unsealed) || exprTable->state == TableState::Free) + { + TypeId indexerType = freshType(exprTable->level); + unify(indexType, indexerType, scope, expr.location); + TypeId indexResultType = freshType(exprTable->level); + + exprTable->indexer = TableIndexer{anyIfNonstrict(indexerType), anyIfNonstrict(indexResultType)}; + return indexResultType; + } + else + { + /* + * If we use [] indexing to fetch a property from a sealed table that + * has no indexer, we have no idea if it will work so we just return any + * and hope for the best. + */ + return anyType; + } } } diff --git a/Dependencies/luau/Luau/Analysis/src/TypePack.cpp b/Dependencies/luau/Luau/Analysis/src/TypePack.cpp index 9f3924f0..7e11d462 100644 --- a/Dependencies/luau/Luau/Analysis/src/TypePack.cpp +++ b/Dependencies/luau/Luau/Analysis/src/TypePack.cpp @@ -6,7 +6,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); namespace Luau { diff --git a/Dependencies/luau/Luau/Analysis/src/TypePath.cpp b/Dependencies/luau/Luau/Analysis/src/TypePath.cpp index 29f5cfb5..d2113ee3 100644 --- a/Dependencies/luau/Luau/Analysis/src/TypePath.cpp +++ b/Dependencies/luau/Luau/Analysis/src/TypePath.cpp @@ -13,7 +13,7 @@ #include #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); // Maximum number of steps to follow when traversing a path. May not always // equate to the number of components in a path, depending on the traversal @@ -29,7 +29,7 @@ namespace TypePath Property::Property(std::string name) : name(std::move(name)) { - LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(!FFlag::LuauSolverV2); } Property Property::read(std::string name) @@ -156,21 +156,21 @@ Path PathBuilder::build() PathBuilder& PathBuilder::readProp(std::string name) { - LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(FFlag::LuauSolverV2); components.push_back(Property{std::move(name), true}); return *this; } PathBuilder& PathBuilder::writeProp(std::string name) { - LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(FFlag::LuauSolverV2); components.push_back(Property{std::move(name), false}); return *this; } PathBuilder& PathBuilder::prop(std::string name) { - LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(!FFlag::LuauSolverV2); components.push_back(Property{std::move(name)}); return *this; } @@ -343,7 +343,7 @@ struct TraversalState if (prop) { std::optional maybeType; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) maybeType = property.isRead ? prop->readTy : prop->writeTy; else maybeType = prop->type(); @@ -540,7 +540,7 @@ std::string toString(const TypePath::Path& path, bool prefixDot) if constexpr (std::is_same_v) { result << '['; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (c.isRead) result << "read "; diff --git a/Dependencies/luau/Luau/Analysis/src/TypeUtils.cpp b/Dependencies/luau/Luau/Analysis/src/TypeUtils.cpp index b40805e9..f1c60f06 100644 --- a/Dependencies/luau/Luau/Analysis/src/TypeUtils.cpp +++ b/Dependencies/luau/Luau/Analysis/src/TypeUtils.cpp @@ -9,7 +9,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); namespace Luau { @@ -153,7 +153,7 @@ std::optional findTablePropertyRespectingMeta( const auto& it = tableType->props.find(name); if (it != tableType->props.end()) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { switch (context) { @@ -301,6 +301,8 @@ TypePack extendTypePack( TypePack newPack; newPack.tail = arena.freshTypePack(ftp->scope); + if (FFlag::LuauSolverV2) + result.tail = newPack.tail; size_t overridesIndex = 0; while (result.head.size() < length) { @@ -311,7 +313,7 @@ TypePack extendTypePack( } else { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType}; t = arena.addType(ft); @@ -424,7 +426,7 @@ TypeId stripNil(NotNull builtinTypes, TypeArena& arena, TypeId ty) ErrorSuppression shouldSuppressErrors(NotNull normalizer, TypeId ty) { - LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(FFlag::LuauSolverV2); std::shared_ptr normType = normalizer->normalize(ty); if (!normType) diff --git a/Dependencies/luau/Luau/Analysis/src/Unifier.cpp b/Dependencies/luau/Luau/Analysis/src/Unifier.cpp index 3dc66d1d..fa7ff876 100644 --- a/Dependencies/luau/Luau/Analysis/src/Unifier.cpp +++ b/Dependencies/luau/Luau/Analysis/src/Unifier.cpp @@ -19,7 +19,7 @@ LUAU_FASTINT(LuauTypeInferTypePackLoopLimit) LUAU_FASTFLAG(LuauErrorRecoveryType) LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false) LUAU_FASTFLAGVARIABLE(LuauTransitiveSubtyping, false) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering, false) LUAU_FASTFLAGVARIABLE(LuauUnifierShouldNotCopyError, false) LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart, false) @@ -405,7 +405,7 @@ Unifier::Unifier(NotNull normalizer, NotNull scope, const Loc LUAU_ASSERT(sharedState.iceHandler); // Unifier is not usable when this flag is enabled! Please consider using Subtyping instead. - LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution); + LUAU_ASSERT(!FFlag::LuauSolverV2); } void Unifier::tryUnify(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection, const LiteralProperties* literalProperties) @@ -1646,7 +1646,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal auto mkFreshType = [this](Scope* scope, TypeLevel level) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return freshType(NotNull{types}, builtinTypes, scope); else return types->freshType(scope, level); diff --git a/Dependencies/luau/Luau/Ast/src/Parser.cpp b/Dependencies/luau/Luau/Ast/src/Parser.cpp index 59533bae..4b9eddda 100644 --- a/Dependencies/luau/Luau/Ast/src/Parser.cpp +++ b/Dependencies/luau/Luau/Ast/src/Parser.cpp @@ -16,7 +16,7 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100) // Warning: If you are introducing new syntax, ensure that it is behind a separate // flag so that we don't break production games by reverting syntax changes. // See docs/SyntaxChanges.md for an explanation. -LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false) +LUAU_FASTFLAGVARIABLE(LuauSolverV2, false) LUAU_FASTFLAGVARIABLE(LuauNativeAttribute, false) LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr, false) LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions, false) @@ -784,6 +784,7 @@ AstStat* Parser::parseAttributeStat() AstExpr* expr = parsePrimaryExpr(/* asStatement= */ true); return parseDeclaration(expr->location, attributes); } + [[fallthrough]]; default: return reportStatError( lexer.current().location, diff --git a/Dependencies/luau/Luau/CLI/Require.cpp b/Dependencies/luau/Luau/CLI/Require.cpp index 5de78a4a..b6753e96 100644 --- a/Dependencies/luau/Luau/CLI/Require.cpp +++ b/Dependencies/luau/Luau/CLI/Require.cpp @@ -204,7 +204,18 @@ void RequireResolver::substituteAliasIfPresent(std::string& path) { if (path.size() < 1 || path[0] != '@') return; - std::string potentialAlias = path.substr(1, path.find_first_of("\\/")); + + // To ignore the '@' alias prefix when processing the alias + const size_t aliasStartPos = 1; + + // If a directory separator was found, the length of the alias is the + // distance between the start of the alias and the separator. Otherwise, + // the whole string after the alias symbol is the alias. + size_t aliasLen = path.find_first_of("\\/"); + if (aliasLen != std::string::npos) + aliasLen -= aliasStartPos; + + const std::string potentialAlias = path.substr(aliasStartPos, aliasLen); // Not worth searching when potentialAlias cannot be an alias if (!Luau::isValidAlias(potentialAlias)) diff --git a/Dependencies/luau/Luau/CMakeLists.txt b/Dependencies/luau/Luau/CMakeLists.txt index 34e104e1..b18cd5c9 100644 --- a/Dependencies/luau/Luau/CMakeLists.txt +++ b/Dependencies/luau/Luau/CMakeLists.txt @@ -111,6 +111,7 @@ if(MSVC) list(APPEND LUAU_OPTIONS "/we4388") # Also signed/unsigned mismatch else() list(APPEND LUAU_OPTIONS -Wall) # All warnings + list(APPEND LUAU_OPTIONS -Wimplicit-fallthrough) list(APPEND LUAU_OPTIONS -Wsign-compare) # This looks to be included in -Wall for GCC but not clang endif() diff --git a/Dependencies/luau/Luau/CodeGen/include/Luau/IrVisitUseDef.h b/Dependencies/luau/Luau/CodeGen/include/Luau/IrVisitUseDef.h index 6744bd65..39dd6cf8 100644 --- a/Dependencies/luau/Luau/CodeGen/include/Luau/IrVisitUseDef.h +++ b/Dependencies/luau/Luau/CodeGen/include/Luau/IrVisitUseDef.h @@ -4,8 +4,6 @@ #include "Luau/Common.h" #include "Luau/IrData.h" -LUAU_FASTFLAG(LuauCodegenFastcall3) - namespace Luau { namespace CodeGen @@ -113,47 +111,16 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i break; case IrCmd::FASTCALL: - if (FFlag::LuauCodegenFastcall3) - { - visitor.use(inst.c); - - if (int nresults = function.intOp(inst.d); nresults != -1) - visitor.defRange(vmRegOp(inst.b), nresults); - } - else - { - if (int count = function.intOp(inst.e); count != -1) - { - if (count >= 3) - { - CODEGEN_ASSERT(inst.d.kind == IrOpKind::VmReg && vmRegOp(inst.d) == vmRegOp(inst.c) + 1); - - visitor.useRange(vmRegOp(inst.c), count); - } - else - { - if (count >= 1) - visitor.use(inst.c); - - if (count >= 2) - visitor.maybeUse(inst.d); // Argument can also be a VmConst - } - } - else - { - visitor.useVarargs(vmRegOp(inst.c)); - } + visitor.use(inst.c); - // Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG - if (int count = function.intOp(inst.f); count != -1) - visitor.defRange(vmRegOp(inst.b), count); - } + if (int nresults = function.intOp(inst.d); nresults != -1) + visitor.defRange(vmRegOp(inst.b), nresults); break; case IrCmd::INVOKE_FASTCALL: - if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.f : inst.e); count != -1) + if (int count = function.intOp(inst.f); count != -1) { // Only LOP_FASTCALL3 lowering is allowed to have third optional argument - if (count >= 3 && (!FFlag::LuauCodegenFastcall3 || inst.e.kind == IrOpKind::Undef)) + if (count >= 3 && inst.e.kind == IrOpKind::Undef) { CODEGEN_ASSERT(inst.d.kind == IrOpKind::VmReg && vmRegOp(inst.d) == vmRegOp(inst.c) + 1); @@ -167,7 +134,7 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i if (count >= 2) visitor.maybeUse(inst.d); // Argument can also be a VmConst - if (FFlag::LuauCodegenFastcall3 && count >= 3) + if (count >= 3) visitor.maybeUse(inst.e); // Argument can also be a VmConst } } @@ -177,7 +144,7 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i } // Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG - if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f); count != -1) + if (int count = function.intOp(inst.g); count != -1) visitor.defRange(vmRegOp(inst.b), count); break; case IrCmd::FORGLOOP: diff --git a/Dependencies/luau/Luau/CodeGen/src/BytecodeAnalysis.cpp b/Dependencies/luau/Luau/CodeGen/src/BytecodeAnalysis.cpp index 01adea34..8d2efebe 100644 --- a/Dependencies/luau/Luau/CodeGen/src/BytecodeAnalysis.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/BytecodeAnalysis.cpp @@ -11,8 +11,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauCodegenFastcall3, false) - namespace Luau { namespace CodeGen @@ -1101,8 +1099,6 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) } case LOP_FASTCALL3: { - CODEGEN_ASSERT(FFlag::LuauCodegenFastcall3); - int bfid = LUAU_INSN_A(*pc); int skip = LUAU_INSN_C(*pc); int aux = pc[1]; diff --git a/Dependencies/luau/Luau/CodeGen/src/CodeGenAssembly.cpp b/Dependencies/luau/Luau/CodeGen/src/CodeGenAssembly.cpp index 295e1cb8..c423a1ce 100644 --- a/Dependencies/luau/Luau/CodeGen/src/CodeGenAssembly.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/CodeGenAssembly.cpp @@ -149,7 +149,10 @@ static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, A Proto* root = clvalue(func)->l.p; if ((options.compilationOptions.flags & CodeGen_OnlyNativeModules) != 0 && (root->flags & LPF_NATIVE_MODULE) == 0) + { + build.finalize(); return std::string(); + } std::vector protos; if (FFlag::LuauNativeAttribute) @@ -174,7 +177,7 @@ static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, A if (protos.empty()) { - build.finalize(); // to avoid assertion in AssemblyBuilder dtor + build.finalize(); return std::string(); } diff --git a/Dependencies/luau/Luau/CodeGen/src/EmitBuiltinsX64.cpp b/Dependencies/luau/Luau/CodeGen/src/EmitBuiltinsX64.cpp index 15aab4b6..bee59908 100644 --- a/Dependencies/luau/Luau/CodeGen/src/EmitBuiltinsX64.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/EmitBuiltinsX64.cpp @@ -12,8 +12,6 @@ #include "lstate.h" -LUAU_FASTFLAG(LuauCodegenMathSign) - namespace Luau { namespace CodeGen @@ -57,36 +55,6 @@ static void emitBuiltinMathModf(IrRegAllocX64& regs, AssemblyBuilderX64& build, } } -static void emitBuiltinMathSign(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int arg) -{ - CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); - - ScopedRegX64 tmp0{regs, SizeX64::xmmword}; - ScopedRegX64 tmp1{regs, SizeX64::xmmword}; - ScopedRegX64 tmp2{regs, SizeX64::xmmword}; - ScopedRegX64 tmp3{regs, SizeX64::xmmword}; - - build.vmovsd(tmp0.reg, luauRegValue(arg)); - build.vxorpd(tmp1.reg, tmp1.reg, tmp1.reg); - - // Set tmp2 to -1 if arg < 0, else 0 - build.vcmpltsd(tmp2.reg, tmp0.reg, tmp1.reg); - build.vmovsd(tmp3.reg, build.f64(-1)); - build.vandpd(tmp2.reg, tmp2.reg, tmp3.reg); - - // Set mask bit to 1 if 0 < arg, else 0 - build.vcmpltsd(tmp0.reg, tmp1.reg, tmp0.reg); - - // Result = (mask-bit == 1) ? 1.0 : tmp2 - // If arg < 0 then tmp2 is -1 and mask-bit is 0, result is -1 - // If arg == 0 then tmp2 is 0 and mask-bit is 0, result is 0 - // If arg > 0 then tmp2 is 0 and mask-bit is 1, result is 1 - build.vblendvpd(tmp0.reg, tmp2.reg, build.f64x2(1, 1), tmp0.reg); - - build.vmovsd(luauRegValue(ra), tmp0.reg); - build.mov(luauRegTag(ra), LUA_TNUMBER); -} - void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int ra, int arg, int nresults) { switch (bfid) @@ -97,10 +65,6 @@ void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int r case LBF_MATH_MODF: CODEGEN_ASSERT(nresults == 1 || nresults == 2); return emitBuiltinMathModf(regs, build, ra, arg, nresults); - case LBF_MATH_SIGN: - CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); - CODEGEN_ASSERT(nresults == 1); - return emitBuiltinMathSign(regs, build, ra, arg); default: CODEGEN_ASSERT(!"Missing x64 lowering"); } diff --git a/Dependencies/luau/Luau/CodeGen/src/IrBuilder.cpp b/Dependencies/luau/Luau/CodeGen/src/IrBuilder.cpp index 53def728..3e6f85d8 100644 --- a/Dependencies/luau/Luau/CodeGen/src/IrBuilder.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/IrBuilder.cpp @@ -13,8 +13,6 @@ #include -LUAU_FASTFLAG(LuauCodegenFastcall3) - namespace Luau { namespace CodeGen @@ -458,8 +456,6 @@ void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i) handleFastcallFallback(translateFastCallN(*this, pc, i, true, 2, vmConst(pc[1]), undef()), pc, i); break; case LOP_FASTCALL3: - CODEGEN_ASSERT(FFlag::LuauCodegenFastcall3); - handleFastcallFallback(translateFastCallN(*this, pc, i, true, 3, vmReg(pc[1] & 0xff), vmReg((pc[1] >> 8) & 0xff)), pc, i); break; case LOP_FORNPREP: diff --git a/Dependencies/luau/Luau/CodeGen/src/IrLoweringA64.cpp b/Dependencies/luau/Luau/CodeGen/src/IrLoweringA64.cpp index f40faa2d..cd73bcbb 100644 --- a/Dependencies/luau/Luau/CodeGen/src/IrLoweringA64.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/IrLoweringA64.cpp @@ -11,8 +11,7 @@ #include "lstate.h" #include "lgc.h" -LUAU_FASTFLAG(LuauCodegenFastcall3) -LUAU_FASTFLAG(LuauCodegenMathSign) +LUAU_FASTFLAGVARIABLE(LuauCodegenArmNumToVecFix, false) namespace Luau { @@ -235,25 +234,6 @@ static bool emitBuiltin(AssemblyBuilderA64& build, IrFunction& function, IrRegAl } return true; } - case LBF_MATH_SIGN: - { - CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); - CODEGEN_ASSERT(nresults == 1); - build.ldr(d0, mem(rBase, arg * sizeof(TValue) + offsetof(TValue, value.n))); - build.fcmpz(d0); - build.fmov(d0, 0.0); - build.fmov(d1, 1.0); - build.fcsel(d0, d1, d0, getConditionFP(IrCondition::Greater)); - build.fmov(d1, -1.0); - build.fcsel(d0, d1, d0, getConditionFP(IrCondition::Less)); - build.str(d0, mem(rBase, res * sizeof(TValue) + offsetof(TValue, value.n))); - - RegisterA64 temp = regs.allocTemp(KindA64::w); - build.mov(temp, LUA_TNUMBER); - build.str(temp, mem(rBase, res * sizeof(TValue) + offsetof(TValue, tt))); - - return true; - } default: CODEGEN_ASSERT(!"Missing A64 lowering"); @@ -701,8 +681,6 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) } case IrCmd::SIGN_NUM: { - CODEGEN_ASSERT(FFlag::LuauCodegenMathSign); - inst.regA64 = regs.allocReuse(KindA64::d, index, {inst.a}); RegisterA64 temp = tempDouble(inst.a); @@ -1143,7 +1121,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) else { RegisterA64 tempd = tempDouble(inst.a); - RegisterA64 temps = castReg(KindA64::s, tempd); + RegisterA64 temps = FFlag::LuauCodegenArmNumToVecFix ? regs.allocTemp(KindA64::s) : castReg(KindA64::s, tempd); build.fcvt(temps, tempd); build.dup_4s(inst.regA64, castReg(KindA64::q, temps), 0); @@ -1194,87 +1172,53 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) case IrCmd::FASTCALL: regs.spill(build, index); - if (FFlag::LuauCodegenFastcall3) - error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d)); - else - error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.f)); - + error |= !emitBuiltin(build, function, regs, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d)); break; case IrCmd::INVOKE_FASTCALL: { - if (FFlag::LuauCodegenFastcall3) - { - // We might need a temporary and we have to preserve it over the spill - RegisterA64 temp = regs.allocTemp(KindA64::q); - regs.spill(build, index, {temp}); + // We might need a temporary and we have to preserve it over the spill + RegisterA64 temp = regs.allocTemp(KindA64::q); + regs.spill(build, index, {temp}); - build.mov(x0, rState); - build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue))); - build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue))); - build.mov(w3, intOp(inst.g)); // nresults - - // 'E' argument can only be produced by LOP_FASTCALL3 lowering - if (inst.e.kind != IrOpKind::Undef) - { - CODEGEN_ASSERT(intOp(inst.f) == 3); + build.mov(x0, rState); + build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue))); + build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue))); + build.mov(w3, intOp(inst.g)); // nresults - build.ldr(x4, mem(rState, offsetof(lua_State, top))); + // 'E' argument can only be produced by LOP_FASTCALL3 lowering + if (inst.e.kind != IrOpKind::Undef) + { + CODEGEN_ASSERT(intOp(inst.f) == 3); - build.ldr(temp, mem(rBase, vmRegOp(inst.d) * sizeof(TValue))); - build.str(temp, mem(x4, 0)); + build.ldr(x4, mem(rState, offsetof(lua_State, top))); - build.ldr(temp, mem(rBase, vmRegOp(inst.e) * sizeof(TValue))); - build.str(temp, mem(x4, sizeof(TValue))); - } - else - { - if (inst.d.kind == IrOpKind::VmReg) - build.add(x4, rBase, uint16_t(vmRegOp(inst.d) * sizeof(TValue))); - else if (inst.d.kind == IrOpKind::VmConst) - emitAddOffset(build, x4, rConstants, vmConstOp(inst.d) * sizeof(TValue)); - else - CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef); - } + build.ldr(temp, mem(rBase, vmRegOp(inst.d) * sizeof(TValue))); + build.str(temp, mem(x4, 0)); - // nparams - if (intOp(inst.f) == LUA_MULTRET) - { - // L->top - (ra + 1) - build.ldr(x5, mem(rState, offsetof(lua_State, top))); - build.sub(x5, x5, rBase); - build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue))); - build.lsr(x5, x5, kTValueSizeLog2); - } - else - build.mov(w5, intOp(inst.f)); + build.ldr(temp, mem(rBase, vmRegOp(inst.e) * sizeof(TValue))); + build.str(temp, mem(x4, sizeof(TValue))); } else { - regs.spill(build, index); - build.mov(x0, rState); - build.add(x1, rBase, uint16_t(vmRegOp(inst.b) * sizeof(TValue))); - build.add(x2, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue))); - build.mov(w3, intOp(inst.f)); // nresults - if (inst.d.kind == IrOpKind::VmReg) build.add(x4, rBase, uint16_t(vmRegOp(inst.d) * sizeof(TValue))); else if (inst.d.kind == IrOpKind::VmConst) emitAddOffset(build, x4, rConstants, vmConstOp(inst.d) * sizeof(TValue)); else CODEGEN_ASSERT(inst.d.kind == IrOpKind::Undef); + } - // nparams - if (intOp(inst.e) == LUA_MULTRET) - { - // L->top - (ra + 1) - build.ldr(x5, mem(rState, offsetof(lua_State, top))); - build.sub(x5, x5, rBase); - build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue))); - build.lsr(x5, x5, kTValueSizeLog2); - } - else - build.mov(w5, intOp(inst.e)); + // nparams + if (intOp(inst.f) == LUA_MULTRET) + { + // L->top - (ra + 1) + build.ldr(x5, mem(rState, offsetof(lua_State, top))); + build.sub(x5, x5, rBase); + build.sub(x5, x5, uint16_t((vmRegOp(inst.b) + 1) * sizeof(TValue))); + build.lsr(x5, x5, kTValueSizeLog2); } + else + build.mov(w5, intOp(inst.f)); build.ldr(x6, mem(rNativeContext, offsetof(NativeContext, luauF_table) + uintOp(inst.a) * sizeof(luau_FastFunction))); build.blr(x6); diff --git a/Dependencies/luau/Luau/CodeGen/src/IrLoweringX64.cpp b/Dependencies/luau/Luau/CodeGen/src/IrLoweringX64.cpp index 76d88955..d06cef13 100644 --- a/Dependencies/luau/Luau/CodeGen/src/IrLoweringX64.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/IrLoweringX64.cpp @@ -15,9 +15,6 @@ #include "lstate.h" #include "lgc.h" -LUAU_FASTFLAG(LuauCodegenFastcall3) -LUAU_FASTFLAG(LuauCodegenMathSign) - namespace Luau { namespace CodeGen @@ -596,8 +593,6 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) break; case IrCmd::SIGN_NUM: { - CODEGEN_ASSERT(FFlag::LuauCodegenMathSign); - inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a}); ScopedRegX64 tmp0{regs, SizeX64::xmmword}; @@ -1038,10 +1033,7 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) case IrCmd::FASTCALL: { - if (FFlag::LuauCodegenFastcall3) - emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d)); - else - emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.f)); + emitBuiltin(regs, build, uintOp(inst.a), vmRegOp(inst.b), vmRegOp(inst.c), intOp(inst.d)); break; } case IrCmd::INVOKE_FASTCALL: @@ -1052,7 +1044,7 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) ScopedRegX64 argsAlt{regs}; // 'E' argument can only be produced by LOP_FASTCALL3 - if (FFlag::LuauCodegenFastcall3 && inst.e.kind != IrOpKind::Undef) + if (inst.e.kind != IrOpKind::Undef) { CODEGEN_ASSERT(intOp(inst.f) == 3); @@ -1079,8 +1071,8 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) int ra = vmRegOp(inst.b); int arg = vmRegOp(inst.c); - int nparams = intOp(FFlag::LuauCodegenFastcall3 ? inst.f : inst.e); - int nresults = intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f); + int nparams = intOp(inst.f); + int nresults = intOp(inst.g); IrCallWrapperX64 callWrap(regs, build, index); callWrap.addArgument(SizeX64::qword, rState); @@ -1088,7 +1080,7 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) callWrap.addArgument(SizeX64::qword, luauRegAddress(arg)); callWrap.addArgument(SizeX64::dword, nresults); - if (FFlag::LuauCodegenFastcall3 && inst.e.kind != IrOpKind::Undef) + if (inst.e.kind != IrOpKind::Undef) callWrap.addArgument(SizeX64::qword, argsAlt); else callWrap.addArgument(SizeX64::qword, args); diff --git a/Dependencies/luau/Luau/CodeGen/src/IrTranslateBuiltins.cpp b/Dependencies/luau/Luau/CodeGen/src/IrTranslateBuiltins.cpp index 7abab9f0..52efaef1 100644 --- a/Dependencies/luau/Luau/CodeGen/src/IrTranslateBuiltins.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/IrTranslateBuiltins.cpp @@ -8,9 +8,6 @@ #include -LUAU_FASTFLAG(LuauCodegenFastcall3) -LUAU_FASTFLAGVARIABLE(LuauCodegenMathSign, false) - // TODO: when nresults is less than our actual result count, we can skip computing/writing unused results static const int kMinMaxUnrolledParams = 5; @@ -39,33 +36,6 @@ static IrOp builtinLoadDouble(IrBuilder& build, IrOp arg) // Wrapper code for all builtins with a fixed signature and manual assembly lowering of the body -// (number, ...) -> number -static BuiltinImplResult translateBuiltinNumberToNumber( - IrBuilder& build, - LuauBuiltinFunction bfid, - int nparams, - int ra, - int arg, - IrOp args, - int nresults, - int pcpos -) -{ - CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); - - if (nparams < 1 || nresults > 1) - return {BuiltinImplType::None, -1}; - - builtinCheckDouble(build, build.vmReg(arg), pcpos); - - if (FFlag::LuauCodegenFastcall3) - build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(1)); - else - build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), args, build.constInt(1), build.constInt(1)); - - return {BuiltinImplType::Full, 1}; -} - static BuiltinImplResult translateBuiltinNumberToNumberLibm( IrBuilder& build, LuauBuiltinFunction bfid, @@ -142,18 +112,7 @@ static BuiltinImplResult translateBuiltinNumberTo2Number( builtinCheckDouble(build, build.vmReg(arg), pcpos); - if (FFlag::LuauCodegenFastcall3) - build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(nresults == 1 ? 1 : 2)); - else - build.inst( - IrCmd::FASTCALL, - build.constUint(bfid), - build.vmReg(ra), - build.vmReg(arg), - build.undef(), - build.constInt(1), - build.constInt(nresults == 1 ? 1 : 2) - ); + build.inst(IrCmd::FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), build.constInt(nresults == 1 ? 1 : 2)); return {BuiltinImplType::Full, 2}; } @@ -250,10 +209,10 @@ static BuiltinImplResult translateBuiltinMathMinMax( builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - if (FFlag::LuauCodegenFastcall3 && nparams >= 3) + if (nparams >= 3) builtinCheckDouble(build, arg3, pcpos); - for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i) + for (int i = 4; i <= nparams; ++i) builtinCheckDouble(build, build.vmReg(vmRegOp(args) + (i - 2)), pcpos); IrOp varg1 = builtinLoadDouble(build, build.vmReg(arg)); @@ -261,13 +220,13 @@ static BuiltinImplResult translateBuiltinMathMinMax( IrOp res = build.inst(cmd, varg2, varg1); // Swapped arguments are required for consistency with VM builtins - if (FFlag::LuauCodegenFastcall3 && nparams >= 3) + if (nparams >= 3) { IrOp arg = builtinLoadDouble(build, arg3); res = build.inst(cmd, arg, res); } - for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i) + for (int i = 4; i <= nparams; ++i) { IrOp arg = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + (i - 2))); res = build.inst(cmd, arg, res); @@ -302,10 +261,10 @@ static BuiltinImplResult translateBuiltinMathClamp( builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos); + builtinCheckDouble(build, arg3, pcpos); IrOp min = builtinLoadDouble(build, args); - IrOp max = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1)); + IrOp max = builtinLoadDouble(build, arg3); build.inst(IrCmd::JUMP_CMP_NUM, min, max, build.cond(IrCondition::NotLessEqual), fallback, block); build.beginBlock(block); @@ -386,10 +345,10 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp( builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - if (FFlag::LuauCodegenFastcall3 && nparams >= 3) + if (nparams >= 3) builtinCheckDouble(build, arg3, pcpos); - for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i) + for (int i = 4; i <= nparams; ++i) builtinCheckDouble(build, build.vmReg(vmRegOp(args) + (i - 2)), pcpos); IrOp va = builtinLoadDouble(build, build.vmReg(arg)); @@ -400,7 +359,7 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp( IrOp res = build.inst(cmd, vaui, vbui); - if (FFlag::LuauCodegenFastcall3 && nparams >= 3) + if (nparams >= 3) { IrOp vc = builtinLoadDouble(build, arg3); IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vc); @@ -408,7 +367,7 @@ static BuiltinImplResult translateBuiltinBit32BinaryOp( res = build.inst(cmd, res, arg); } - for (int i = (FFlag::LuauCodegenFastcall3 ? 4 : 3); i <= nparams; ++i) + for (int i = 4; i <= nparams; ++i) { IrOp vc = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + (i - 2))); IrOp arg = build.inst(IrCmd::NUM_TO_UINT, vc); @@ -599,8 +558,8 @@ static BuiltinImplResult translateBuiltinBit32Extract( { IrOp f = build.inst(IrCmd::NUM_TO_INT, vb); - builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1), pcpos); - IrOp vc = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1)); + builtinCheckDouble(build, arg3, pcpos); + IrOp vc = builtinLoadDouble(build, arg3); IrOp w = build.inst(IrCmd::NUM_TO_INT, vc); IrOp block1 = build.block(IrBlockKind::Internal); @@ -705,11 +664,11 @@ static BuiltinImplResult translateBuiltinBit32Replace( builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1), pcpos); + builtinCheckDouble(build, arg3, pcpos); IrOp va = builtinLoadDouble(build, build.vmReg(arg)); IrOp vb = builtinLoadDouble(build, args); - IrOp vc = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(args.index + 1)); + IrOp vc = builtinLoadDouble(build, arg3); IrOp n = build.inst(IrCmd::NUM_TO_UINT, va); IrOp v = build.inst(IrCmd::NUM_TO_UINT, vb); @@ -734,8 +693,8 @@ static BuiltinImplResult translateBuiltinBit32Replace( } else { - builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? build.vmReg(vmRegOp(args) + 2) : build.vmReg(args.index + 2), pcpos); - IrOp vd = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? build.vmReg(vmRegOp(args) + 2) : build.vmReg(args.index + 2)); + builtinCheckDouble(build, build.vmReg(vmRegOp(args) + 2), pcpos); + IrOp vd = builtinLoadDouble(build, build.vmReg(vmRegOp(args) + 2)); IrOp w = build.inst(IrCmd::NUM_TO_INT, vd); IrOp block1 = build.block(IrBlockKind::Internal); @@ -781,11 +740,11 @@ static BuiltinImplResult translateBuiltinVector(IrBuilder& build, int nparams, i builtinCheckDouble(build, build.vmReg(arg), pcpos); builtinCheckDouble(build, args, pcpos); - builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos); + builtinCheckDouble(build, arg3, pcpos); IrOp x = builtinLoadDouble(build, build.vmReg(arg)); IrOp y = builtinLoadDouble(build, args); - IrOp z = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1)); + IrOp z = builtinLoadDouble(build, arg3); build.inst(IrCmd::STORE_VECTOR, build.vmReg(ra), x, y, z); build.inst(IrCmd::STORE_TAG, build.vmReg(ra), build.constTag(LUA_TVECTOR)); @@ -863,7 +822,7 @@ static void translateBufferArgsAndCheckBounds( builtinCheckDouble(build, args, pcpos); if (nparams == 3) - builtinCheckDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1), pcpos); + builtinCheckDouble(build, arg3, pcpos); buf = build.inst(IrCmd::LOAD_POINTER, build.vmReg(arg)); @@ -920,7 +879,7 @@ static BuiltinImplResult translateBuiltinBufferWrite( IrOp buf, intIndex; translateBufferArgsAndCheckBounds(build, nparams, arg, args, arg3, size, pcpos, buf, intIndex); - IrOp numValue = builtinLoadDouble(build, FFlag::LuauCodegenFastcall3 ? arg3 : build.vmReg(vmRegOp(args) + 1)); + IrOp numValue = builtinLoadDouble(build, arg3); build.inst(writeCmd, buf, intIndex, convCmd == IrCmd::NOP ? numValue : build.inst(convCmd, numValue)); return {BuiltinImplType::Full, 0}; @@ -982,10 +941,7 @@ BuiltinImplResult translateBuiltin( case LBF_MATH_LOG10: return translateBuiltinNumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, nresults, pcpos); case LBF_MATH_SIGN: - if (FFlag::LuauCodegenMathSign) - return translateBuiltinMathUnary(build, IrCmd::SIGN_NUM, nparams, ra, arg, nresults, pcpos); - else - return translateBuiltinNumberToNumber(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos); + return translateBuiltinMathUnary(build, IrCmd::SIGN_NUM, nparams, ra, arg, nresults, pcpos); case LBF_MATH_POW: case LBF_MATH_FMOD: case LBF_MATH_ATAN2: diff --git a/Dependencies/luau/Luau/CodeGen/src/IrTranslation.cpp b/Dependencies/luau/Luau/CodeGen/src/IrTranslation.cpp index 5d6aa95a..62829766 100644 --- a/Dependencies/luau/Luau/CodeGen/src/IrTranslation.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/IrTranslation.cpp @@ -13,8 +13,6 @@ #include "lstate.h" #include "ltm.h" -LUAU_FASTFLAG(LuauCodegenFastcall3) - namespace Luau { namespace CodeGen @@ -767,7 +765,7 @@ IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool builtinArgs = build.constDouble(protok.value.n); } - IrOp builtinArg3 = FFlag::LuauCodegenFastcall3 ? (customParams ? customArg3 : build.vmReg(ra + 3)) : IrOp{}; + IrOp builtinArg3 = customParams ? customArg3 : build.vmReg(ra + 3); IrOp fallback = build.block(IrBlockKind::Fallback); @@ -793,7 +791,7 @@ IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool return build.undef(); } } - else if (FFlag::LuauCodegenFastcall3) + else { IrOp arg3 = customParams ? customArg3 : build.undef(); @@ -817,21 +815,6 @@ IrOp translateFastCallN(IrBuilder& build, const Instruction* pc, int pcpos, bool else if (nparams == LUA_MULTRET) build.inst(IrCmd::ADJUST_STACK_TO_TOP); } - else - { - // TODO: we can skip saving pc for some well-behaved builtins which we didn't inline - build.inst(IrCmd::SET_SAVEDPC, build.constUint(pcpos + getOpLength(opcode))); - - IrOp res = build.inst( - IrCmd::INVOKE_FASTCALL, build.constUint(bfid), build.vmReg(ra), build.vmReg(arg), args, build.constInt(nparams), build.constInt(nresults) - ); - build.inst(IrCmd::CHECK_FASTCALL_RES, res, fallback); - - if (nresults == LUA_MULTRET) - build.inst(IrCmd::ADJUST_STACK_TO_REG, build.vmReg(ra), res); - else if (nparams == LUA_MULTRET) - build.inst(IrCmd::ADJUST_STACK_TO_TOP); - } return fallback; } diff --git a/Dependencies/luau/Luau/CodeGen/src/IrValueLocationTracking.cpp b/Dependencies/luau/Luau/CodeGen/src/IrValueLocationTracking.cpp index 0224b49b..050b951e 100644 --- a/Dependencies/luau/Luau/CodeGen/src/IrValueLocationTracking.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/IrValueLocationTracking.cpp @@ -3,8 +3,6 @@ #include "Luau/IrUtils.h" -LUAU_FASTFLAG(LuauCodegenFastcall3) - namespace Luau { namespace CodeGen @@ -46,11 +44,11 @@ void IrValueLocationTracking::beforeInstLowering(IrInst& inst) invalidateRestoreVmRegs(vmRegOp(inst.a), -1); break; case IrCmd::FASTCALL: - invalidateRestoreVmRegs(vmRegOp(inst.b), function.intOp(FFlag::LuauCodegenFastcall3 ? inst.d : inst.f)); + invalidateRestoreVmRegs(vmRegOp(inst.b), function.intOp(inst.d)); break; case IrCmd::INVOKE_FASTCALL: // Multiple return sequences (count == -1) are defined by ADJUST_STACK_TO_REG - if (int count = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f); count != -1) + if (int count = function.intOp(inst.g); count != -1) invalidateRestoreVmRegs(vmRegOp(inst.b), count); break; case IrCmd::DO_ARITH: diff --git a/Dependencies/luau/Luau/CodeGen/src/OptimizeConstProp.cpp b/Dependencies/luau/Luau/CodeGen/src/OptimizeConstProp.cpp index e7207731..f3271d3f 100644 --- a/Dependencies/luau/Luau/CodeGen/src/OptimizeConstProp.cpp +++ b/Dependencies/luau/Luau/CodeGen/src/OptimizeConstProp.cpp @@ -18,8 +18,6 @@ LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3) LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64) LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64) LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks, false) -LUAU_FASTFLAG(LuauCodegenFastcall3) -LUAU_FASTFLAG(LuauCodegenMathSign) namespace Luau { @@ -1119,7 +1117,7 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& { LuauBuiltinFunction bfid = LuauBuiltinFunction(function.uintOp(inst.a)); int firstReturnReg = vmRegOp(inst.b); - int nresults = function.intOp(FFlag::LuauCodegenFastcall3 ? inst.d : inst.f); + int nresults = function.intOp(inst.d); // TODO: FASTCALL is more restrictive than INVOKE_FASTCALL; we should either determine the exact semantics, or rework it handleBuiltinEffects(state, bfid, firstReturnReg, nresults); @@ -1133,19 +1131,13 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& if (nresults > 1) state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg + 1)}, LUA_TNUMBER); break; - case LBF_MATH_SIGN: - CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); - state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER); - break; default: break; } break; } case IrCmd::INVOKE_FASTCALL: - handleBuiltinEffects( - state, LuauBuiltinFunction(function.uintOp(inst.a)), vmRegOp(inst.b), function.intOp(FFlag::LuauCodegenFastcall3 ? inst.g : inst.f) - ); + handleBuiltinEffects(state, LuauBuiltinFunction(function.uintOp(inst.a)), vmRegOp(inst.b), function.intOp(inst.g)); break; // These instructions don't have an effect on register/memory state we are tracking diff --git a/Dependencies/luau/Luau/Common/include/Luau/Bytecode.h b/Dependencies/luau/Luau/Common/include/Luau/Bytecode.h index 1603d205..82185e7f 100644 --- a/Dependencies/luau/Luau/Common/include/Luau/Bytecode.h +++ b/Dependencies/luau/Luau/Common/include/Luau/Bytecode.h @@ -441,7 +441,7 @@ enum LuauBytecodeTag // Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled LBC_VERSION_MIN = 3, LBC_VERSION_MAX = 6, - LBC_VERSION_TARGET = 5, + LBC_VERSION_TARGET = 6, // Type encoding version LBC_TYPE_VERSION_MIN = 1, LBC_TYPE_VERSION_MAX = 3, diff --git a/Dependencies/luau/Luau/Common/include/Luau/Common.h b/Dependencies/luau/Luau/Common/include/Luau/Common.h index 406f6553..2f4f1df8 100644 --- a/Dependencies/luau/Luau/Common/include/Luau/Common.h +++ b/Dependencies/luau/Luau/Common/include/Luau/Common.h @@ -20,6 +20,19 @@ #define LUAU_DEBUGBREAK() __builtin_trap() #endif +// LUAU_FALLTHROUGH is a C++11-compatible alternative to [[fallthrough]] for use in the VM library +#if defined(__clang__) && defined(__has_warning) +#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") +#define LUAU_FALLTHROUGH [[clang::fallthrough]] +#else +#define LUAU_FALLTHROUGH +#endif +#elif defined(__GNUC__) && __GNUC__ >= 7 +#define LUAU_FALLTHROUGH [[gnu::fallthrough]] +#else +#define LUAU_FALLTHROUGH +#endif + #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define LUAU_BIG_ENDIAN #endif diff --git a/Dependencies/luau/Luau/Common/include/Luau/ExperimentalFlags.h b/Dependencies/luau/Luau/Common/include/Luau/ExperimentalFlags.h index f83de70b..c534bcb4 100644 --- a/Dependencies/luau/Luau/Common/include/Luau/ExperimentalFlags.h +++ b/Dependencies/luau/Luau/Common/include/Luau/ExperimentalFlags.h @@ -13,7 +13,8 @@ inline bool isFlagExperimental(const char* flag) static const char* const kList[] = { "LuauInstantiateInSubtyping", // requires some fixes to lua-apps code "LuauFixIndexerSubtypingOrdering", // requires some small fixes to lua-apps code since this fixes a false negative - "StudioReportLuauAny", // takes telemetry data for usage of any types + "StudioReportLuauAny2", // takes telemetry data for usage of any types + "LuauSolverV2", // makes sure we always have at least one entry nullptr, }; diff --git a/Dependencies/luau/Luau/Compiler/src/BytecodeBuilder.cpp b/Dependencies/luau/Luau/Compiler/src/BytecodeBuilder.cpp index 2e9bd88c..685d94fa 100644 --- a/Dependencies/luau/Luau/Compiler/src/BytecodeBuilder.cpp +++ b/Dependencies/luau/Luau/Compiler/src/BytecodeBuilder.cpp @@ -7,9 +7,6 @@ #include #include -LUAU_FASTFLAG(LuauCompileUserdataInfo) -LUAU_FASTFLAG(LuauCompileFastcall3) - namespace Luau { @@ -334,8 +331,6 @@ unsigned int BytecodeBuilder::addStringTableEntry(StringRef value) const char* BytecodeBuilder::tryGetUserdataTypeName(LuauBytecodeType type) const { - LUAU_ASSERT(FFlag::LuauCompileUserdataInfo); - unsigned index = unsigned((type & ~LBC_TYPE_OPTIONAL_BIT) - LBC_TYPE_TAGGED_USERDATA_BASE); if (index < userdataTypes.size()) @@ -575,8 +570,6 @@ void BytecodeBuilder::pushUpvalTypeInfo(LuauBytecodeType type) uint32_t BytecodeBuilder::addUserdataType(const char* name) { - LUAU_ASSERT(FFlag::LuauCompileUserdataInfo); - UserdataType ty; ty.name = name; @@ -587,8 +580,6 @@ uint32_t BytecodeBuilder::addUserdataType(const char* name) void BytecodeBuilder::useUserdataType(uint32_t index) { - LUAU_ASSERT(FFlag::LuauCompileUserdataInfo); - userdataTypes[index].used = true; } @@ -673,13 +664,10 @@ void BytecodeBuilder::finalize() { LUAU_ASSERT(bytecode.empty()); - if (FFlag::LuauCompileUserdataInfo) + for (auto& ty : userdataTypes) { - for (auto& ty : userdataTypes) - { - if (ty.used) - ty.nameRef = addStringTableEntry(StringRef({ty.name.c_str(), ty.name.length()})); - } + if (ty.used) + ty.nameRef = addStringTableEntry(StringRef({ty.name.c_str(), ty.name.length()})); } // preallocate space for bytecode blob @@ -705,7 +693,6 @@ void BytecodeBuilder::finalize() writeStringTable(bytecode); - if (FFlag::LuauCompileUserdataInfo) { // Write the mapping between used type name indices and their name for (uint32_t i = 0; i < uint32_t(userdataTypes.size()); i++) @@ -1232,19 +1219,12 @@ std::string BytecodeBuilder::getError(const std::string& message) uint8_t BytecodeBuilder::getVersion() { - // This function usually returns LBC_VERSION_TARGET but may sometimes return a higher number (within LBC_VERSION_MIN/MAX) under fast flags - if (FFlag::LuauCompileFastcall3) - return 6; - return LBC_VERSION_TARGET; } uint8_t BytecodeBuilder::getTypeEncodingVersion() { - if (FFlag::LuauCompileUserdataInfo) - return LBC_TYPE_VERSION_TARGET; - - return 2; + return LBC_TYPE_VERSION_TARGET; } #ifdef LUAU_ASSERTENABLED @@ -1617,8 +1597,6 @@ void BytecodeBuilder::validateInstructions() const break; case LOP_FASTCALL3: - LUAU_ASSERT(FFlag::LuauCompileFastcall3); - VREG(LUAU_INSN_B(insn)); VJUMP(LUAU_INSN_C(insn)); LUAU_ASSERT(LUAU_INSN_OP(insns[i + 1 + LUAU_INSN_C(insn)]) == LOP_CALL); @@ -2240,8 +2218,6 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, break; case LOP_FASTCALL3: - LUAU_ASSERT(FFlag::LuauCompileFastcall3); - formatAppend(result, "FASTCALL3 %d R%d R%d R%d L%d\n", LUAU_INSN_A(insn), LUAU_INSN_B(insn), *code & 0xff, (*code >> 8) & 0xff, targetLabel); code++; break; @@ -2373,74 +2349,38 @@ std::string BytecodeBuilder::dumpCurrentFunction(std::vector& dumpinstoffs) { const std::string& typeinfo = functions.back().typeinfo; - if (FFlag::LuauCompileUserdataInfo) + // Arguments start from third byte in function typeinfo string + for (uint8_t i = 2; i < typeinfo.size(); ++i) { - // Arguments start from third byte in function typeinfo string - for (uint8_t i = 2; i < typeinfo.size(); ++i) - { - uint8_t et = typeinfo[i]; - - const char* userdata = tryGetUserdataTypeName(LuauBytecodeType(et)); - const char* name = userdata ? userdata : getBaseTypeString(et); - const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - - formatAppend(result, "R%d: %s%s [argument]\n", i - 2, name, optional); - } - - for (size_t i = 0; i < typedUpvals.size(); ++i) - { - const TypedUpval& l = typedUpvals[i]; - - const char* userdata = tryGetUserdataTypeName(l.type); - const char* name = userdata ? userdata : getBaseTypeString(l.type); - const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - - formatAppend(result, "U%d: %s%s\n", int(i), name, optional); - } - - for (size_t i = 0; i < typedLocals.size(); ++i) - { - const TypedLocal& l = typedLocals[i]; + uint8_t et = typeinfo[i]; - const char* userdata = tryGetUserdataTypeName(l.type); - const char* name = userdata ? userdata : getBaseTypeString(l.type); - const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* userdata = tryGetUserdataTypeName(LuauBytecodeType(et)); + const char* name = userdata ? userdata : getBaseTypeString(et); + const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, name, optional, l.startpc, l.endpc); - } + formatAppend(result, "R%d: %s%s [argument]\n", i - 2, name, optional); } - else - { - // Arguments start from third byte in function typeinfo string - for (uint8_t i = 2; i < typeinfo.size(); ++i) - { - uint8_t et = typeinfo[i]; - - const char* base = getBaseTypeString(et); - const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "R%d: %s%s [argument]\n", i - 2, base, optional); - } - - for (size_t i = 0; i < typedUpvals.size(); ++i) - { - const TypedUpval& l = typedUpvals[i]; + for (size_t i = 0; i < typedUpvals.size(); ++i) + { + const TypedUpval& l = typedUpvals[i]; - const char* base = getBaseTypeString(l.type); - const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* userdata = tryGetUserdataTypeName(l.type); + const char* name = userdata ? userdata : getBaseTypeString(l.type); + const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "U%d: %s%s\n", int(i), base, optional); - } + formatAppend(result, "U%d: %s%s\n", int(i), name, optional); + } - for (size_t i = 0; i < typedLocals.size(); ++i) - { - const TypedLocal& l = typedLocals[i]; + for (size_t i = 0; i < typedLocals.size(); ++i) + { + const TypedLocal& l = typedLocals[i]; - const char* base = getBaseTypeString(l.type); - const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* userdata = tryGetUserdataTypeName(l.type); + const char* name = userdata ? userdata : getBaseTypeString(l.type); + const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, base, optional, l.startpc, l.endpc); - } + formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, name, optional, l.startpc, l.endpc); } } diff --git a/Dependencies/luau/Luau/Compiler/src/Compiler.cpp b/Dependencies/luau/Luau/Compiler/src/Compiler.cpp index 93c31252..7ed70d14 100644 --- a/Dependencies/luau/Luau/Compiler/src/Compiler.cpp +++ b/Dependencies/luau/Luau/Compiler/src/Compiler.cpp @@ -26,9 +26,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25) LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300) LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) -LUAU_FASTFLAGVARIABLE(LuauCompileUserdataInfo, false) -LUAU_FASTFLAGVARIABLE(LuauCompileFastcall3, false) - LUAU_FASTFLAG(LuauNativeAttribute) namespace Luau @@ -469,33 +466,19 @@ struct Compiler { LUAU_ASSERT(!expr->self); LUAU_ASSERT(expr->args.size >= 1); - - if (FFlag::LuauCompileFastcall3) - LUAU_ASSERT(expr->args.size <= 3); - else - LUAU_ASSERT(expr->args.size <= 2 || (bfid == LBF_BIT32_EXTRACTK && expr->args.size == 3)); - + LUAU_ASSERT(expr->args.size <= 3); LUAU_ASSERT(bfid == LBF_BIT32_EXTRACTK ? bfK >= 0 : bfK < 0); LuauOpcode opc = LOP_NOP; - if (FFlag::LuauCompileFastcall3) - { - if (expr->args.size == 1) - opc = LOP_FASTCALL1; - else if (bfK >= 0 || (expr->args.size == 2 && isConstant(expr->args.data[1]))) - opc = LOP_FASTCALL2K; - else if (expr->args.size == 2) - opc = LOP_FASTCALL2; - else - opc = LOP_FASTCALL3; - } + if (expr->args.size == 1) + opc = LOP_FASTCALL1; + else if (bfK >= 0 || (expr->args.size == 2 && isConstant(expr->args.data[1]))) + opc = LOP_FASTCALL2K; + else if (expr->args.size == 2) + opc = LOP_FASTCALL2; else - { - opc = expr->args.size == 1 ? LOP_FASTCALL1 - : (bfK >= 0 || (expr->args.size == 2 && isConstant(expr->args.data[1]))) ? LOP_FASTCALL2K - : LOP_FASTCALL2; - } + opc = LOP_FASTCALL3; uint32_t args[3] = {}; @@ -524,7 +507,7 @@ struct Compiler bytecode.emitABC(opc, uint8_t(bfid), uint8_t(args[0]), 0); - if (FFlag::LuauCompileFastcall3 && opc == LOP_FASTCALL3) + if (opc == LOP_FASTCALL3) { LUAU_ASSERT(bfK < 0); bytecode.emitAux(args[1] | (args[2] << 8)); @@ -886,7 +869,7 @@ struct Compiler unsigned maxFastcallArgs = 2; // Fastcall with 3 arguments is only used if it can help save one or more move instructions - if (FFlag::LuauCompileFastcall3 && bfid >= 0 && expr->args.size == 3) + if (bfid >= 0 && expr->args.size == 3) { for (size_t i = 0; i < expr->args.size; ++i) { @@ -899,7 +882,7 @@ struct Compiler } // Optimization: for 1/2/3 argument fast calls use specialized opcodes - if (bfid >= 0 && expr->args.size >= 1 && expr->args.size <= (FFlag::LuauCompileFastcall3 ? maxFastcallArgs : 2u)) + if (bfid >= 0 && expr->args.size >= 1 && expr->args.size <= maxFastcallArgs) { if (!isExprMultRet(expr->args.data[expr->args.size - 1])) { @@ -4231,20 +4214,17 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c predictTableShapes(compiler.tableShapes, root); } - if (FFlag::LuauCompileUserdataInfo) + if (const char* const* ptr = options.userdataTypes) { - if (const char* const* ptr = options.userdataTypes) + for (; *ptr; ++ptr) { - for (; *ptr; ++ptr) - { - // Type will only resolve to an AstName if it is actually mentioned in the source - if (AstName name = names.get(*ptr); name.value) - compiler.userdataTypes[name] = bytecode.addUserdataType(name.value); - } - - if (uintptr_t(ptr - options.userdataTypes) > (LBC_TYPE_TAGGED_USERDATA_END - LBC_TYPE_TAGGED_USERDATA_BASE)) - CompileError::raise(root->location, "Exceeded userdata type limit in the compilation options"); + // Type will only resolve to an AstName if it is actually mentioned in the source + if (AstName name = names.get(*ptr); name.value) + compiler.userdataTypes[name] = bytecode.addUserdataType(name.value); } + + if (uintptr_t(ptr - options.userdataTypes) > (LBC_TYPE_TAGGED_USERDATA_END - LBC_TYPE_TAGGED_USERDATA_BASE)) + CompileError::raise(root->location, "Exceeded userdata type limit in the compilation options"); } // computes type information for all functions based on type annotations diff --git a/Dependencies/luau/Luau/Compiler/src/Types.cpp b/Dependencies/luau/Luau/Compiler/src/Types.cpp index 0e7a3242..18dc248f 100644 --- a/Dependencies/luau/Luau/Compiler/src/Types.cpp +++ b/Dependencies/luau/Luau/Compiler/src/Types.cpp @@ -3,8 +3,6 @@ #include "Luau/BytecodeBuilder.h" -LUAU_FASTFLAG(LuauCompileUserdataInfo) - namespace Luau { @@ -70,13 +68,10 @@ static LuauBytecodeType getType( if (LuauBytecodeType prim = getPrimitiveType(ref->name); prim != LBC_TYPE_INVALID) return prim; - if (FFlag::LuauCompileUserdataInfo) + if (const uint8_t* userdataIndex = userdataTypes.find(ref->name)) { - if (const uint8_t* userdataIndex = userdataTypes.find(ref->name)) - { - bytecode.useUserdataType(*userdataIndex); - return LuauBytecodeType(LBC_TYPE_TAGGED_USERDATA_BASE + *userdataIndex); - } + bytecode.useUserdataType(*userdataIndex); + return LuauBytecodeType(LBC_TYPE_TAGGED_USERDATA_BASE + *userdataIndex); } // not primitive or alias or generic => host-provided, we assume userdata for now diff --git a/Dependencies/luau/Luau/EqSat/include/Luau/EGraph.h b/Dependencies/luau/Luau/EqSat/include/Luau/EGraph.h index 8344b0e5..480aa07d 100644 --- a/Dependencies/luau/Luau/EqSat/include/Luau/EGraph.h +++ b/Dependencies/luau/Luau/EqSat/include/Luau/EGraph.h @@ -5,7 +5,6 @@ #include "Luau/Id.h" #include "Luau/Language.h" #include "Luau/UnionFind.h" -#include "Luau/VecDeque.h" #include #include @@ -145,7 +144,7 @@ struct EGraph final /// The hashcons 𝐻 is a map from e-nodes to e-class ids. std::unordered_map hashcons; - VecDeque> worklist; + std::vector> worklist; private: void canonicalize(L& enode) @@ -183,7 +182,7 @@ struct EGraph final for (Id operand : enode.operands()) get(operand).parents.push_back({enode, id}); - worklist.push_back({enode, id}); + worklist.emplace_back(enode, id); hashcons.insert_or_assign(enode, id); return id; diff --git a/Dependencies/luau/Luau/EqSat/include/Luau/Language.h b/Dependencies/luau/Luau/EqSat/include/Luau/Language.h index 4aeb6c32..8855d851 100644 --- a/Dependencies/luau/Luau/EqSat/include/Luau/Language.h +++ b/Dependencies/luau/Luau/EqSat/include/Luau/Language.h @@ -7,7 +7,6 @@ #include "Luau/Variant.h" #include -#include #include #include @@ -233,12 +232,6 @@ struct Language final { } - Language(const Language&) noexcept = default; - Language& operator=(const Language&) noexcept = default; - - Language(Language&&) noexcept = default; - Language& operator=(Language&&) noexcept = default; - int index() const noexcept { return v.index(); diff --git a/Dependencies/luau/Luau/EqSat/include/Luau/LanguageHash.h b/Dependencies/luau/Luau/EqSat/include/Luau/LanguageHash.h index 7226132c..506f352b 100644 --- a/Dependencies/luau/Luau/EqSat/include/Luau/LanguageHash.h +++ b/Dependencies/luau/Luau/EqSat/include/Luau/LanguageHash.h @@ -3,6 +3,7 @@ #include #include +#include namespace Luau::EqSat { diff --git a/Dependencies/luau/Luau/EqSat/src/UnionFind.cpp b/Dependencies/luau/Luau/EqSat/src/UnionFind.cpp index 3c4825be..619c3f47 100644 --- a/Dependencies/luau/Luau/EqSat/src/UnionFind.cpp +++ b/Dependencies/luau/Luau/EqSat/src/UnionFind.cpp @@ -11,6 +11,7 @@ Id UnionFind::makeSet() Id id{parents.size()}; parents.push_back(id); ranks.push_back(0); + return id; } @@ -32,6 +33,7 @@ Id UnionFind::find(Id id) parents[size_t(id)] = set; id = parent; } + return set; } @@ -47,6 +49,7 @@ void UnionFind::merge(Id a, Id b) std::swap(aSet, bSet); parents[size_t(bSet)] = aSet; + if (ranks[size_t(aSet)] == ranks[size_t(bSet)]) ranks[size_t(aSet)]++; } diff --git a/Dependencies/luau/Luau/Makefile b/Dependencies/luau/Luau/Makefile index b86ec2a6..3e6b85ad 100644 --- a/Dependencies/luau/Luau/Makefile +++ b/Dependencies/luau/Luau/Makefile @@ -181,8 +181,7 @@ coverage: $(TESTS_TARGET) $(COMPILE_CLI_TARGET) mv default.profraw tests.profraw $(TESTS_TARGET) --fflags=true mv default.profraw tests-flags.profraw - # new solver is expected to fail tests at the moment, remove '!' once tests are fixed and this starts to fail - ! $(TESTS_TARGET) --fflags=true,DebugLuauDeferredConstraintResolution=true + $(TESTS_TARGET) --fflags=true,DebugLuauDeferredConstraintResolution=true mv default.profraw tests-dcr.profraw $(TESTS_TARGET) -ts=Conformance --codegen mv default.profraw codegen.profraw diff --git a/Dependencies/luau/Luau/VM/src/lstrlib.cpp b/Dependencies/luau/Luau/VM/src/lstrlib.cpp index 85669e97..b5a4bd13 100644 --- a/Dependencies/luau/Luau/VM/src/lstrlib.cpp +++ b/Dependencies/luau/Luau/VM/src/lstrlib.cpp @@ -552,9 +552,9 @@ static const char* match(MatchState* ms, const char* s, const char* p) } break; } - case '+': // 1 or more repetitions - s++; // 1 match already done - // go through + case '+': // 1 or more repetitions + s++; // 1 match already done + LUAU_FALLTHROUGH; // go through case '*': // 0 or more repetitions s = max_expand(ms, s, p, ep); break; @@ -1480,7 +1480,8 @@ static int str_pack(lua_State* L) break; } case Kpadding: - luaL_addchar(&b, LUAL_PACKPADBYTE); // FALLTHROUGH + luaL_addchar(&b, LUAL_PACKPADBYTE); + LUAU_FALLTHROUGH; case Kpaddalign: case Knop: arg--; // undo increment diff --git a/Dependencies/luau/Luau/VM/src/ltable.cpp b/Dependencies/luau/Luau/VM/src/ltable.cpp index c963ac8d..dafb2b3f 100644 --- a/Dependencies/luau/Luau/VM/src/ltable.cpp +++ b/Dependencies/luau/Luau/VM/src/ltable.cpp @@ -659,7 +659,7 @@ const TValue* luaH_get(Table* t, const TValue* key) luai_num2int(k, n); if (luai_numeq(cast_num(k), nvalue(key))) // index is int? return luaH_getnum(t, k); // use specialized version - // else go through + LUAU_FALLTHROUGH; // else go through } default: { diff --git a/Dependencies/luau/Luau/fuzz/proto.cpp b/Dependencies/luau/Luau/fuzz/proto.cpp index 9114f824..9c2ab35c 100644 --- a/Dependencies/luau/Luau/fuzz/proto.cpp +++ b/Dependencies/luau/Luau/fuzz/proto.cpp @@ -56,7 +56,7 @@ LUAU_FASTINT(LuauTypeInferIterationLimit) LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauAbortingChecks) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) std::chrono::milliseconds kInterruptTimeout(10); std::chrono::time_point interruptDeadline; @@ -247,7 +247,7 @@ DEFINE_PROTO_FUZZER(const luau::ModuleSet& message) FFlag::DebugLuauFreezeArena.value = true; FFlag::DebugLuauAbortingChecks.value = true; - FFlag::DebugLuauDeferredConstraintResolution.value = kFuzzUseNewSolver; + FFlag::LuauSolverV2.value = kFuzzUseNewSolver; std::vector sources = protoprint(message, kFuzzTypes); diff --git a/Dependencies/luau/Luau/tests/AnyTypeSummary.test.cpp b/Dependencies/luau/Luau/tests/AnyTypeSummary.test.cpp index aed7b7e5..5c3b4aa3 100644 --- a/Dependencies/luau/Luau/tests/AnyTypeSummary.test.cpp +++ b/Dependencies/luau/Luau/tests/AnyTypeSummary.test.cpp @@ -5,6 +5,7 @@ #include "Fixture.h" +#include "ScopedFlags.h" #include "doctest.h" #include @@ -13,15 +14,14 @@ using namespace Luau; using Pattern = AnyTypeSummary::Pattern; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) -LUAU_FASTFLAG(DebugLuauFreezeArena); -LUAU_FASTFLAG(DebugLuauMagicTypes); +LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(DebugLuauFreezeArena) +LUAU_FASTFLAG(DebugLuauMagicTypes) +LUAU_FASTFLAG(StudioReportLuauAny2) -LUAU_FASTFLAG(StudioReportLuauAny); struct ATSFixture : BuiltinsFixture { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; ATSFixture() { @@ -34,6 +34,11 @@ TEST_SUITE_BEGIN("AnyTypeSummaryTest"); TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( type A = (number, string) -> ...any )"; @@ -43,16 +48,18 @@ type A = (number, string) -> ...any ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); - LUAU_ASSERT(module->ats.typeInfo[0].node == "type A = (number, string)->( ...any)"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); + LUAU_ASSERT(module->ats.typeInfo[0].node == "type A = (number, string)->( ...any)"); } TEST_CASE_FIXTURE(ATSFixture, "export_alias") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( export type t8 = t0 &((true | any)->('')) )"; @@ -62,16 +69,18 @@ export type t8 = t0 &((true | any)->('')) ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); - LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8 = t0 &((true | any)->(''))"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); + LUAU_ASSERT(module->ats.typeInfo[0].node == "export type t8 = t0 &((true | any)->(''))"); } TEST_CASE_FIXTURE(ATSFixture, "typepacks") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local function fallible(t: number): ...any if t > 0 then @@ -86,16 +95,18 @@ end ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 3); - LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::TypePk); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local function fallible(t: number): ...any\n if t > 0 then\n return true, t\n end\n return false, 'must be positive'\nend"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 3); + LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::TypePk); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local function fallible(t: number): ...any\n if t > 0 then\n return true, t\n end\n return false, 'must be positive'\nend"); } TEST_CASE_FIXTURE(ATSFixture, "typepacks_no_ret") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( -- TODO: if partially typed, we'd want to know too local function fallible(t: number) @@ -111,14 +122,16 @@ end ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 0); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 0); } TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any_gen_table") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( type Pair = {first: T, second: any} )"; @@ -128,16 +141,18 @@ type Pair = {first: T, second: any} ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); - LUAU_ASSERT(module->ats.typeInfo[0].node == "type Pair = {first: T, second: any}"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); + LUAU_ASSERT(module->ats.typeInfo[0].node == "type Pair = {first: T, second: any}"); } TEST_CASE_FIXTURE(ATSFixture, "assign_uneq") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/B"] = R"( local function greetings(name: string) return "Hello, " .. name, nil @@ -152,14 +167,16 @@ local x, y, z = greetings("Dibri") -- mismatch LUAU_REQUIRE_ERROR_COUNT(1, result1); ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/B"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 0); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 0); } TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any_gen") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( -- type Pair = (boolean, string, ...any) -> {T} -- type aliases with generics/pack do not seem to be processed? type Pair = (boolean, T) -> ...any @@ -170,16 +187,18 @@ type Pair = (boolean, T) -> ...any ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); - LUAU_ASSERT(module->ats.typeInfo[0].node == "type Pair = (boolean, T)->( ...any)"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); + LUAU_ASSERT(module->ats.typeInfo[0].node == "type Pair = (boolean, T)->( ...any)"); } TEST_CASE_FIXTURE(ATSFixture, "typeof_any_in_func") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local function f() local a: any = 1 @@ -192,16 +211,18 @@ TEST_CASE_FIXTURE(ATSFixture, "typeof_any_in_func") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 2); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f()\n local a: any = 1\n local b: typeof(a) = 1\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 2); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f()\n local a: any = 1\n local b: typeof(a) = 1\n end"); } TEST_CASE_FIXTURE(ATSFixture, "generic_types") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local function foo(a: (...A) -> any, ...: A) return a(...) @@ -220,16 +241,18 @@ foo(addNumbers) ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 3); - LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::FuncApp); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local function foo(a: (...A)->( any),...: A)\n return a(...)\nend"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 3); + LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::FuncApp); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local function foo(a: (...A)->( any),...: A)\n return a(...)\nend"); } TEST_CASE_FIXTURE(ATSFixture, "no_annot") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local character = script.Parent )"; @@ -239,14 +262,16 @@ local character = script.Parent ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 0); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 0); } TEST_CASE_FIXTURE(ATSFixture, "if_any") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( function f(x: any) if not x then @@ -266,22 +291,24 @@ end ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); - LUAU_ASSERT( - module->ats.typeInfo[0].node == "function f(x: any)\nif not x then\nx = {\n y = math.random(0, 2^31-1),\n left = nil,\n right = " - "nil\n}\nelse\n local expected = x * 5\nend\nend" - ); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); + LUAU_ASSERT( + module->ats.typeInfo[0].node == "function f(x: any)\nif not x then\nx = {\n y = math.random(0, 2^31-1),\n left = nil,\n right = " + "nil\n}\nelse\n local expected = x * 5\nend\nend" + ); } TEST_CASE_FIXTURE(ATSFixture, "variadic_any") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local function f(): (number, ...any) - return 1, 5 + return 1, 5 --catching this end local x, y, z = f() -- not catching this any because no annot @@ -292,16 +319,18 @@ TEST_CASE_FIXTURE(ATSFixture, "variadic_any") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncRet); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(): (number, ...any)\n return 1, 5\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 2); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncRet); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(): (number, ...any)\n return 1, 5\n end"); } TEST_CASE_FIXTURE(ATSFixture, "type_alias_intersection") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( type XCoord = {x: number} type YCoord = {y: any} @@ -314,16 +343,18 @@ TEST_CASE_FIXTURE(ATSFixture, "type_alias_intersection") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 3); - LUAU_ASSERT(module->ats.typeInfo[2].code == Pattern::VarAnnot); - LUAU_ASSERT(module->ats.typeInfo[2].node == "local vec2: Vector2 = {x = 1, y = 2}"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 3); + LUAU_ASSERT(module->ats.typeInfo[2].code == Pattern::VarAnnot); + LUAU_ASSERT(module->ats.typeInfo[2].node == "local vec2: Vector2 = {x = 1, y = 2}"); } TEST_CASE_FIXTURE(ATSFixture, "var_func_arg") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local function f(...: any) end @@ -340,16 +371,18 @@ TEST_CASE_FIXTURE(ATSFixture, "var_func_arg") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 4); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAny); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(...: any)\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 4); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAny); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(...: any)\n end"); } TEST_CASE_FIXTURE(ATSFixture, "var_func_apps") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local function f(...: any) end @@ -362,17 +395,19 @@ TEST_CASE_FIXTURE(ATSFixture, "var_func_apps") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 3); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAny); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(...: any)\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 3); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAny); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local function f(...: any)\n end"); } TEST_CASE_FIXTURE(ATSFixture, "CannotExtendTable") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local CAR_COLLISION_GROUP = "Car" @@ -390,14 +425,16 @@ end ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 0); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 0); } TEST_CASE_FIXTURE(ATSFixture, "unknown_symbol") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local function manageRace(raceContainer: Model) RaceManager.new(raceContainer) @@ -410,16 +447,18 @@ end ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 2); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local function manageRace(raceContainer: Model)\n RaceManager.new(raceContainer)\nend"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 2); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local function manageRace(raceContainer: Model)\n RaceManager.new(raceContainer)\nend"); } TEST_CASE_FIXTURE(ATSFixture, "racing_3_short") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local CollectionService = game:GetService("CollectionService") @@ -449,16 +488,18 @@ initialize() ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 5); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local function manageRace(raceContainer: Model)\n RaceManager.new(raceContainer)\nend"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 5); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local function manageRace(raceContainer: Model)\n RaceManager.new(raceContainer)\nend"); } TEST_CASE_FIXTURE(ATSFixture, "racing_collision_2") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local PhysicsService = game:GetService("PhysicsService") local ReplicatedStorage = game:GetService("ReplicatedStorage") @@ -524,22 +565,24 @@ initialize() ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 11); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); - LUAU_ASSERT( - module->ats.typeInfo[0].node == - "local function onCharacterAdded(character: Model)\n\n character.DescendantAdded:Connect(function(descendant)\n if " - "descendant:IsA('BasePart')then\n descendant.CollisionGroup = CHARACTER_COLLISION_GROUP\n end\n end)\n\n\n for _, descendant in " - "character:GetDescendants()do\n if descendant:IsA('BasePart')then\n descendant.CollisionGroup = CHARACTER_COLLISION_GROUP\n end\n " - "end\nend" - ); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 11); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); + LUAU_ASSERT( + module->ats.typeInfo[0].node == + "local function onCharacterAdded(character: Model)\n\n character.DescendantAdded:Connect(function(descendant)\n if " + "descendant:IsA('BasePart')then\n descendant.CollisionGroup = CHARACTER_COLLISION_GROUP\n end\n end)\n\n\n for _, descendant in " + "character:GetDescendants()do\n if descendant:IsA('BasePart')then\n descendant.CollisionGroup = CHARACTER_COLLISION_GROUP\n end\n " + "end\nend" + ); } TEST_CASE_FIXTURE(ATSFixture, "racing_spawning_1") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local CollectionService = game:GetService("CollectionService") local Players = game:GetService("Players") @@ -593,23 +636,25 @@ initialize() ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 7); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); - LUAU_ASSERT( - module->ats.typeInfo[0].node == - "local function setupKiosk(kiosk: Model)\n local spawnLocation = kiosk:FindFirstChild('SpawnLocation')\n assert(spawnLocation, " - "`{kiosk:GetFullName()} has no SpawnLocation part`)\n local promptPart = kiosk:FindFirstChild('Prompt')\n assert(promptPart, " - "`{kiosk:GetFullName()} has no Prompt part`)\n\n\n spawnLocation.Transparency = 1\n\n\n local spawnPrompt = " - "spawnPromptTemplate:Clone()\n spawnPrompt.Parent = promptPart\n\n spawnPrompt.Triggered:Connect(function(player: Player)\n\n " - "destroyPlayerCars(player)\n\n spawnCar(spawnLocation.CFrame, player)\n end)\nend" - ); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 7); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); + LUAU_ASSERT( + module->ats.typeInfo[0].node == + "local function setupKiosk(kiosk: Model)\n local spawnLocation = kiosk:FindFirstChild('SpawnLocation')\n assert(spawnLocation, " + "`{kiosk:GetFullName()} has no SpawnLocation part`)\n local promptPart = kiosk:FindFirstChild('Prompt')\n assert(promptPart, " + "`{kiosk:GetFullName()} has no Prompt part`)\n\n\n spawnLocation.Transparency = 1\n\n\n local spawnPrompt = " + "spawnPromptTemplate:Clone()\n spawnPrompt.Parent = promptPart\n\n spawnPrompt.Triggered:Connect(function(player: Player)\n\n " + "destroyPlayerCars(player)\n\n spawnCar(spawnLocation.CFrame, player)\n end)\nend" + ); } TEST_CASE_FIXTURE(ATSFixture, "mutually_recursive_generic") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( --!strict type T = { f: a, g: U } @@ -625,14 +670,16 @@ TEST_CASE_FIXTURE(ATSFixture, "mutually_recursive_generic") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 0); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 0); } TEST_CASE_FIXTURE(ATSFixture, "explicit_pack") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( type Foo = (T...) -> () -- also want to see how these are used. type Bar = Foo<(number, any)> @@ -643,16 +690,39 @@ type Bar = Foo<(number, any)> ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); - LUAU_ASSERT(module->ats.typeInfo[0].node == "type Bar = Foo<(number, any)>"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); + LUAU_ASSERT(module->ats.typeInfo[0].node == "type Bar = Foo<(number, any)>"); +} + +TEST_CASE_FIXTURE(ATSFixture, "local_val") +{ + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + + fileResolver.source["game/Gui/Modules/A"] = R"( +local a, b, c = 1 :: any +)"; + + CheckResult result1 = frontend.check("game/Gui/Modules/A"); + LUAU_REQUIRE_NO_ERRORS(result1); + + ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); + + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Casts); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local a, b, c = 1 :: any"); } TEST_CASE_FIXTURE(ATSFixture, "var_any_local") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local x = 2 local x: any = 2, 3 @@ -665,16 +735,18 @@ local x: number, y: any, z, h: nil = 1, nil ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 3); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: any = 2, 3"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 3); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: any = 2, 3"); } TEST_CASE_FIXTURE(ATSFixture, "table_uses_any") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local x: any = 0 local y: number @@ -686,16 +758,18 @@ TEST_CASE_FIXTURE(ATSFixture, "table_uses_any") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: any = 0"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: any = 0"); } TEST_CASE_FIXTURE(ATSFixture, "typeof_any") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local x: any = 0 function some1(x: typeof(x)) @@ -707,16 +781,18 @@ TEST_CASE_FIXTURE(ATSFixture, "typeof_any") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 2); - LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::FuncArg); - LUAU_ASSERT(module->ats.typeInfo[0].node == "function some1(x: typeof(x))\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 2); + LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::FuncArg); + LUAU_ASSERT(module->ats.typeInfo[0].node == "function some1(x: typeof(x))\n end"); } TEST_CASE_FIXTURE(ATSFixture, "table_type_assigned") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( local x: { x: any?} = {x = 1} local z: { x : any, y : number? } -- not catching this @@ -729,16 +805,18 @@ TEST_CASE_FIXTURE(ATSFixture, "table_type_assigned") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 2); - LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::Assign); - LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: { x: any?} = {x = 1}"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 2); + LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::Assign); + LUAU_ASSERT(module->ats.typeInfo[0].node == "local x: { x: any?} = {x = 1}"); } TEST_CASE_FIXTURE(ATSFixture, "simple_func_wo_ret") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( function some(x: any) end @@ -749,16 +827,18 @@ TEST_CASE_FIXTURE(ATSFixture, "simple_func_wo_ret") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); - LUAU_ASSERT(module->ats.typeInfo[0].node == "function some(x: any)\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); + LUAU_ASSERT(module->ats.typeInfo[0].node == "function some(x: any)\n end"); } TEST_CASE_FIXTURE(ATSFixture, "simple_func_w_ret") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( function other(y: number): any return "gotcha!" @@ -770,16 +850,18 @@ TEST_CASE_FIXTURE(ATSFixture, "simple_func_w_ret") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncRet); - LUAU_ASSERT(module->ats.typeInfo[0].node == "function other(y: number): any\n return 'gotcha!'\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncRet); + LUAU_ASSERT(module->ats.typeInfo[0].node == "function other(y: number): any\n return 'gotcha!'\n end"); } TEST_CASE_FIXTURE(ATSFixture, "nested_local") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( function cool(y: number): number local g: any = "gratatataaa" @@ -792,16 +874,18 @@ TEST_CASE_FIXTURE(ATSFixture, "nested_local") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot); - LUAU_ASSERT(module->ats.typeInfo[0].node == "function cool(y: number): number\n local g: any = 'gratatataaa'\n return y\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::VarAnnot); + LUAU_ASSERT(module->ats.typeInfo[0].node == "function cool(y: number): number\n local g: any = 'gratatataaa'\n return y\n end"); } TEST_CASE_FIXTURE(ATSFixture, "generic_func") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( function reverse(a: {T}, b: any): {T} return a @@ -813,16 +897,18 @@ TEST_CASE_FIXTURE(ATSFixture, "generic_func") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 1); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); - LUAU_ASSERT(module->ats.typeInfo[0].node == "function reverse(a: {T}, b: any): {T}\n return a\n end"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 1); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::FuncArg); + LUAU_ASSERT(module->ats.typeInfo[0].node == "function reverse(a: {T}, b: any): {T}\n return a\n end"); } TEST_CASE_FIXTURE(ATSFixture, "type_alias_any") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/Gui/Modules/A"] = R"( type Clear = any local z: Clear = "zip" @@ -833,16 +919,18 @@ TEST_CASE_FIXTURE(ATSFixture, "type_alias_any") ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 2); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); - LUAU_ASSERT(module->ats.typeInfo[0].node == "type Clear = any"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 2); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); + LUAU_ASSERT(module->ats.typeInfo[0].node == "type Clear = any"); } TEST_CASE_FIXTURE(ATSFixture, "multi_module_any") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/A"] = R"( export type MyFunction = (number, string) -> (any) )"; @@ -864,16 +952,18 @@ TEST_CASE_FIXTURE(ATSFixture, "multi_module_any") ModulePtr module = frontend.moduleResolver.getModule("game/B"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 2); - LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); - LUAU_ASSERT(module->ats.typeInfo[0].node == "type Clear = any"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 2); + LUAU_ASSERT(module->ats.typeInfo[0].code == Pattern::Alias); + LUAU_ASSERT(module->ats.typeInfo[0].node == "type Clear = any"); } TEST_CASE_FIXTURE(ATSFixture, "cast_on_cyclic_req") { + ScopedFastFlag sff[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::StudioReportLuauAny2, true}, + }; + fileResolver.source["game/A"] = R"( local a = require(script.Parent.B) -- not resolving this module export type MyFunction = (number, string) -> (any) @@ -890,12 +980,9 @@ TEST_CASE_FIXTURE(ATSFixture, "cast_on_cyclic_req") ModulePtr module = frontend.moduleResolver.getModule("game/B"); - if (FFlag::StudioReportLuauAny) - { - LUAU_ASSERT(module->ats.typeInfo.size() == 3); - LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::Alias); - LUAU_ASSERT(module->ats.typeInfo[1].node == "type Clear = any"); - } + LUAU_ASSERT(module->ats.typeInfo.size() == 3); + LUAU_ASSERT(module->ats.typeInfo[1].code == Pattern::Alias); + LUAU_ASSERT(module->ats.typeInfo[1].node == "type Clear = any"); } diff --git a/Dependencies/luau/Luau/tests/AstQuery.test.cpp b/Dependencies/luau/Luau/tests/AstQuery.test.cpp index ec32c5f1..6822ce6d 100644 --- a/Dependencies/luau/Luau/tests/AstQuery.test.cpp +++ b/Dependencies/luau/Luau/tests/AstQuery.test.cpp @@ -6,8 +6,6 @@ #include "doctest.h" #include "Fixture.h" -LUAU_FASTFLAG(LuauFixBindingForGlobalPos); - using namespace Luau; struct DocumentationSymbolFixture : BuiltinsFixture @@ -171,7 +169,7 @@ TEST_SUITE_BEGIN("AstQuery"); TEST_CASE_FIXTURE(Fixture, "last_argument_function_call_type") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; check(R"( local function foo() return 2 end @@ -353,7 +351,7 @@ TEST_CASE_FIXTURE(Fixture, "find_expr_ancestry") TEST_CASE_FIXTURE(BuiltinsFixture, "find_binding_at_position_global_start_of_file") { - ScopedFastFlag sff{FFlag::LuauFixBindingForGlobalPos, true}; + check("local x = string.char(1)"); const Position pos(0, 12); diff --git a/Dependencies/luau/Luau/tests/Autocomplete.test.cpp b/Dependencies/luau/Luau/tests/Autocomplete.test.cpp index c1b62d7a..7f020b18 100644 --- a/Dependencies/luau/Luau/tests/Autocomplete.test.cpp +++ b/Dependencies/luau/Luau/tests/Autocomplete.test.cpp @@ -110,7 +110,7 @@ struct ACFixtureImpl : BaseType ); freeze(globals.globalTypes); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { GlobalTypes& globals = this->frontend.globals; unfreeze(globals.globalTypes); @@ -1607,6 +1607,9 @@ return target(a.@1 TEST_CASE_FIXTURE(ACFixture, "type_correct_suggestion_in_table") { + if (FFlag::LuauSolverV2) // CLI-116815 Autocomplete cannot suggest keys while autocompleting inside of a table + return; + check(R"( type Foo = { a: number, b: string } local a = { one = 4, two = "hello" } @@ -2210,7 +2213,7 @@ local fp: @1= f auto ac = autocomplete('1'); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) REQUIRE_EQ("({ x: number, y: number }) -> number", toString(requireType("f"))); else REQUIRE_EQ("({| x: number, y: number |}) -> number", toString(requireType("f"))); @@ -2261,6 +2264,9 @@ local ec = e(f@5) TEST_CASE_FIXTURE(ACFixture, "type_correct_suggestion_for_overloads") { + if (FFlag::LuauSolverV2) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly + // (overloads and singletons) + return; check(R"( local target: ((number) -> string) & ((string) -> number)) @@ -2608,6 +2614,10 @@ end TEST_CASE_FIXTURE(ACFixture, "suggest_table_keys") { + if (FFlag::LuauSolverV2) // CLI-116812 AutocompleteTest.suggest_table_keys needs to populate expected types for nested + // tables without an annotation + return; + check(R"( type Test = { first: number, second: number } local t: Test = { f@1 } @@ -3091,6 +3101,10 @@ TEST_CASE_FIXTURE(ACBuiltinsFixture, "autocomplete_on_string_singletons") TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons") { + if (FFlag::LuauSolverV2) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly + // (overloads and singletons) + return; + check(R"( type tag = "cat" | "dog" local function f(a: tag) end @@ -3196,7 +3210,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key") TEST_CASE_FIXTURE(ACFixture, "string_singleton_in_if_statement") { ScopedFastFlag sff[]{ - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; check(R"( @@ -3280,7 +3294,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_singleton_in_if_statement") TEST_CASE_FIXTURE(ACFixture, "string_singleton_in_if_statement2") { // don't run this when the DCR flag isn't set - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; check(R"( @@ -3564,7 +3578,7 @@ t.@1 REQUIRE(ac.entryMap.count("m")); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(ac.entryMap["m"].wrongIndexType); else CHECK(!ac.entryMap["m"].wrongIndexType); @@ -3746,7 +3760,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback") declare function require(path: string): any )"); - GlobalTypes& globals = FFlag::DebugLuauDeferredConstraintResolution ? frontend.globals : frontend.globalsForAutocomplete; + GlobalTypes& globals = FFlag::LuauSolverV2 ? frontend.globals : frontend.globalsForAutocomplete; std::optional require = globals.globalScope->linearSearchForBinding("require"); REQUIRE(require); @@ -3773,7 +3787,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback") TEST_CASE_FIXTURE(ACFixture, "autocomplete_response_perf1" * doctest::timeout(0.5)) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; // FIXME: This test is just barely at the threshhold which makes it very flaky under the new solver // Build a function type with a large overload set @@ -3855,7 +3869,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_completion_outside_quotes") declare function require(path: string): any )"); - GlobalTypes& globals = FFlag::DebugLuauDeferredConstraintResolution ? frontend.globals : frontend.globalsForAutocomplete; + GlobalTypes& globals = FFlag::LuauSolverV2 ? frontend.globals : frontend.globalsForAutocomplete; std::optional require = globals.globalScope->linearSearchForBinding("require"); REQUIRE(require); @@ -4247,6 +4261,9 @@ foo(@1) TEST_CASE_FIXTURE(ACFixture, "anonymous_autofilled_generic_type_pack_vararg") { + // CLI-116932 - Autocomplete on a anonymous function in a function argument should not recommend a function with a generic parameter. + if (FFlag::LuauSolverV2) + return; check(R"( local function foo(a: (...A) -> number, ...: A) return a(...) @@ -4276,7 +4293,8 @@ end foo(@1) )"); - const std::optional EXPECTED_INSERT = "function(...): number end"; + const std::optional EXPECTED_INSERT = + FFlag::LuauSolverV2 ? "function(...: number): number end" : "function(...): number end"; auto ac = autocomplete('1'); diff --git a/Dependencies/luau/Luau/tests/Compiler.test.cpp b/Dependencies/luau/Luau/tests/Compiler.test.cpp index 58f52a08..48bd45d7 100644 --- a/Dependencies/luau/Luau/tests/Compiler.test.cpp +++ b/Dependencies/luau/Luau/tests/Compiler.test.cpp @@ -22,9 +22,6 @@ LUAU_FASTINT(LuauCompileLoopUnrollThreshold) LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost) LUAU_FASTINT(LuauRecursionLimit) -LUAU_FASTFLAG(LuauCompileUserdataInfo) -LUAU_FASTFLAG(LuauCompileFastcall3) - using namespace Luau; static std::string compileFunction(const char* source, uint32_t id, int optimizationLevel = 1, bool enableVectors = false) @@ -3330,8 +3327,6 @@ RETURN R0 0 TEST_CASE("DebugTypes") { - ScopedFastFlag luauCompileUserdataInfo{FFlag::LuauCompileUserdataInfo, true}; - const char* source = R"( local up: number = 2 @@ -3591,8 +3586,6 @@ RETURN R1 -1 TEST_CASE("Fastcall3") { - ScopedFastFlag luauCompileFastcall3{FFlag::LuauCompileFastcall3, true}; - CHECK_EQ( "\n" + compileFunction0(R"( local a, b, c = ... @@ -4898,8 +4891,6 @@ L0: RETURN R0 -1 TEST_CASE("VectorFastCall3") { - ScopedFastFlag luauCompileFastcall3{FFlag::LuauCompileFastcall3, true}; - const char* source = R"( local a, b, c = ... return Vector3.new(a, b, c) diff --git a/Dependencies/luau/Luau/tests/Conformance.test.cpp b/Dependencies/luau/Luau/tests/Conformance.test.cpp index 59f862cb..376caa44 100644 --- a/Dependencies/luau/Luau/tests/Conformance.test.cpp +++ b/Dependencies/luau/Luau/tests/Conformance.test.cpp @@ -35,6 +35,7 @@ LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_FASTFLAG(LuauNativeAttribute) LUAU_FASTFLAG(LuauPreserveLudataRenaming) +LUAU_FASTFLAG(LuauCodegenArmNumToVecFix) static lua_CompileOptions defaultOptions() { @@ -132,7 +133,12 @@ static int lua_vector_cross(lua_State* L) const float* a = luaL_checkvector(L, 1); const float* b = luaL_checkvector(L, 2); +#if LUA_VECTOR_SIZE == 4 + lua_pushvector(L, a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0f); +#else lua_pushvector(L, a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); +#endif + return 1; } @@ -143,15 +149,25 @@ static int lua_vector_index(lua_State* L) if (strcmp(name, "Magnitude") == 0) { +#if LUA_VECTOR_SIZE == 4 + lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3])); +#else lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])); +#endif return 1; } if (strcmp(name, "Unit") == 0) { +#if LUA_VECTOR_SIZE == 4 + float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]); + + lua_pushvector(L, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt, v[3] * invSqrt); +#else float invSqrt = 1.0f / sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); lua_pushvector(L, v[0] * invSqrt, v[1] * invSqrt, v[2] * invSqrt); +#endif return 1; } @@ -809,6 +825,8 @@ TEST_CASE("Pack") TEST_CASE("Vector") { + ScopedFastFlag luauCodegenArmNumToVecFix{FFlag::LuauCodegenArmNumToVecFix, true}; + lua_CompileOptions copts = defaultOptions(); Luau::CodeGen::CompilationOptions nativeOpts = defaultCodegenOptions(); diff --git a/Dependencies/luau/Luau/tests/ConstraintGeneratorFixture.cpp b/Dependencies/luau/Luau/tests/ConstraintGeneratorFixture.cpp index 5c16258e..7f168465 100644 --- a/Dependencies/luau/Luau/tests/ConstraintGeneratorFixture.cpp +++ b/Dependencies/luau/Luau/tests/ConstraintGeneratorFixture.cpp @@ -2,7 +2,7 @@ #include "ConstraintGeneratorFixture.h" #include "ScopedFlags.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); namespace Luau { @@ -10,7 +10,7 @@ namespace Luau ConstraintGeneratorFixture::ConstraintGeneratorFixture() : Fixture() , mainModule(new Module) - , forceTheFlag{FFlag::DebugLuauDeferredConstraintResolution, true} + , forceTheFlag{FFlag::LuauSolverV2, true} { mainModule->name = "MainModule"; mainModule->humanReadableName = "MainModule"; diff --git a/Dependencies/luau/Luau/tests/ConstraintSolver.test.cpp b/Dependencies/luau/Luau/tests/ConstraintSolver.test.cpp index 170510bd..b83fb345 100644 --- a/Dependencies/luau/Luau/tests/ConstraintSolver.test.cpp +++ b/Dependencies/luau/Luau/tests/ConstraintSolver.test.cpp @@ -4,7 +4,7 @@ #include "Fixture.h" #include "doctest.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); using namespace Luau; diff --git a/Dependencies/luau/Luau/tests/DataFlowGraph.test.cpp b/Dependencies/luau/Luau/tests/DataFlowGraph.test.cpp index 07d64e96..4ea656ee 100644 --- a/Dependencies/luau/Luau/tests/DataFlowGraph.test.cpp +++ b/Dependencies/luau/Luau/tests/DataFlowGraph.test.cpp @@ -11,12 +11,12 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); struct DataFlowGraphFixture { // Only needed to fix the operator== reflexivity of an empty Symbol. - ScopedFastFlag dcr{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag dcr{FFlag::LuauSolverV2, true}; InternalErrorReporter handle; diff --git a/Dependencies/luau/Luau/tests/Differ.test.cpp b/Dependencies/luau/Luau/tests/Differ.test.cpp index a10056a7..a2b2280b 100644 --- a/Dependencies/luau/Luau/tests/Differ.test.cpp +++ b/Dependencies/luau/Luau/tests/Differ.test.cpp @@ -15,7 +15,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) TEST_SUITE_BEGIN("Differ"); @@ -234,7 +234,7 @@ TEST_CASE_FIXTURE(DifferFixture, "right_cyclic_table_left_table_property_wrong") TEST_CASE_FIXTURE(DifferFixture, "equal_table_two_cyclic_tables_are_not_different") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function id(x: a): a @@ -625,7 +625,7 @@ TEST_CASE_FIXTURE(DifferFixture, "equal_table_cyclic_diamonds_unraveled") TEST_CASE_FIXTURE(DifferFixture, "equal_function_cyclic") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo() @@ -646,7 +646,7 @@ TEST_CASE_FIXTURE(DifferFixture, "equal_function_cyclic") TEST_CASE_FIXTURE(DifferFixture, "equal_function_table_cyclic") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo() @@ -673,7 +673,7 @@ TEST_CASE_FIXTURE(DifferFixture, "equal_function_table_cyclic") TEST_CASE_FIXTURE(DifferFixture, "function_table_self_referential_cyclic") { // Old solver does not correctly infer function typepacks - // ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + // ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo() @@ -692,7 +692,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_table_self_referential_cyclic") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) compareTypesNe( "foo", "almostFoo", @@ -726,7 +726,7 @@ TEST_CASE_FIXTURE(DifferFixture, "equal_union_cyclic") TEST_CASE_FIXTURE(DifferFixture, "equal_intersection_cyclic") { // Old solver does not correctly refine test types - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo1(x: number) @@ -804,7 +804,7 @@ TEST_CASE_FIXTURE(DifferFixture, "singleton_string") TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "negation") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -879,7 +879,7 @@ TEST_CASE_FIXTURE(DifferFixture, "union_missing") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) compareTypesNe( "foo", "almostFoo", @@ -931,7 +931,7 @@ TEST_CASE_FIXTURE(DifferFixture, "intersection_tables_missing_right") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) compareTypesNe( "foo", "almostFoo", @@ -953,7 +953,7 @@ TEST_CASE_FIXTURE(DifferFixture, "intersection_tables_missing_left") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) compareTypesNe( "foo", "almostFoo", @@ -970,7 +970,7 @@ TEST_CASE_FIXTURE(DifferFixture, "intersection_tables_missing_left") TEST_CASE_FIXTURE(DifferFixture, "equal_function") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number) @@ -988,7 +988,7 @@ TEST_CASE_FIXTURE(DifferFixture, "equal_function") TEST_CASE_FIXTURE(DifferFixture, "equal_function_inferred_ret_length") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function bar(x: number, y: string) @@ -1012,7 +1012,7 @@ TEST_CASE_FIXTURE(DifferFixture, "equal_function_inferred_ret_length") TEST_CASE_FIXTURE(DifferFixture, "equal_function_inferred_ret_length_2") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function bar(x: number, y: string) @@ -1033,7 +1033,7 @@ TEST_CASE_FIXTURE(DifferFixture, "equal_function_inferred_ret_length_2") TEST_CASE_FIXTURE(DifferFixture, "function_arg_normal") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: number, z: number) @@ -1055,7 +1055,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_arg_normal") TEST_CASE_FIXTURE(DifferFixture, "function_arg_normal_2") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: number, z: string) @@ -1077,7 +1077,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_arg_normal_2") TEST_CASE_FIXTURE(DifferFixture, "function_ret_normal") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: number, z: string) @@ -1099,7 +1099,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_ret_normal") TEST_CASE_FIXTURE(DifferFixture, "function_arg_length") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: number) @@ -1121,7 +1121,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_arg_length") TEST_CASE_FIXTURE(DifferFixture, "function_arg_length_2") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: string, z: number) @@ -1143,7 +1143,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_arg_length_2") TEST_CASE_FIXTURE(DifferFixture, "function_arg_length_none") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo() @@ -1165,7 +1165,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_arg_length_none") TEST_CASE_FIXTURE(DifferFixture, "function_arg_length_none_2") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number) @@ -1187,7 +1187,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_arg_length_none_2") TEST_CASE_FIXTURE(DifferFixture, "function_ret_length") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: number) @@ -1209,7 +1209,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_ret_length") TEST_CASE_FIXTURE(DifferFixture, "function_ret_length_2") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: string, z: number) @@ -1231,7 +1231,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_ret_length_2") TEST_CASE_FIXTURE(DifferFixture, "function_ret_length_none") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: string) @@ -1253,7 +1253,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_ret_length_none") TEST_CASE_FIXTURE(DifferFixture, "function_ret_length_none_2") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo() @@ -1275,7 +1275,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_ret_length_none_2") TEST_CASE_FIXTURE(DifferFixture, "function_variadic_arg_normal") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: string, ...: number) @@ -1297,7 +1297,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_variadic_arg_normal") TEST_CASE_FIXTURE(DifferFixture, "function_variadic_arg_missing") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: string, ...: number) @@ -1319,7 +1319,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_variadic_arg_missing") TEST_CASE_FIXTURE(DifferFixture, "function_variadic_arg_missing_2") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: number, y: string) @@ -1341,7 +1341,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_variadic_arg_missing_2") TEST_CASE_FIXTURE(DifferFixture, "function_variadic_oversaturation") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( -- allowed to be oversaturated @@ -1363,7 +1363,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_variadic_oversaturation") TEST_CASE_FIXTURE(DifferFixture, "function_variadic_oversaturation_2") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( -- must not be oversaturated @@ -1385,7 +1385,7 @@ TEST_CASE_FIXTURE(DifferFixture, "function_variadic_oversaturation_2") TEST_CASE_FIXTURE(DifferFixture, "generic") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x, y) @@ -1407,7 +1407,7 @@ TEST_CASE_FIXTURE(DifferFixture, "generic") TEST_CASE_FIXTURE(DifferFixture, "generic_one_vs_two") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: X, y: X) @@ -1429,7 +1429,7 @@ TEST_CASE_FIXTURE(DifferFixture, "generic_one_vs_two") TEST_CASE_FIXTURE(DifferFixture, "generic_three_or_three") { // Old solver does not correctly infer function typepacks - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function foo(x: X, y: X, z: Y) @@ -1473,7 +1473,7 @@ TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "equal_metatable") TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "metatable_normal") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local metaFoo = { diff --git a/Dependencies/luau/Luau/tests/Error.test.cpp b/Dependencies/luau/Luau/tests/Error.test.cpp index cc178a83..15c317fc 100644 --- a/Dependencies/luau/Luau/tests/Error.test.cpp +++ b/Dependencies/luau/Luau/tests/Error.test.cpp @@ -6,7 +6,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) TEST_SUITE_BEGIN("ErrorTests"); @@ -47,7 +47,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_function_errors") LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ( "Operator '+' could not be applied to operands of types number and string; there is no corresponding overload for __add", toString(result.errors[0]) @@ -66,7 +66,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "unary_op_type_function_errors") )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ( diff --git a/Dependencies/luau/Luau/tests/Fixture.cpp b/Dependencies/luau/Luau/tests/Fixture.cpp index 83454aab..0b0e1b7c 100644 --- a/Dependencies/luau/Luau/tests/Fixture.cpp +++ b/Dependencies/luau/Luau/tests/Fixture.cpp @@ -3,6 +3,7 @@ #include "Luau/AstQuery.h" #include "Luau/BuiltinDefinitions.h" +#include "Luau/Common.h" #include "Luau/Constraint.h" #include "Luau/ModuleResolver.h" #include "Luau/NotNull.h" @@ -22,9 +23,10 @@ static const char* mainModuleName = "MainModule"; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(DebugLuauFreezeArena); LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile) +LUAU_FASTFLAG(LuauDCRMagicFunctionTypeChecker); extern std::optional randomSeed; // tests/main.cpp @@ -150,6 +152,8 @@ const Config& TestConfigResolver::getConfig(const ModuleName& name) const Fixture::Fixture(bool freeze, bool prepareAutocomplete) : sff_DebugLuauFreezeArena(FFlag::DebugLuauFreezeArena, freeze) + // In tests, we *always* want to register the extra magic functions for typechecking `string.format`. + , sff_LuauDCRMagicFunctionTypeChecker(FFlag::LuauDCRMagicFunctionTypeChecker, true) , frontend( &fileResolver, &configResolver, @@ -204,7 +208,7 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars // if AST is available, check how lint and typecheck handle error nodes if (result.root) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { Mode mode = sourceModule->mode ? *sourceModule->mode : Mode::Strict; ModulePtr module = Luau::check( @@ -371,7 +375,7 @@ std::optional Fixture::getType(const std::string& name) if (!module->hasModuleScope()) return std::nullopt; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return linearSearchForBinding(module->getModuleScope().get(), name.c_str()); else return lookupName(module->getModuleScope(), name); diff --git a/Dependencies/luau/Luau/tests/Fixture.h b/Dependencies/luau/Luau/tests/Fixture.h index 790e6f41..f50431f3 100644 --- a/Dependencies/luau/Luau/tests/Fixture.h +++ b/Dependencies/luau/Luau/tests/Fixture.h @@ -99,6 +99,7 @@ struct Fixture TypeId requireExportedType(const ModuleName& moduleName, const std::string& name); ScopedFastFlag sff_DebugLuauFreezeArena; + ScopedFastFlag sff_LuauDCRMagicFunctionTypeChecker; TestFileResolver fileResolver; TestConfigResolver configResolver; diff --git a/Dependencies/luau/Luau/tests/Frontend.test.cpp b/Dependencies/luau/Luau/tests/Frontend.test.cpp index d7fc1956..88f91708 100644 --- a/Dependencies/luau/Luau/tests/Frontend.test.cpp +++ b/Dependencies/luau/Luau/tests/Frontend.test.cpp @@ -12,7 +12,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauFreezeArena); LUAU_FASTFLAG(DebugLuauMagicTypes); @@ -164,7 +164,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "automatically_check_dependent_scripts") auto bExports = first(bModule->returnType); REQUIRE(!!bExports); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ b_value: number }", toString(*bExports)); else CHECK_EQ("{| b_value: number |}", toString(*bExports)); @@ -311,7 +311,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "nocheck_cycle_used_by_checked") std::optional cExports = first(cModule->returnType); REQUIRE(bool(cExports)); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ a: { hello: any }, b: { hello: any } }", toString(*cExports)); else CHECK_EQ("{| a: any, b: any |}", toString(*cExports)); @@ -485,13 +485,13 @@ return {mod_b = 2} LUAU_REQUIRE_ERRORS(resultB); TypeId tyB = requireExportedType("game/B", "btype"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(tyB, opts), "{ x: number }"); else CHECK_EQ(toString(tyB, opts), "{| x: number |}"); TypeId tyA = requireExportedType("game/A", "atype"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(tyA, opts), "{ x: any }"); else CHECK_EQ(toString(tyA, opts), "{| x: any |}"); @@ -501,13 +501,13 @@ return {mod_b = 2} LUAU_REQUIRE_ERRORS(resultB); tyB = requireExportedType("game/B", "btype"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(tyB, opts), "{ x: number }"); else CHECK_EQ(toString(tyB, opts), "{| x: number |}"); tyA = requireExportedType("game/A", "atype"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(tyA, opts), "{ x: any }"); else CHECK_EQ(toString(tyA, opts), "{| x: any |}"); @@ -583,7 +583,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "recheck_if_dependent_script_is_dirty") auto bExports = first(bModule->returnType); REQUIRE(!!bExports); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ b_value: string }", toString(*bExports)); else CHECK_EQ("{| b_value: string |}", toString(*bExports)); @@ -917,7 +917,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_f // When this test fails, it is because the TypeIds needed by the error have been deallocated. // It is thus basically impossible to predict what will happen when this assert is evaluated. // It could segfault, or you could see weird type names like the empty string or - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) REQUIRE_EQ( R"(Type '{ count: string }' @@ -934,7 +934,7 @@ could not be converted into TEST_CASE_FIXTURE(FrontendFixture, "trace_requires_in_nonstrict_mode") { // The new non-strict mode is not currently expected to signal any errors here. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; fileResolver.source["Module/A"] = R"( @@ -1003,7 +1003,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "environments") CheckResult resultB = frontend.check("B"); // In the new non-strict mode, we do not currently support error reporting for unknown symbols in type positions. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(resultB); else LUAU_REQUIRE_ERROR_COUNT(1, resultB); @@ -1111,7 +1111,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "typecheck_twice_for_ast_types") TEST_CASE_FIXTURE(FrontendFixture, "imported_table_modification_2") { // This test describes non-strict mode behavior that is just not currently present in the new non-strict mode. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; frontend.options.retainFullTypeGraphs = false; @@ -1375,7 +1375,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "checked_modules_have_the_correct_mode") TEST_CASE_FIXTURE(FrontendFixture, "separate_caches_for_autocomplete") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; fileResolver.source["game/A"] = R"( --!nonstrict @@ -1405,7 +1405,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "separate_caches_for_autocomplete") TEST_CASE_FIXTURE(FrontendFixture, "no_separate_caches_with_the_new_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; fileResolver.source["game/A"] = R"( --!nonstrict diff --git a/Dependencies/luau/Luau/tests/Generalization.test.cpp b/Dependencies/luau/Luau/tests/Generalization.test.cpp index 55af4919..1388b900 100644 --- a/Dependencies/luau/Luau/tests/Generalization.test.cpp +++ b/Dependencies/luau/Luau/tests/Generalization.test.cpp @@ -14,7 +14,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) TEST_SUITE_BEGIN("Generalization"); @@ -29,7 +29,7 @@ struct GeneralizationFixture DenseHashSet generalizedTypes_{nullptr}; NotNull> generalizedTypes{&generalizedTypes_}; - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; std::pair freshType() { diff --git a/Dependencies/luau/Luau/tests/IrBuilder.test.cpp b/Dependencies/luau/Luau/tests/IrBuilder.test.cpp index 2984fde1..d02fd9f1 100644 --- a/Dependencies/luau/Luau/tests/IrBuilder.test.cpp +++ b/Dependencies/luau/Luau/tests/IrBuilder.test.cpp @@ -13,8 +13,6 @@ #include LUAU_FASTFLAG(DebugLuauAbortingChecks) -LUAU_FASTFLAG(LuauCodegenFastcall3) -LUAU_FASTFLAG(LuauCodegenMathSign) using namespace Luau::CodeGen; @@ -334,8 +332,6 @@ TEST_SUITE_BEGIN("ConstantFolding"); TEST_CASE_FIXTURE(IrBuilderFixture, "Numeric") { - ScopedFastFlag luauCodegenMathSign{FFlag::LuauCodegenMathSign, true}; - IrOp block = build.block(IrBlockKind::Internal); build.beginBlock(block); @@ -2631,8 +2627,6 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "ForgprepInvalidation") TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects1") { - ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -2656,8 +2650,6 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects1") TEST_CASE_FIXTURE(IrBuilderFixture, "FastCallEffects2") { - ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -2852,8 +2844,6 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "ImplicitFixedRegistersInVarargCall") TEST_CASE_FIXTURE(IrBuilderFixture, "ExplicitUseOfRegisterInVarargSequence") { - ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; - IrOp entry = build.block(IrBlockKind::Internal); IrOp exit = build.block(IrBlockKind::Internal); diff --git a/Dependencies/luau/Luau/tests/IrLowering.test.cpp b/Dependencies/luau/Luau/tests/IrLowering.test.cpp index 6d4aafeb..39667846 100644 --- a/Dependencies/luau/Luau/tests/IrLowering.test.cpp +++ b/Dependencies/luau/Luau/tests/IrLowering.test.cpp @@ -15,10 +15,6 @@ #include #include -LUAU_FASTFLAG(LuauCompileUserdataInfo) -LUAU_FASTFLAG(LuauCompileFastcall3) -LUAU_FASTFLAG(LuauCodegenFastcall3) - static std::string getCodegenAssembly(const char* source, bool includeIrTypes = false, int debugLevel = 1) { Luau::CodeGen::AssemblyOptions options; @@ -425,8 +421,6 @@ end TEST_CASE("DseInitialStackState2") { - ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; - CHECK_EQ( "\n" + getCodegenAssembly(R"( local function foo(a) @@ -943,10 +937,9 @@ end ); } +#if LUA_VECTOR_SIZE == 3 TEST_CASE("FastcallTypeInferThroughLocal") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -997,8 +990,6 @@ end TEST_CASE("FastcallTypeInferThroughUpvalue") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1055,6 +1046,7 @@ end )" ); } +#endif TEST_CASE("LoadAndMoveTypePropagation") { @@ -1125,10 +1117,9 @@ end ); } +#if LUA_VECTOR_SIZE == 3 TEST_CASE("ArgumentTypeRefinement") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1164,6 +1155,7 @@ end )" ); } +#endif TEST_CASE("InlineFunctionType") { @@ -1434,10 +1426,9 @@ end ); } +#if LUA_VECTOR_SIZE == 3 TEST_CASE("UnaryTypeResolve") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; - CHECK_EQ( "\n" + getCodegenHeader(R"( local function foo(a, b: vector, c) @@ -1457,6 +1448,7 @@ end )" ); } +#endif TEST_CASE("ForInManualAnnotation") { @@ -1637,37 +1629,12 @@ end ); } -// Temporary test, when we don't compile new typeinfo, but support loading it -TEST_CASE("CustomUserdataTypesTemp") -{ - // This test requires runtime component to be present - if (!Luau::CodeGen::isSupported()) - return; - - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, false}}; - - CHECK_EQ( - "\n" + getCodegenHeader(R"( -local function foo(v: vec2, x: mat3) - return v.X * x -end -)"), - R"( -; function foo(v, x) line 2 -; R0: userdata [argument 'v'] -; R1: userdata [argument 'x'] -)" - ); -} - TEST_CASE("CustomUserdataTypes") { // This test requires runtime component to be present if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenHeader(R"( local function foo(v: vec2, x: mat3) @@ -1688,8 +1655,6 @@ TEST_CASE("CustomUserdataPropertyAccess") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1727,8 +1692,6 @@ TEST_CASE("CustomUserdataPropertyAccess2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1768,8 +1731,6 @@ TEST_CASE("CustomUserdataNamecall1") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1818,8 +1779,6 @@ TEST_CASE("CustomUserdataNamecall2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1871,8 +1830,6 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1907,8 +1864,6 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1941,8 +1896,6 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow3") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1975,8 +1928,6 @@ TEST_CASE("CustomUserdataMetamethod") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( diff --git a/Dependencies/luau/Luau/tests/Linter.test.cpp b/Dependencies/luau/Luau/tests/Linter.test.cpp index 6fd70626..8647777a 100644 --- a/Dependencies/luau/Luau/tests/Linter.test.cpp +++ b/Dependencies/luau/Luau/tests/Linter.test.cpp @@ -7,7 +7,7 @@ #include "doctest.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauNativeAttribute); LUAU_FASTFLAG(LintRedundantNativeAttribute); @@ -1265,7 +1265,7 @@ _ = { TEST_CASE_FIXTURE(Fixture, "read_write_table_props") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; LintResult result = lint(R"(-- line 1 type A = {x: number} @@ -1659,7 +1659,7 @@ table.create(42, {} :: {}) TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperationsIndexer") { // CLI-116824 Linter incorrectly issues false positive when taking the length of a unannotated string function argument - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; LintResult result = lint(R"( diff --git a/Dependencies/luau/Luau/tests/Module.test.cpp b/Dependencies/luau/Luau/tests/Module.test.cpp index bcad38d3..4519ba82 100644 --- a/Dependencies/luau/Luau/tests/Module.test.cpp +++ b/Dependencies/luau/Luau/tests/Module.test.cpp @@ -11,7 +11,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(DebugLuauFreezeArena); LUAU_FASTINT(LuauTypeCloneIterationLimit); @@ -111,7 +111,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table") // not, but it's tangental to the core purpose of this test. ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; CheckResult result = check(R"( @@ -283,7 +283,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_class") TEST_CASE_FIXTURE(Fixture, "clone_free_types") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; TypeArena arena; TypeId freeTy = freshType(NotNull{&arena}, builtinTypes, nullptr); @@ -316,6 +316,9 @@ TEST_CASE_FIXTURE(Fixture, "clone_free_tables") TEST_CASE_FIXTURE(BuiltinsFixture, "clone_self_property") { + // CLI-117082 ModuleTests.clone_self_property we don't infer self correctly, instead replacing it with unknown. + if (FFlag::LuauSolverV2) + return; fileResolver.source["Module/A"] = R"( --!nonstrict local a = {} @@ -412,7 +415,7 @@ type B = A auto it = mod->exportedTypeBindings.find("A"); REQUIRE(it != mod->exportedTypeBindings.end()); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(it->second.type) == "any"); else CHECK(toString(it->second.type) == "*error-type*"); diff --git a/Dependencies/luau/Luau/tests/NonStrictTypeChecker.test.cpp b/Dependencies/luau/Luau/tests/NonStrictTypeChecker.test.cpp index b7198a9d..ffb44049 100644 --- a/Dependencies/luau/Luau/tests/NonStrictTypeChecker.test.cpp +++ b/Dependencies/luau/Luau/tests/NonStrictTypeChecker.test.cpp @@ -67,7 +67,7 @@ struct NonStrictTypeCheckerFixture : Fixture CheckResult checkNonStrict(const std::string& code) { ScopedFastFlag flags[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; LoadDefinitionFileResult res = loadDefinition(definitions); LUAU_ASSERT(res.success); @@ -77,7 +77,7 @@ struct NonStrictTypeCheckerFixture : Fixture CheckResult checkNonStrictModule(const std::string& moduleName) { ScopedFastFlag flags[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; LoadDefinitionFileResult res = loadDefinition(definitions); LUAU_ASSERT(res.success); diff --git a/Dependencies/luau/Luau/tests/NonstrictMode.test.cpp b/Dependencies/luau/Luau/tests/NonstrictMode.test.cpp index a22e1e16..3acd3909 100644 --- a/Dependencies/luau/Luau/tests/NonstrictMode.test.cpp +++ b/Dependencies/luau/Luau/tests/NonstrictMode.test.cpp @@ -16,7 +16,7 @@ TEST_SUITE_BEGIN("NonstrictModeTests"); TEST_CASE_FIXTURE(Fixture, "infer_nullary_function") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict function foo(x, y) end @@ -39,7 +39,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_nullary_function") TEST_CASE_FIXTURE(Fixture, "infer_the_maximum_number_of_values_the_function_could_return") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict function getMinCardCountForWidth(width) @@ -103,7 +103,7 @@ TEST_CASE_FIXTURE(Fixture, "inconsistent_return_types_are_ok") TEST_CASE_FIXTURE(Fixture, "locals_are_any_by_default") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict local m = 55 @@ -130,7 +130,7 @@ TEST_CASE_FIXTURE(Fixture, "parameters_having_type_any_are_optional") TEST_CASE_FIXTURE(Fixture, "local_tables_are_not_any") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict local T = {} @@ -148,7 +148,7 @@ TEST_CASE_FIXTURE(Fixture, "local_tables_are_not_any") TEST_CASE_FIXTURE(Fixture, "offer_a_hint_if_you_use_a_dot_instead_of_a_colon") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict local T = {} @@ -163,7 +163,7 @@ TEST_CASE_FIXTURE(Fixture, "offer_a_hint_if_you_use_a_dot_instead_of_a_colon") TEST_CASE_FIXTURE(Fixture, "table_props_are_any") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict local T = {} @@ -185,7 +185,7 @@ TEST_CASE_FIXTURE(Fixture, "table_props_are_any") TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict local T = { @@ -261,7 +261,7 @@ TEST_CASE_FIXTURE(Fixture, "delay_function_does_not_require_its_argument_to_retu TEST_CASE_FIXTURE(Fixture, "inconsistent_module_return_types_are_ok") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict diff --git a/Dependencies/luau/Luau/tests/Normalize.test.cpp b/Dependencies/luau/Luau/tests/Normalize.test.cpp index 407aaf4e..23b4f133 100644 --- a/Dependencies/luau/Luau/tests/Normalize.test.cpp +++ b/Dependencies/luau/Luau/tests/Normalize.test.cpp @@ -10,7 +10,7 @@ #include "Luau/Normalize.h" #include "Luau/BuiltinDefinitions.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauNormalizeNotUnknownIntersection) LUAU_FASTINT(LuauTypeInferRecursionLimit) using namespace Luau; @@ -142,7 +142,7 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "table_with_union_prop") TypeId a = requireType("a"); TypeId b = requireType("b"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(!isSubtype(a, b)); // table properties are invariant else CHECK(isSubtype(a, b)); @@ -159,7 +159,7 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "table_with_any_prop") TypeId a = requireType("a"); TypeId b = requireType("b"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(!isSubtype(a, b)); // table properties are invariant else CHECK(isSubtype(a, b)); @@ -219,7 +219,7 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "tables") TypeId c = requireType("c"); TypeId d = requireType("d"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(!isSubtype(a, b)); // table properties are invariant else CHECK(isSubtype(a, b)); @@ -231,7 +231,7 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "tables") CHECK(isSubtype(d, a)); CHECK(!isSubtype(a, d)); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(!isSubtype(d, b)); // table properties are invariant else CHECK(isSubtype(d, b)); @@ -406,7 +406,7 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "error_suppression") // We have added this as an exception - the set of inhabitants of any is exactly the set of inhabitants of unknown (since error has no // inhabitants). any = err | unknown, so under semantic subtyping, {} U unknown = unknown - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK(isSubtype(any, unk)); } @@ -415,7 +415,15 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "error_suppression") CHECK(!isSubtype(any, unk)); } - CHECK(!isSubtype(err, str)); + if (FFlag::LuauSolverV2) + { + CHECK(isSubtype(err, str)); + } + else + { + CHECK(!isSubtype(err, str)); + } + CHECK(!isSubtype(str, err)); CHECK(!isSubtype(err, unk)); @@ -446,7 +454,7 @@ struct NormalizeFixture : Fixture CheckResult result = check("type _Res = " + annotation); LUAU_REQUIRE_ERROR_COUNT(expectedErrors, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { SourceModule* sourceModule = getMainSourceModule(); REQUIRE(sourceModule); @@ -662,7 +670,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "negated_function_is_anything_except_a_funct TEST_CASE_FIXTURE(NormalizeFixture, "specific_functions_cannot_be_negated") { - CHECK(nullptr == toNormalizedType("Not<(boolean) -> boolean>", FFlag::DebugLuauDeferredConstraintResolution ? 1 : 0)); + CHECK(nullptr == toNormalizedType("Not<(boolean) -> boolean>", FFlag::LuauSolverV2 ? 1 : 0)); } TEST_CASE_FIXTURE(NormalizeFixture, "trivial_intersection_inhabited") @@ -701,6 +709,10 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function") TEST_CASE_FIXTURE(Fixture, "higher_order_function_with_annotation") { + // CLI-117088 - Inferring the type of a higher order function with an annotation sometimes doesn't fully constrain the type (there are free types + // left over). + if (FFlag::LuauSolverV2) + return; check(R"( function apply(f: (a) -> b, x) return f(x) @@ -769,7 +781,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "narrow_union_of_classes_with_intersection") TEST_CASE_FIXTURE(NormalizeFixture, "intersection_of_metatables_where_the_metatable_is_top_or_bottom") { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("{ @metatable *error-type*, { } }" == toString(normal("Mt<{}, any> & Mt<{}, err>"))); else CHECK("{ @metatable *error-type*, {| |} }" == toString(normal("Mt<{}, any> & Mt<{}, err>"))); @@ -866,7 +878,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "classes_and_never") TEST_CASE_FIXTURE(NormalizeFixture, "top_table_type") { CHECK("table" == toString(normal("{} | tbl"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("{ }" == toString(normal("{} & tbl"))); else CHECK("{| |}" == toString(normal("{} & tbl"))); @@ -875,7 +887,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "top_table_type") TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_tables") { - CHECK(nullptr == toNormalizedType("Not<{}>", FFlag::DebugLuauDeferredConstraintResolution ? 1 : 0)); + CHECK(nullptr == toNormalizedType("Not<{}>", FFlag::LuauSolverV2 ? 1 : 0)); CHECK("(boolean | buffer | class | function | number | string | thread)?" == toString(normal("Not"))); CHECK("table" == toString(normal("Not>"))); } @@ -916,7 +928,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "normalize_unknown") TEST_CASE_FIXTURE(NormalizeFixture, "read_only_props") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CHECK("{ x: string }" == toString(normal("{ read x: string } & { x: string }"), {true})); CHECK("{ x: string }" == toString(normal("{ x: string } & { read x: string }"), {true})); @@ -924,7 +936,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "read_only_props") TEST_CASE_FIXTURE(NormalizeFixture, "read_only_props_2") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CHECK(R"({ x: "hello" })" == toString(normal(R"({ x: "hello" } & { x: string })"), {true})); CHECK(R"(never)" == toString(normal(R"({ x: "hello" } & { x: "world" })"), {true})); @@ -932,7 +944,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "read_only_props_2") TEST_CASE_FIXTURE(NormalizeFixture, "read_only_props_3") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CHECK(R"({ read x: "hello" })" == toString(normal(R"({ read x: "hello" } & { read x: string })"), {true})); CHECK("never" == toString(normal(R"({ read x: "hello" } & { read x: "world" })"), {true})); @@ -997,7 +1009,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "cyclic_stack_overflow_2") TEST_CASE_FIXTURE(NormalizeFixture, "truthy_table_property_and_optional_table_with_optional_prop") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; // { x: ~(false?) } TypeId t1 = arena.addType(TableType{TableType::Props{{"x", builtinTypes->truthyType}}, std::nullopt, TypeLevel{}, TableState::Sealed}); diff --git a/Dependencies/luau/Luau/tests/Parser.test.cpp b/Dependencies/luau/Luau/tests/Parser.test.cpp index b285a318..dfcf0ded 100644 --- a/Dependencies/luau/Luau/tests/Parser.test.cpp +++ b/Dependencies/luau/Luau/tests/Parser.test.cpp @@ -15,7 +15,7 @@ LUAU_FASTFLAG(LuauLexerLookaheadRemembersBraceType); LUAU_FASTINT(LuauRecursionLimit); LUAU_FASTINT(LuauTypeLengthLimit); LUAU_FASTINT(LuauParseErrorLimit); -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr); LUAU_FASTFLAG(LuauUserDefinedTypeFunctions); @@ -1192,7 +1192,7 @@ until false TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_local_function") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; try @@ -1229,7 +1229,7 @@ end TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_failsafe_earlier") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; try @@ -2597,7 +2597,7 @@ TEST_CASE_FIXTURE(Fixture, "recovery_of_parenthesized_expressions") }; ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; checkRecovery("function foo(a, b. c) return a + b end", "function foo(a, b) return a + b end", 1); @@ -2841,7 +2841,7 @@ TEST_CASE_FIXTURE(Fixture, "AstName_comparison") TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; try diff --git a/Dependencies/luau/Luau/tests/RequireByString.test.cpp b/Dependencies/luau/Luau/tests/RequireByString.test.cpp index 7f2300a5..f76f9faf 100644 --- a/Dependencies/luau/Luau/tests/RequireByString.test.cpp +++ b/Dependencies/luau/Luau/tests/RequireByString.test.cpp @@ -436,6 +436,12 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequirePathWithParentAlias") assertOutputContainsAll({"true", "result from other_dependency"}); } +TEST_CASE_FIXTURE(ReplWithPathFixture, "RequirePathWithAliasPointingToDirectory") +{ + std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/with_config/src/directory_alias_requirer"; + runProtectedRequire(path); + assertOutputContainsAll({"true", "result from subdirectory_dependency"}); +} TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireAliasThatDoesNotExist") { diff --git a/Dependencies/luau/Luau/tests/RuntimeLimits.test.cpp b/Dependencies/luau/Luau/tests/RuntimeLimits.test.cpp index 41418aa8..b4acf138 100644 --- a/Dependencies/luau/Luau/tests/RuntimeLimits.test.cpp +++ b/Dependencies/luau/Luau/tests/RuntimeLimits.test.cpp @@ -18,7 +18,7 @@ using namespace Luau; LUAU_FASTINT(LuauTypeInferRecursionLimit) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) struct LimitFixture : BuiltinsFixture { @@ -46,7 +46,7 @@ TEST_SUITE_BEGIN("RuntimeLimits"); TEST_CASE_FIXTURE(LimitFixture, "typescript_port_of_Result_type") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; constexpr const char* src = R"LUA( diff --git a/Dependencies/luau/Luau/tests/Simplify.test.cpp b/Dependencies/luau/Luau/tests/Simplify.test.cpp index 08b2fc4f..92ac68de 100644 --- a/Dependencies/luau/Luau/tests/Simplify.test.cpp +++ b/Dependencies/luau/Luau/tests/Simplify.test.cpp @@ -8,7 +8,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_DYNAMIC_FASTINT(LuauSimplificationComplexityLimit) namespace @@ -62,7 +62,7 @@ struct SimplifyFixture : Fixture TypeId anotherChildClassTy = nullptr; TypeId unrelatedClassTy = nullptr; - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; SimplifyFixture() { diff --git a/Dependencies/luau/Luau/tests/Subtyping.test.cpp b/Dependencies/luau/Luau/tests/Subtyping.test.cpp index 01ddac0c..a59312ac 100644 --- a/Dependencies/luau/Luau/tests/Subtyping.test.cpp +++ b/Dependencies/luau/Luau/tests/Subtyping.test.cpp @@ -15,7 +15,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); using namespace Luau; @@ -67,17 +67,17 @@ struct SubtypeFixture : Fixture UnifierSharedState sharedState{&ice}; Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopePtr rootScope{new Scope(builtinTypes->emptyTypePack)}; ScopePtr moduleScope{new Scope(rootScope)}; - Subtyping subtyping = mkSubtyping(rootScope); + Subtyping subtyping = mkSubtyping(); BuiltinTypeFunctions builtinTypeFunctions{}; - Subtyping mkSubtyping(const ScopePtr& scope) + Subtyping mkSubtyping() { - return Subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&iceReporter}, NotNull{scope.get()}}; + return Subtyping{builtinTypes, NotNull{&arena}, NotNull{&normalizer}, NotNull{&iceReporter}}; } TypePackId pack(std::initializer_list tys) @@ -184,7 +184,7 @@ struct SubtypeFixture : Fixture SubtypingResult isSubtype(TypeId subTy, TypeId superTy) { - return subtyping.isSubtype(subTy, superTy); + return subtyping.isSubtype(subTy, superTy, NotNull{rootScope.get()}); } TypeId helloType = arena.addType(SingletonType{StringSingleton{"hello"}}); @@ -837,28 +837,28 @@ TEST_CASE_FIXTURE(SubtypeFixture, "{x: (T) -> ()} <: {x: (U) -> ()}") TEST_CASE_FIXTURE(SubtypeFixture, "{ x: number } <: { read x: number }") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CHECK_IS_SUBTYPE(tbl({{"x", builtinTypes->numberType}}), tbl({{"x", Property::readonly(builtinTypes->numberType)}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ x: number } <: { write x: number }") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CHECK_IS_SUBTYPE(tbl({{"x", builtinTypes->numberType}}), tbl({{"x", Property::writeonly(builtinTypes->numberType)}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ x: \"hello\" } <: { read x: string }") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CHECK_IS_SUBTYPE(tbl({{"x", helloType}}), tbl({{"x", Property::readonly(builtinTypes->stringType)}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ x: string } <: { write x: string }") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CHECK_IS_SUBTYPE(tbl({{"x", builtinTypes->stringType}}), tbl({{"x", Property::writeonly(builtinTypes->stringType)}})); } @@ -1210,7 +1210,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "(...any) -> () <: (T...) -> ()") TypeId anysToNothing = arena.addType(FunctionType{builtinTypes->anyTypePack, builtinTypes->emptyTypePack}); TypeId genericTToAnys = arena.addType(FunctionType{genericAs, builtinTypes->emptyTypePack}); - CHECK_MESSAGE(subtyping.isSubtype(anysToNothing, genericTToAnys).isSubtype, "(...any) -> () <: (T...) -> ()"); + CHECK_MESSAGE(isSubtype(anysToNothing, genericTToAnys).isSubtype, "(...any) -> () <: (T...) -> ()"); } // See https://github.com/luau-lang/luau/issues/767 @@ -1220,7 +1220,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "(...unknown) -> () <: (T...) -> ()") arena.addType(FunctionType{arena.addTypePack(VariadicTypePack{builtinTypes->unknownType}), builtinTypes->emptyTypePack}); TypeId genericTToAnys = arena.addType(FunctionType{genericAs, builtinTypes->emptyTypePack}); - CHECK_MESSAGE(subtyping.isSubtype(unknownsToNothing, genericTToAnys).isSubtype, "(...unknown) -> () <: (T...) -> ()"); + CHECK_MESSAGE(isSubtype(unknownsToNothing, genericTToAnys).isSubtype, "(...unknown) -> () <: (T...) -> ()"); } TEST_CASE_FIXTURE(SubtypeFixture, "bill") @@ -1233,8 +1233,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "bill") {{"a", builtinTypes->stringType}}, TableIndexer{builtinTypes->stringType, builtinTypes->numberType}, TypeLevel{}, nullptr, TableState::Sealed }); - CHECK(subtyping.isSubtype(a, b).isSubtype); - CHECK(subtyping.isSubtype(b, a).isSubtype); + CHECK(isSubtype(a, b).isSubtype); + CHECK(isSubtype(b, a).isSubtype); } // TEST_CASE_FIXTURE(SubtypeFixture, "({[string]: number, a: string}) -> () <: ({[string]: number, a: string}) -> ()") @@ -1256,7 +1256,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fred") TypeId a = makeTheType(); TypeId b = makeTheType(); - CHECK_MESSAGE(subtyping.isSubtype(a, b).isSubtype, "({[string]: number, a: string}) -> () <: ({[string]: number, a: string}) -> ()"); + CHECK_MESSAGE(isSubtype(a, b).isSubtype, "({[string]: number, a: string}) -> () <: ({[string]: number, a: string}) -> ()"); } /* @@ -1273,17 +1273,17 @@ TEST_CASE_FIXTURE(SubtypeFixture, "unknown <: X") TypeId genericX = arena.addType(GenericType(childScope.get(), "X")); - SubtypingResult usingGlobalScope = subtyping.isSubtype(builtinTypes->unknownType, genericX); + SubtypingResult usingGlobalScope = isSubtype(builtinTypes->unknownType, genericX); CHECK_MESSAGE(!usingGlobalScope.isSubtype, "Expected " << builtinTypes->unknownType << " unknownType, genericX); + SubtypingResult usingChildScope = childSubtyping.isSubtype(builtinTypes->unknownType, genericX, NotNull{childScope.get()}); CHECK_MESSAGE(usingChildScope.isSubtype, "Expected " << builtinTypes->unknownType << " <: " << genericX); - Subtyping grandChildSubtyping{mkSubtyping(grandChildScope)}; + Subtyping grandChildSubtyping{mkSubtyping()}; - SubtypingResult usingGrandChildScope = grandChildSubtyping.isSubtype(builtinTypes->unknownType, genericX); + SubtypingResult usingGrandChildScope = grandChildSubtyping.isSubtype(builtinTypes->unknownType, genericX, NotNull{grandChildScope.get()}); CHECK_MESSAGE(usingGrandChildScope.isSubtype, "Expected " << builtinTypes->unknownType << " <: " << genericX); } diff --git a/Dependencies/luau/Luau/tests/Symbol.test.cpp b/Dependencies/luau/Luau/tests/Symbol.test.cpp index 526dd24f..83482c03 100644 --- a/Dependencies/luau/Luau/tests/Symbol.test.cpp +++ b/Dependencies/luau/Luau/tests/Symbol.test.cpp @@ -8,7 +8,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) TEST_SUITE_BEGIN("SymbolTests"); @@ -68,7 +68,7 @@ TEST_CASE("equality_and_hashing_of_locals") TEST_CASE("equality_of_empty_symbols") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; std::string s1 = "name"; std::string s2 = "name"; diff --git a/Dependencies/luau/Luau/tests/ToDot.test.cpp b/Dependencies/luau/Luau/tests/ToDot.test.cpp index 4f0ae809..fd72579b 100644 --- a/Dependencies/luau/Luau/tests/ToDot.test.cpp +++ b/Dependencies/luau/Luau/tests/ToDot.test.cpp @@ -9,7 +9,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); struct ToDotClassFixture : Fixture { @@ -145,7 +145,7 @@ local function f(a, ...: string) return a end ToDotOptions opts; opts.showPointers = false; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ( R"(digraph graphname { @@ -242,7 +242,7 @@ local a: A ToDotOptions opts; opts.showPointers = false; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ( R"(digraph graphname { @@ -323,7 +323,7 @@ n3 [label="TableType 3"]; TEST_CASE_FIXTURE(Fixture, "free") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; Type type{TypeVariant{FreeType{TypeLevel{0, 0}}}}; @@ -341,7 +341,7 @@ n1 [label="FreeType 1"]; TEST_CASE_FIXTURE(Fixture, "free_with_constraints") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; Type type{TypeVariant{FreeType{nullptr, builtinTypes->numberType, builtinTypes->optionalNumberType}}}; diff --git a/Dependencies/luau/Luau/tests/ToString.test.cpp b/Dependencies/luau/Luau/tests/ToString.test.cpp index 67e247f2..59d9b5fd 100644 --- a/Dependencies/luau/Luau/tests/ToString.test.cpp +++ b/Dependencies/luau/Luau/tests/ToString.test.cpp @@ -11,7 +11,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction); -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauAttributeSyntax); LUAU_FASTFLAG(LuauUserDefinedTypeFunctions) @@ -22,7 +22,7 @@ TEST_CASE_FIXTURE(Fixture, "primitive") CheckResult result = check("local a = nil local b = 44 local c = 'lalala' local d = true"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("nil" == toString(requireType("a"))); else { @@ -45,7 +45,7 @@ TEST_CASE_FIXTURE(Fixture, "bound_types") TEST_CASE_FIXTURE(Fixture, "free_types") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check("local a"); LUAU_REQUIRE_NO_ERRORS(result); @@ -59,7 +59,7 @@ TEST_CASE_FIXTURE(Fixture, "cyclic_table") TableType* tableOne = getMutable(&cyclicTable); tableOne->props["self"] = {&cyclicTable}; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("t1 where t1 = {| self: t1 |}", toString(&cyclicTable)); else CHECK_EQ("t1 where t1 = { self: t1 }", toString(&cyclicTable)); @@ -80,7 +80,7 @@ TEST_CASE_FIXTURE(Fixture, "empty_table") local a: {} )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ }", toString(requireType("a"))); else CHECK_EQ("{| |}", toString(requireType("a"))); @@ -88,7 +88,7 @@ TEST_CASE_FIXTURE(Fixture, "empty_table") // Should stay the same with useLineBreaks enabled ToStringOptions opts; opts.useLineBreaks = true; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ }", toString(requireType("a"), opts)); else CHECK_EQ("{| |}", toString(requireType("a"), opts)); @@ -103,7 +103,7 @@ TEST_CASE_FIXTURE(Fixture, "table_respects_use_line_break") ToStringOptions opts; opts.useLineBreaks = true; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ( "{\n" " anotherProp: number,\n" @@ -150,7 +150,7 @@ TEST_CASE_FIXTURE(Fixture, "metatable") Type table{TypeVariant(TableType())}; Type metatable{TypeVariant(TableType())}; Type mtv{TypeVariant(MetatableType{&table, &metatable})}; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ @metatable {| |}, {| |} }", toString(&mtv)); else CHECK_EQ("{ @metatable { }, { } }", toString(&mtv)); @@ -166,7 +166,7 @@ TEST_CASE_FIXTURE(Fixture, "named_metatable") TEST_CASE_FIXTURE(BuiltinsFixture, "named_metatable_toStringNamedFunction") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function createTbl(): NamedMetatable @@ -207,7 +207,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "exhaustive_toString_of_cyclic_table") CHECK_EQ(std::string::npos, a.find("CYCLE")); CHECK_EQ(std::string::npos, a.find("TRUNCATED")); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK( "t2 where " @@ -346,7 +346,7 @@ TEST_CASE_FIXTURE(Fixture, "quit_stringifying_table_type_when_length_is_exceeded ToStringOptions o; o.exhaustive = false; o.maxTableLength = 40; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(&tv, o), "{| a: number, b: number, c: number, d: number, e: number, ... 10 more ... |}"); else CHECK_EQ(toString(&tv, o), "{ a: number, b: number, c: number, d: number, e: number, ... 10 more ... }"); @@ -363,7 +363,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_is_still_capped_when_exhaust ToStringOptions o; o.exhaustive = true; o.maxTableLength = 40; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(&tv, o), "{| a: number, b: number, c: number, d: number, e: number, ... 2 more ... |}"); else CHECK_EQ(toString(&tv, o), "{ a: number, b: number, c: number, d: number, e: number, ... 2 more ... }"); @@ -377,7 +377,7 @@ TEST_CASE_FIXTURE(Fixture, "quit_stringifying_type_when_length_is_exceeded") function f2(f) return f or f1 end function f3(f) return f or f2 end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); @@ -412,7 +412,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive") function f3(f) return f or f2 end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); @@ -448,7 +448,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table ToStringOptions o; o.maxTableLength = 40; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(&tv, o), "{ a: number, b: number, c: number, d: number, e: number, ... 5 more ... }"); else CHECK_EQ(toString(&tv, o), "{| a: number, b: number, c: number, d: number, e: number, ... 5 more ... |}"); @@ -482,7 +482,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_array_uses_array_syntax") CHECK_EQ("{string}", toString(Type{ttv})); ttv.props["A"] = {builtinTypes->numberType}; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ [number]: string, A: number }", toString(Type{ttv})); else CHECK_EQ("{| [number]: string, A: number |}", toString(Type{ttv})); @@ -594,7 +594,7 @@ TEST_CASE_FIXTURE(Fixture, "toStringDetailed") TEST_CASE_FIXTURE(Fixture, "toStringErrorPack") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function target(callback: nil) return callback(4, "hello") end @@ -633,7 +633,7 @@ TEST_CASE_FIXTURE(Fixture, "toString_the_boundTo_table_type_contained_within_a_T TypePackVar tpv2{TypePack{{&tv2}}}; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("{ hello: number, world: number }", toString(&tpv1)); CHECK_EQ("{ hello: number, world: number }", toString(&tpv2)); @@ -680,7 +680,7 @@ TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_cyclic_function_type_in_inters LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("(() -> t1) & ((number) -> ()) where t1 = () -> t1" == toString(requireType("a"))); else CHECK_EQ("((number) -> ()) & t1 where t1 = () -> t1", toString(requireType("a"))); @@ -723,7 +723,7 @@ TEST_CASE_FIXTURE(Fixture, "toStringNamedFunction_map") TypeId ty = requireType("map"); const FunctionType* ftv = get(follow(ty)); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("map(arr: {a}, fn: (a) -> (b, ...unknown)): {b}", toStringNamedFunction("map", *ftv)); else CHECK_EQ("map(arr: {a}, fn: (a) -> b): {b}", toStringNamedFunction("map", *ftv)); @@ -841,9 +841,9 @@ TEST_CASE_FIXTURE(Fixture, "pick_distinct_names_for_mixed_explicit_and_implicit_ function foo(x: a, y) end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { - CHECK("(a, 'b) -> ()" == toString(requireType("foo"))); + CHECK("(a, unknown) -> ()" == toString(requireType("foo"))); } else CHECK("(a, b) -> ()" == toString(requireType("foo"))); @@ -873,7 +873,7 @@ TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch") )"); std::string expected; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) expected = R"(Type pack '{ a: number, b: string, c: { d: string } }' could not be converted into '{ a: number, b: string, c: { d: number } }'; at [0][read "c"][read "d"], string is not exactly number)"; else @@ -901,7 +901,7 @@ Type 'string' could not be converted into 'number' in an invariant context)"; TEST_CASE_FIXTURE(Fixture, "checked_fn_toString") { ScopedFastFlag flags[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; auto _result = loadDefinition(R"( @@ -920,7 +920,7 @@ local f = abs TEST_CASE_FIXTURE(Fixture, "read_only_properties") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type A = {x: string} @@ -950,7 +950,7 @@ TEST_CASE_FIXTURE(Fixture, "cycle_rooted_in_a_pack") packPtr->head[0] = theTable; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("tp1 where tp1 = { read BaseField: unknown, read BaseMethod: (tp1) -> () }, number" == toString(thePack)); else CHECK("tp1 where tp1 = {| BaseField: unknown, BaseMethod: (tp1) -> () |}, number" == toString(thePack)); @@ -969,7 +969,7 @@ TEST_CASE_FIXTURE(Fixture, "correct_stringification_user_defined_type_functions" Type tv{tftt}; - if (FFlag::DebugLuauDeferredConstraintResolution && FFlag::LuauUserDefinedTypeFunctions) + if (FFlag::LuauSolverV2 && FFlag::LuauUserDefinedTypeFunctions) CHECK_EQ(toString(&tv, {}), "woohoo"); } diff --git a/Dependencies/luau/Luau/tests/TxnLog.test.cpp b/Dependencies/luau/Luau/tests/TxnLog.test.cpp index d050fb9d..b4b18353 100644 --- a/Dependencies/luau/Luau/tests/TxnLog.test.cpp +++ b/Dependencies/luau/Luau/tests/TxnLog.test.cpp @@ -12,7 +12,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) struct TxnLogFixture { @@ -35,7 +35,7 @@ TEST_SUITE_BEGIN("TxnLog"); TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_greater_scope") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; log.replace(c, BoundType{a}); log2.replace(a, BoundType{c}); @@ -68,7 +68,7 @@ TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_greater_scop TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_lesser_scope") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; log.replace(a, BoundType{c}); log2.replace(c, BoundType{a}); @@ -101,7 +101,7 @@ TEST_CASE_FIXTURE(TxnLogFixture, "colliding_union_incoming_type_has_lesser_scope TEST_CASE_FIXTURE(TxnLogFixture, "colliding_coincident_logs_do_not_create_degenerate_unions") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; log.replace(a, BoundType{b}); log2.replace(a, BoundType{b}); diff --git a/Dependencies/luau/Luau/tests/TypeFunction.test.cpp b/Dependencies/luau/Luau/tests/TypeFunction.test.cpp index 7b45a69c..2b7b40a3 100644 --- a/Dependencies/luau/Luau/tests/TypeFunction.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeFunction.test.cpp @@ -12,7 +12,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauUserDefinedTypeFunctions) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) @@ -67,7 +67,7 @@ TEST_SUITE_BEGIN("TypeFunctionTests"); TEST_CASE_FIXTURE(TypeFunctionFixture, "basic_type_function") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -89,7 +89,7 @@ TEST_CASE_FIXTURE(TypeFunctionFixture, "basic_type_function") TEST_CASE_FIXTURE(TypeFunctionFixture, "function_as_fn_ret") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -108,7 +108,7 @@ TEST_CASE_FIXTURE(TypeFunctionFixture, "function_as_fn_ret") TEST_CASE_FIXTURE(TypeFunctionFixture, "function_as_fn_arg") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -126,7 +126,7 @@ TEST_CASE_FIXTURE(TypeFunctionFixture, "function_as_fn_arg") TEST_CASE_FIXTURE(TypeFunctionFixture, "resolve_deep_functions") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -139,7 +139,7 @@ TEST_CASE_FIXTURE(TypeFunctionFixture, "resolve_deep_functions") TEST_CASE_FIXTURE(TypeFunctionFixture, "unsolvable_function") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -159,7 +159,7 @@ TEST_CASE_FIXTURE(TypeFunctionFixture, "unsolvable_function") TEST_CASE_FIXTURE(TypeFunctionFixture, "table_internal_functions") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -179,7 +179,7 @@ TEST_CASE_FIXTURE(TypeFunctionFixture, "table_internal_functions") TEST_CASE_FIXTURE(TypeFunctionFixture, "function_internal_functions") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -200,7 +200,7 @@ TEST_CASE_FIXTURE(TypeFunctionFixture, "function_internal_functions") TEST_CASE_FIXTURE(Fixture, "add_function_at_work") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -229,7 +229,7 @@ TEST_CASE_FIXTURE(Fixture, "add_function_at_work") TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_add_function_at_work") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -242,7 +242,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_add_function_at_work") TEST_CASE_FIXTURE(BuiltinsFixture, "mul_function_with_union_of_multiplicatives") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; loadDefinition(R"( @@ -265,7 +265,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "mul_function_with_union_of_multiplicatives") TEST_CASE_FIXTURE(BuiltinsFixture, "mul_function_with_union_of_multiplicatives_2") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; loadDefinition(R"( @@ -285,7 +285,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "mul_function_with_union_of_multiplicatives_2 TEST_CASE_FIXTURE(Fixture, "internal_functions_raise_errors") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -303,7 +303,7 @@ TEST_CASE_FIXTURE(Fixture, "internal_functions_raise_errors") TEST_CASE_FIXTURE(BuiltinsFixture, "type_functions_can_be_shadowed") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -328,7 +328,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_functions_can_be_shadowed") TEST_CASE_FIXTURE(BuiltinsFixture, "type_functions_inhabited_with_normalization") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -346,7 +346,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_functions_inhabited_with_normalization" TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -367,7 +367,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works") TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -388,9 +388,28 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables") CHECK_EQ("\"w\" | \"x\" | \"y\" | \"z\"", toString(tpm->givenTp)); } +TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_single_entry_no_uniontype") +{ + if (!FFlag::LuauSolverV2) + return; + + CheckResult result = check(R"( + local tbl_A = { abc = "value" } + local tbl_B = { a1 = nil, ["a2"] = nil } + + type keyof_A = keyof + type keyof_B = keyof + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + + CHECK(toString(requireTypeAlias("keyof_A")) == "\"abc\""); + CHECK(toString(requireTypeAlias("keyof_B")) == "\"a1\" | \"a2\""); +} + TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_errors_if_it_has_nontable_part") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -408,7 +427,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_errors_if_it_has_nontabl TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -436,7 +455,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer") TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_common_subset_if_union_of_differing_tables") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -457,7 +476,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_common_subset_if_union_o TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_never_for_empty_table") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -472,7 +491,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_never_for_empty_table") TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_works") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -493,7 +512,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_works") TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_ignores_metatables") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -516,7 +535,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_ignores_metatables") TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_errors_if_it_has_nontable_part") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -534,7 +553,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_errors_if_it_has_nont TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_common_subset_if_union_of_differing_tables") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -555,7 +574,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_common_subset_if_unio TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_never_for_empty_table") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -570,7 +589,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_never_for_empty_table TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_works_on_classes") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -590,7 +609,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_works_on_classes") TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_errors_if_it_has_nonclass_part") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -607,7 +626,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_errors_if_it_has_nonclass_p TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_common_subset_if_union_of_differing_classes") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -619,9 +638,23 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_common_subset_if_union_of_d LUAU_REQUIRE_NO_ERRORS(result); } +TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_works_with_parent_classes_too") +{ + if (!FFlag::LuauSolverV2) + return; + + CheckResult result = check(R"( + type KeysOfMyObject = keyof + + local function ok(idx: KeysOfMyObject): "BaseField" | "BaseMethod" | "Method" | "Touched" return idx end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_CASE_FIXTURE(ClassFixture, "binary_type_function_works_with_default_argument") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -636,7 +669,7 @@ TEST_CASE_FIXTURE(ClassFixture, "binary_type_function_works_with_default_argumen TEST_CASE_FIXTURE(ClassFixture, "vector2_multiply_is_overloaded") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -659,7 +692,7 @@ TEST_CASE_FIXTURE(ClassFixture, "vector2_multiply_is_overloaded") TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_rfc_example") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -690,7 +723,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_rfc_example") TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_oss_crash_gh1161") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -730,7 +763,7 @@ _(setmetatable(_,{[...]=_,})) TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_concat_function_at_work") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -743,7 +776,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_concat_function_at_work") TEST_CASE_FIXTURE(BuiltinsFixture, "exceeded_distributivity_limits") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; ScopedFastInt sfi{DFInt::LuauTypeFamilyApplicationCartesianProductLimit, 10}; @@ -776,7 +809,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "exceeded_distributivity_limits") TEST_CASE_FIXTURE(BuiltinsFixture, "didnt_quite_exceed_distributivity_limits") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; // We duplicate the test here because we want to make sure the test failed @@ -810,7 +843,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "didnt_quite_exceed_distributivity_limits") TEST_CASE_FIXTURE(BuiltinsFixture, "ensure_equivalence_with_distributivity") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; loadDefinition(R"( @@ -843,7 +876,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "ensure_equivalence_with_distributivity") TEST_CASE_FIXTURE(BuiltinsFixture, "we_shouldnt_warn_that_a_reducible_type_function_is_uninhabited") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -873,7 +906,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -894,9 +927,35 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works") CHECK_EQ("string", toString(tpm->givenTp)); } +TEST_CASE_FIXTURE(BuiltinsFixture, "index_wait_for_pending_no_crash") +{ + if (!FFlag::LuauSolverV2) + return; + + CheckResult result = check(R"( + local PlayerData = { + Coins = 0, + Level = 1, + Exp = 0, + MaxExp = 100 + } + + type Keys = index> + + -- This function makes it think that there's going to be a pending expansion + local function UpdateData(key: Keys, value) + PlayerData[key] = value + end + + UpdateData("Coins", 2) + )"); + + // Should not crash! +} + TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_array") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -911,7 +970,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_array") TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_generic_types") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -933,7 +992,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_generic_types") TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_errors_w_bad_indexer") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -949,7 +1008,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_errors_w_bad_indexer") TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_errors_w_var_indexer") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -966,7 +1025,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_errors_w_var_indexer") TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_union_type_indexer") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -984,7 +1043,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_union_type_index TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_union_type_indexee") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1003,7 +1062,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_union_type_index TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_rfc_alternative_section") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1021,7 +1080,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_rfc_alternative_section" TEST_CASE_FIXTURE(ClassFixture, "index_type_function_works_on_classes") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1033,9 +1092,23 @@ TEST_CASE_FIXTURE(ClassFixture, "index_type_function_works_on_classes") LUAU_REQUIRE_NO_ERRORS(result); } +TEST_CASE_FIXTURE(ClassFixture, "index_type_function_works_on_classes_with_parents") +{ + if (!FFlag::LuauSolverV2) + return; + + CheckResult result = check(R"( + type KeysOfMyObject = index + + local function ok(idx: KeysOfMyObject): number return idx end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_index_metatables") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1061,7 +1134,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_index_metatables TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1083,7 +1156,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works") TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_array") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1097,7 +1170,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_array") TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_errors_w_var_indexer") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1113,7 +1186,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_errors_w_var_indexer") TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_union_type_indexer") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1129,7 +1202,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_union_type_inde TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_union_type_indexee") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1146,7 +1219,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_union_type_inde TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_index_metatables") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1166,7 +1239,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works_w_index_metatable TEST_CASE_FIXTURE(ClassFixture, "rawget_type_function_errors_w_classes") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( diff --git a/Dependencies/luau/Luau/tests/TypeInfer.aliases.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.aliases.test.cpp index 5b689939..bb5a2cdd 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.aliases.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.aliases.test.cpp @@ -8,7 +8,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauUserDefinedTypeFunctions) TEST_SUITE_BEGIN("TypeAliases"); @@ -72,7 +72,7 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK( result.errors[0] == @@ -105,7 +105,7 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias") TEST_CASE_FIXTURE(Fixture, "mismatched_generic_type_param") { // We erroneously report an extra error in this case when the new solver is enabled. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type T = (A...) -> () @@ -205,7 +205,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_aliases") TEST_CASE_FIXTURE(Fixture, "generic_aliases") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; CheckResult result = check(R"( type T = { v: a } @@ -223,7 +223,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_aliases") TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; CheckResult result = check(R"( @@ -244,7 +244,7 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases") { // CLI-116108 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -344,7 +344,7 @@ TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_typ TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("t1 where t1 = ({ a: t1 }) -> string", toString(tm->wantedType)); else CHECK_EQ("t1 where t1 = ({| a: t1 |}) -> string", toString(tm->wantedType)); @@ -355,7 +355,7 @@ TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_typ TEST_CASE_FIXTURE(Fixture, "cli_38393_recursive_intersection_oom") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; // FIXME CheckResult result = check(R"( @@ -417,7 +417,7 @@ TEST_CASE_FIXTURE(Fixture, "corecursive_function_types") TEST_CASE_FIXTURE(Fixture, "generic_param_remap") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; const std::string code = R"( -- An example of a forwarded use of a type that has different type arguments than parameters @@ -543,7 +543,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_import_mutation") TEST_CASE_FIXTURE(Fixture, "type_alias_local_mutation") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Cool = { a: number, b: string } @@ -564,7 +564,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_local_mutation") TEST_CASE_FIXTURE(Fixture, "type_alias_local_rename") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Cool = { a: number, b: string } @@ -660,7 +660,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_of_an_imported_recursive_generic_ ty2 = lookupType("X"); REQUIRE(ty2); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK(toString(*ty1, {true}) == "t1 where t1 = { C: t1?, a: T, b: U }"); CHECK(toString(*ty2, {true}) == "t1 where t1 = { C: t1?, a: U, b: T }"); @@ -712,7 +712,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_ok") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1") { // CLI-116108 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( -- OK because forwarded types are used with their parameters. @@ -726,7 +726,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_2") { // CLI-116108 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( -- Not OK because forwarded types are used with different types than their parameters. @@ -750,7 +750,7 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_ok") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_not_ok") { // CLI-116108 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Tree1 = { data: T, children: {Tree2} } @@ -855,7 +855,7 @@ TEST_CASE_FIXTURE(Fixture, "forward_declared_alias_is_not_clobbered_by_prior_uni local d: FutureType = { smth = true } -- missing error, 'd' is resolved to 'any' )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ foo: number }", toString(requireType("d"), {true})); else CHECK_EQ("{| foo: number |}", toString(requireType("d"), {true})); @@ -875,7 +875,7 @@ TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_ok") TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok") { // CLI-116108 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( -- this would be an infinite type if we allowed it @@ -888,7 +888,7 @@ TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok") TEST_CASE_FIXTURE(Fixture, "report_shadowed_aliases") { // CLI-116110 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; // We allow a previous type alias to depend on a future type alias. That exact feature enables a confusing example, like the following snippet, // which has the type alias FakeString point to the type alias `string` that which points to `number`. @@ -972,7 +972,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_locations") TEST_CASE_FIXTURE(BuiltinsFixture, "dont_lose_track_of_PendingExpansionTypes_after_substitution") { // CLI-114134 - We need egraphs to properly simplify these types. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; fileResolver.source["game/ReactCurrentDispatcher"] = R"( export type BasicStateAction = ((S) -> S) | S @@ -1115,7 +1115,7 @@ type Foo = Foo TEST_CASE_FIXTURE(Fixture, "recursive_type_alias_bad_pack_use_warns") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1155,7 +1155,7 @@ type Foo = Foo | string TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_adds_reduce_constraint_for_type_function") { - if (!FFlag::DebugLuauDeferredConstraintResolution || !FFlag::LuauUserDefinedTypeFunctions) + if (!FFlag::LuauSolverV2 || !FFlag::LuauUserDefinedTypeFunctions) return; CheckResult result = check(R"( diff --git a/Dependencies/luau/Luau/tests/TypeInfer.annotations.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.annotations.test.cpp index 02f34054..4029924a 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.annotations.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.annotations.test.cpp @@ -7,7 +7,7 @@ #include "doctest.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(DebugLuauMagicTypes); using namespace Luau; @@ -78,7 +78,7 @@ TEST_CASE_FIXTURE(Fixture, "assignment_cannot_transform_a_table_property_type") TEST_CASE_FIXTURE(Fixture, "assignments_to_unannotated_parameters_can_transform_the_type") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function f(x) @@ -94,7 +94,7 @@ TEST_CASE_FIXTURE(Fixture, "assignments_to_unannotated_parameters_can_transform_ TEST_CASE_FIXTURE(Fixture, "assignments_to_annotated_parameters_are_checked") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function f(x: string) @@ -264,7 +264,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_of_value_a_via_typeof_with_assignment") a = "foo" )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("string?" == toString(requireType("a"))); CHECK("nil" == toString(requireType("b"))); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.anyerror.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.anyerror.test.cpp index dc1c6199..445ce072 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.anyerror.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.anyerror.test.cpp @@ -13,7 +13,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); TEST_SUITE_BEGIN("TypeInferAnyError"); @@ -32,7 +32,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("any?" == toString(requireType("a"))); else CHECK(builtinTypes->anyType == requireType("a")); @@ -53,7 +53,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any2") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("any?" == toString(requireType("a"))); else CHECK("any" == toString(requireType("a"))); @@ -72,7 +72,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("any?" == toString(requireType("a"))); else CHECK("any" == toString(requireType("a"))); @@ -89,7 +89,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any2") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("any?" == toString(requireType("a"))); else CHECK("any" == toString(requireType("a"))); @@ -108,7 +108,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_any_pack") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("any?" == toString(requireType("a"))); else CHECK("any" == toString(requireType("a"))); @@ -126,7 +126,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error") LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // Bug: We do not simplify at the right time CHECK_EQ("*error-type*?", toString(requireType("a"))); @@ -148,7 +148,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_is_error2") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-97375(awe): `bar()` is returning `nil` here, which isn't wrong necessarily, // but then we're signaling an additional error for the access on `nil`. @@ -277,7 +277,7 @@ TEST_CASE_FIXTURE(Fixture, "calling_error_type_yields_error") CHECK_EQ("unknown", err->name); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("any", toString(requireType("a"))); else CHECK_EQ("*error-type*", toString(requireType("a"))); @@ -289,7 +289,7 @@ TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error") local a = Utility.Create "Foo" {} )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("any", toString(requireType("a"))); else CHECK_EQ("*error-type*", toString(requireType("a"))); @@ -307,7 +307,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_comp LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("any?", toString(requireType("b"))); else CHECK_EQ("any", toString(requireType("b"))); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.builtins.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.builtins.test.cpp index d52e6390..9912cc35 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.builtins.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.builtins.test.cpp @@ -8,7 +8,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); TEST_SUITE_BEGIN("BuiltinTests"); @@ -132,7 +132,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_predicate") TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_bad_predicate") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -414,7 +414,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_pack") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ [number]: boolean | number | string, n: number }", toString(requireType("t"))); else CHECK_EQ("{| [number]: boolean | number | string, n: number |}", toString(requireType("t"))); @@ -432,7 +432,7 @@ local t = table.pack(f()) )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ [number]: number | string, n: number }", toString(requireType("t"))); else CHECK_EQ("{| [number]: number | string, n: number |}", toString(requireType("t"))); @@ -445,7 +445,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_pack_reduce") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ [number]: boolean | number, n: number }", toString(requireType("t"))); else CHECK_EQ("{| [number]: boolean | number, n: number |}", toString(requireType("t"))); @@ -455,7 +455,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_pack_reduce") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ [number]: string, n: number }", toString(requireType("t"))); else CHECK_EQ("{| [number]: string, n: number |}", toString(requireType("t"))); @@ -512,7 +512,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "buffer_is_a_type") TEST_CASE_FIXTURE(BuiltinsFixture, "coroutine_resume_anything_goes") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function nifty(x, y) @@ -551,7 +551,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "coroutine_wrap_anything_goes") TEST_CASE_FIXTURE(BuiltinsFixture, "setmetatable_should_not_mutate_persisted_types") { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -599,7 +599,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_arg_count_mismatch") TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_correctly_ordered_types") { // CLI-115690 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -703,7 +703,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_select_should_not_crash") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + 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. @@ -721,7 +721,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_select_should_not_crash") TEST_CASE_FIXTURE(BuiltinsFixture, "select_way_out_of_range") { // CLI-115720 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -736,7 +736,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_way_out_of_range") TEST_CASE_FIXTURE(BuiltinsFixture, "select_slightly_out_of_range") { // CLI-115720 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -770,7 +770,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail") TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail_and_string_head") { // CLI-115720 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -792,10 +792,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail_and_strin TEST_CASE_FIXTURE(Fixture, "string_format_as_method") { - // CLI-115690 - if (FFlag::DebugLuauDeferredConstraintResolution) - return; - CheckResult result = check("local _ = ('%s'):format(5)"); LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -819,10 +815,6 @@ TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument") TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument2") { - // CLI-115690 - if (FFlag::DebugLuauDeferredConstraintResolution) - return; - CheckResult result = check(R"( local _ = ("%s %d").format("%d %s", "A type error", 2) )"); @@ -881,10 +873,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "debug_info_is_crazy") TEST_CASE_FIXTURE(BuiltinsFixture, "aliased_string_format") { - // CLI-115690 - if (FFlag::DebugLuauDeferredConstraintResolution) - return; - CheckResult result = check(R"( local fmt = string.format local s = fmt("%d", "oops") @@ -944,10 +932,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_on_variadic") TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_report_all_type_errors_at_correct_positions") { - // CLI-115690 - if (FFlag::DebugLuauDeferredConstraintResolution) - return; - CheckResult result = check(R"( ("%s%d%s"):format(1, "hello", true) string.format("%s%d%s", 1, "hello", true) @@ -986,7 +970,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tonumber_returns_optional_number_type") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ( "Type 'number?' could not be converted into 'number'; type number?[1] (nil) is not a subtype of number (number)", toString(result.errors[0]) @@ -1009,7 +993,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_add_definitions_to_persistent_types") { // This test makes no sense with type states and I think it generally makes no sense under the new solver. // TODO: clip. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1042,7 +1026,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_removes_falsy_types") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("((boolean | number)?) -> number | true", toString(requireType("f"))); else CHECK_EQ("((boolean | number)?) -> boolean | number", toString(requireType("f"))); @@ -1070,7 +1054,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_removes_falsy_types3") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("((boolean | number)?) -> number | true", toString(requireType("f"))); else // without the annotation, the old solver doesn't infer the best return type here CHECK_EQ("((boolean | number)?) -> boolean | number", toString(requireType("f"))); @@ -1078,7 +1062,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_removes_falsy_types3") TEST_CASE_FIXTURE(BuiltinsFixture, "assert_removes_falsy_types_even_from_type_pack_tail_but_only_for_the_first_type") { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1093,7 +1077,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_removes_falsy_types_even_from_type_pa TEST_CASE_FIXTURE(BuiltinsFixture, "assert_returns_false_and_string_iff_it_knows_the_first_argument_cannot_be_truthy") { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-114134 - egraph simplification return; @@ -1128,7 +1112,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("Key 'b' not found in table '{ a: number }'" == toString(result.errors[0])); else CHECK_EQ("Key 'b' not found in table '{| a: number |}'", toString(result.errors[0])); @@ -1138,7 +1122,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic") CHECK_EQ("string", toString(requireType("b"))); CHECK_EQ("boolean", toString(requireType("c"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("any", toString(requireType("d"))); else CHECK_EQ("*error-type*", toString(requireType("d"))); @@ -1147,7 +1131,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic") TEST_CASE_FIXTURE(BuiltinsFixture, "set_metatable_needs_arguments") { // In the new solver, nil can certainly be used where a generic is required, so all generic parameters are optional. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local a = {b=setmetatable} diff --git a/Dependencies/luau/Luau/tests/TypeInfer.classes.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.classes.test.cpp index 17b14082..16751559 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.classes.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.classes.test.cpp @@ -13,7 +13,7 @@ using namespace Luau; using std::nullopt; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); TEST_SUITE_BEGIN("TypeInferClasses"); @@ -128,7 +128,7 @@ TEST_CASE_FIXTURE(ClassFixture, "we_can_infer_that_a_parameter_must_be_a_particu TEST_CASE_FIXTURE(ClassFixture, "we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function makeClone(o) @@ -156,7 +156,7 @@ TEST_CASE_FIXTURE(ClassFixture, "we_can_report_when_someone_is_trying_to_use_a_t TEST_CASE_FIXTURE(ClassFixture, "we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class_using_new_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function makeClone(o) @@ -235,7 +235,7 @@ TEST_CASE_FIXTURE(ClassFixture, "can_assign_to_prop_of_base_class_using_string") TEST_CASE_FIXTURE(ClassFixture, "cannot_unify_class_instance_with_primitive") { // This is allowed in the new solver - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local v = Vector2.New(0, 5) @@ -397,7 +397,7 @@ TEST_CASE_FIXTURE(ClassFixture, "table_class_unification_reports_sane_errors_for foo(a) )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK("Type 'Vector2' could not be converted into '{ Y: number, w: number, x: number }'" == toString(result.errors[0])); @@ -454,7 +454,7 @@ b(a) LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("Type 'number' could not be converted into 'string'" == toString(result.errors.at(0))); } @@ -472,7 +472,7 @@ Type 'number' could not be converted into 'string')"; TEST_CASE_FIXTURE(ClassFixture, "class_type_mismatch_with_name_conflict") { // CLI-116433 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local i = ChildClass.New() @@ -545,7 +545,7 @@ local b: B = a LUAU_REQUIRE_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors.at(0)) == "Type 'A' could not be converted into 'B'; at [read \"x\"], ChildClass is not exactly BaseClass"); else { @@ -559,7 +559,7 @@ Type 'ChildClass' could not be converted into 'BaseClass' in an invariant contex TEST_CASE_FIXTURE(ClassFixture, "optional_class_casts_work_in_new_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type A = { x: ChildClass } @@ -664,7 +664,7 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes") local y = x[true] )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK( "Type 'boolean' could not be converted into 'number | string'" == toString(result.errors.at(0)) ); @@ -679,7 +679,7 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes") x[true] = 42 )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK( "Type 'boolean' could not be converted into 'number | string'" == toString(result.errors.at(0)) ); @@ -696,7 +696,7 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes") x.key = "string value" )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // Disabled for now. CLI-115686 } @@ -724,7 +724,7 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes") local x : IndexableNumericKeyClass x["key"] = 1 )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(result.errors.at(0)), "Key 'key' not found in class 'IndexableNumericKeyClass'"); else CHECK_EQ(toString(result.errors.at(0)), "Type 'string' could not be converted into 'number'"); @@ -749,7 +749,7 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes") local x : IndexableNumericKeyClass local y = x["key"] )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors.at(0)) == "Key 'key' not found in class 'IndexableNumericKeyClass'"); else CHECK_EQ(toString(result.errors.at(0)), "Type 'string' could not be converted into 'number'"); @@ -766,7 +766,7 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes") TEST_CASE_FIXTURE(Fixture, "read_write_class_properties") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; TypeArena& arena = frontend.globals.globalTypes; diff --git a/Dependencies/luau/Luau/tests/TypeInfer.functions.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.functions.test.cpp index 773636c6..c76745c7 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.functions.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.functions.test.cpp @@ -17,7 +17,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauInstantiateInSubtyping); -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTINT(LuauTarjanChildLimit); LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError) @@ -79,7 +79,7 @@ TEST_CASE_FIXTURE(Fixture, "check_function_bodies") LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { const TypePackMismatch* tm = get(result.errors[0]); REQUIRE(tm); @@ -238,7 +238,7 @@ TEST_CASE_FIXTURE(Fixture, "list_only_alternative_overloads_that_match_argument_ LUAU_REQUIRE_ERROR_COUNT(2, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { GenericError* g = get(result.errors[0]); REQUIRE(g); @@ -255,7 +255,7 @@ TEST_CASE_FIXTURE(Fixture, "list_only_alternative_overloads_that_match_argument_ ExtraInformation* ei = get(result.errors[1]); REQUIRE(ei); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("Available overloads: (number) -> number; (number) -> string; and (number, number) -> number" == ei->message); else CHECK_EQ("Other overloads are also not viable: (number) -> string", ei->message); @@ -312,7 +312,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_return_type_from_selected_overload") TEST_CASE_FIXTURE(Fixture, "too_many_arguments") { // This is not part of the new non-strict specification currently. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict @@ -480,7 +480,7 @@ TEST_CASE_FIXTURE(Fixture, "another_higher_order_function") TEST_CASE_FIXTURE(Fixture, "another_other_higher_order_function") { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CheckResult result = check(R"( local function f(d) @@ -490,7 +490,6 @@ TEST_CASE_FIXTURE(Fixture, "another_other_higher_order_function") )"); LUAU_REQUIRE_NO_ERRORS(result); - } else { @@ -605,7 +604,7 @@ TEST_CASE_FIXTURE(Fixture, "duplicate_functions_allowed_in_nonstrict") TEST_CASE_FIXTURE(Fixture, "duplicate_functions_with_different_signatures_not_allowed_in_nonstrict") { // This is not part of the spec for the new non-strict mode currently. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict @@ -778,11 +777,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "higher_order_function_4") end )"); - // This function currently has a bug in the new solver reporting `{T} | {T}` is not a table. - if (FFlag::DebugLuauDeferredConstraintResolution) - LUAU_REQUIRE_ERRORS(result); - else - LUAU_REQUIRE_NO_ERRORS(result); + LUAU_REQUIRE_NO_ERRORS(result); /* * mergesort takes two arguments: an array of some type T and a function that takes two Ts. @@ -888,7 +883,7 @@ TEST_CASE_FIXTURE(Fixture, "another_indirect_function_case_where_it_is_ok_to_pro TEST_CASE_FIXTURE(Fixture, "report_exiting_without_return_nonstrict") { // new non-strict mode spec does not include this error yet. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict @@ -1014,7 +1009,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_lea opts.exhaustive = true; opts.maxTableLength = 0; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{string}", toString(requireType("tab"), opts)); else CHECK_EQ("{any}", toString(requireType("tab"), opts)); @@ -1023,7 +1018,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_lea TEST_CASE_FIXTURE(Fixture, "too_many_return_values") { // FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -1047,7 +1042,7 @@ TEST_CASE_FIXTURE(Fixture, "too_many_return_values") TEST_CASE_FIXTURE(Fixture, "too_many_return_values_in_parentheses") { // FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -1071,7 +1066,7 @@ TEST_CASE_FIXTURE(Fixture, "too_many_return_values_in_parentheses") TEST_CASE_FIXTURE(Fixture, "too_many_return_values_no_function") { // FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -1113,7 +1108,7 @@ TEST_CASE_FIXTURE(Fixture, "function_does_not_return_enough_values") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -1153,7 +1148,7 @@ TEST_CASE_FIXTURE(Fixture, "function_cast_error_uses_correct_language") REQUIRE(tm1); CHECK_EQ("(string) -> number", toString(tm1->wantedType)); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("(unknown, unknown) -> number", toString(tm1->givenType)); else CHECK_EQ("(string, *error-type*) -> number", toString(tm1->givenType)); @@ -1162,7 +1157,7 @@ TEST_CASE_FIXTURE(Fixture, "function_cast_error_uses_correct_language") REQUIRE(tm2); CHECK_EQ("(number, number) -> (number, number)", toString(tm2->wantedType)); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("(unknown, unknown) -> number", toString(tm1->givenType)); else CHECK_EQ("(string, *error-type*) -> number", toString(tm2->givenType)); @@ -1182,7 +1177,7 @@ TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type") LUAU_REQUIRE_NO_ERRORS(result); TypeId type = requireTypeAtPosition(Position(6, 14)); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("(unknown, number, number) -> number", toString(type)); else CHECK_EQ("(tbl, number, number) -> number", toString(type)); @@ -1229,7 +1224,7 @@ TEST_CASE_FIXTURE(Fixture, "return_type_by_overload") LUAU_REQUIRE_ERRORS(result); CHECK_EQ("string", toString(requireType("x"))); // the new solver does not currently "favor" arity-matching overloads when the call itself is ill-typed. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("string", toString(requireType("y"))); else CHECK_EQ("number", toString(requireType("y"))); @@ -1240,7 +1235,7 @@ TEST_CASE_FIXTURE(Fixture, "return_type_by_overload") TEST_CASE_FIXTURE(BuiltinsFixture, "infer_anonymous_function_arguments") { // FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; // Simple direct arg to arg propagation CheckResult result = check(R"( @@ -1359,7 +1354,7 @@ f(function(x) return x * 2 end) TEST_CASE_FIXTURE(BuiltinsFixture, "infer_generic_function_function_argument") { // FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function sum(x: a, y: a, f: (a, a) -> a) return f(x, y) end @@ -1390,7 +1385,7 @@ local r = foldl(a, {s=0,c=0}, function(a, b) return {s = a.s + b, c = a.c + 1} e TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded") { // FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function g1(a: T, f: (T) -> T) return f(a) end @@ -1457,7 +1452,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "variadic_any_is_compatible_with_a_generic_Ty TEST_CASE_FIXTURE(Fixture, "infer_anonymous_function_arguments_outside_call") { // FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Table = { x: number, y: number } @@ -1491,7 +1486,7 @@ end )"); // `h` regresses in the new solver, the return type is not being pushed into the body. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_ERROR_COUNT(1, result); else LUAU_REQUIRE_NO_ERRORS(result); @@ -1500,7 +1495,7 @@ end TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_arg_count") { // FIXME: CLI-116111 test disabled until type path stringification is improved - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type A = (number, number) -> string @@ -1523,7 +1518,7 @@ caused by: TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_arg") { // FIXME: CLI-116111 test disabled until type path stringification is improved - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type A = (number, number) -> string @@ -1547,7 +1542,7 @@ Type 'string' could not be converted into 'number')"; TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret_count") { // FIXME: CLI-116111 test disabled until type path stringification is improved - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type A = (number, number) -> (number) @@ -1570,7 +1565,7 @@ caused by: TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret") { // FIXME: CLI-116111 test disabled until type path stringification is improved - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type A = (number, number) -> string @@ -1594,7 +1589,7 @@ Type 'string' could not be converted into 'number')"; TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret_mult") { // FIXME: CLI-116111 test disabled until type path stringification is improved - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type A = (number, number) -> (number, string) @@ -1675,11 +1670,17 @@ t.f = function(x) end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); - CHECK_EQ(toString(result.errors[0]), R"(Type function instance add depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)"); - CHECK_EQ(toString(result.errors[1]), R"(Type function instance add depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)"); + CHECK_EQ( + toString(result.errors[0]), + R"(Type function instance add depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)" + ); + CHECK_EQ( + toString(result.errors[1]), + R"(Type function instance add depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)" + ); } else { @@ -1717,8 +1718,9 @@ TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_th TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_the_right_time3") { - // This test regresses in the new solver, but is sort of nonsensical insofar as `foo` is known to be `nil`, so it's "right" to not be able to call it. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + // This test regresses in the new solver, but is sort of nonsensical insofar as `foo` is known to be `nil`, so it's "right" to not be able to call + // it. + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local foo @@ -1746,10 +1748,13 @@ t.f = function(x) end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); - CHECK_EQ(toString(result.errors[0]), R"(Type function instance add depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)"); + CHECK_EQ( + toString(result.errors[0]), + R"(Type function instance add depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)" + ); } else { @@ -1784,7 +1789,7 @@ TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments") TEST_CASE_FIXTURE(Fixture, "function_statement_sealed_table_assignment_through_indexer") { // FIXME: CLI-116122 bug where `t:b` does not check against the type from the indexer annotation on `t`. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local t: {[string]: () -> number} = {} @@ -1829,7 +1834,7 @@ TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic") TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic_generic") { // FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function test(a: number, b: string, ...) @@ -1857,7 +1862,7 @@ wrapper(test) TEST_CASE_FIXTURE(BuiltinsFixture, "too_few_arguments_variadic_generic2") { // FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function test(a: number, b: string, ...) @@ -1898,7 +1903,7 @@ TEST_CASE_FIXTURE(Fixture, "occurs_check_failure_in_function_return_type") TEST_CASE_FIXTURE(Fixture, "free_is_not_bound_to_unknown") { // This test only makes sense for the old solver - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1932,7 +1937,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_ LUAU_REQUIRE_NO_ERRORS(result); CHECK_EQ("(a) -> a", toString(requireType("f"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("({ read p: { read q: unknown } }) -> ~(false?)?", toString(requireType("g"))); else CHECK_EQ("({+ p: {+ q: nil +} +}) -> nil", toString(requireType("g"))); @@ -1980,7 +1985,7 @@ u.b().foo() )"); LUAU_REQUIRE_ERROR_COUNT(9, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // These improvements to the error messages are currently regressed in the new type solver. CHECK_EQ(toString(result.errors[0]), "Argument count mismatch. Function expects 1 argument, but none are specified"); @@ -2011,7 +2016,7 @@ u.b().foo() TEST_CASE_FIXTURE(BuiltinsFixture, "improved_function_arg_mismatch_error_nonstrict") { // This behavior is not part of the current specification of the new type solver. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict @@ -2026,7 +2031,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "improved_function_arg_mismatch_error_nonstri TEST_CASE_FIXTURE(Fixture, "luau_subtyping_is_np_hard") { // The case that _should_ succeed here (`z = x`) does not currently in the new solver. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -2148,7 +2153,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "dont_assert_when_the_tarjan_limit_is_exceeded_during_generalization") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopedFastInt sfi{FInt::LuauTarjanChildLimit, 1}; CheckResult result = check(R"( @@ -2194,7 +2199,7 @@ TEST_CASE_FIXTURE(Fixture, "instantiated_type_packs_must_have_a_non_null_scope") TEST_CASE_FIXTURE(Fixture, "inner_frees_become_generic_in_dcr") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -2224,7 +2229,7 @@ TEST_CASE_FIXTURE(Fixture, "function_exprs_are_generalized_at_signature_scope_no )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(requireType("foo")) == "((unknown) -> nil)?"); else { @@ -2243,7 +2248,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "param_1_and_2_both_takes_the_same_generic_bu local ret: number = foo(vec2, { x = 5 }) )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -2307,7 +2312,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "param_1_and_2_both_takes_the_same_generic_bu local z: boolean = f(5, "five") )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -2337,14 +2342,14 @@ TEST_CASE_FIXTURE(Fixture, "attempt_to_call_an_intersection_of_tables") if (DFFlag::LuauImproveNonFunctionCallError) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type { x: number } & { y: string }"); else CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type {| x: number |}"); } else { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(result.errors[0]), "Cannot call non-function { x: number } & { y: string }"); else CHECK_EQ(toString(result.errors[0]), "Cannot call non-function {| x: number |}"); @@ -2368,7 +2373,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "attempt_to_call_an_intersection_of_tables_wi TEST_CASE_FIXTURE(Fixture, "generic_packs_are_not_variadic") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local function apply(f: (a, b...) -> c..., x: a) @@ -2404,7 +2409,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors.at(0)) == "Type pack 'string' could not be converted into 'number'; at [0], string is not a subtype of number"); else CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0])); @@ -2429,7 +2434,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_after_num_or_str") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors.at(0)) == "Type pack 'string' could not be converted into 'number'; at [0], string is not a subtype of number"); else CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0])); @@ -2477,6 +2482,9 @@ a = function(a, b) return a + b end TEST_CASE_FIXTURE(BuiltinsFixture, "simple_unannotated_mutual_recursion") { + // CLI-117118 - TypeInferFunctions.simple_unannotated_mutual_recursion relies on unstable assertions to pass. + if (FFlag::LuauSolverV2) + return; CheckResult result = check(R"( function even(n) if n == 0 then @@ -2497,14 +2505,16 @@ function odd(n) end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(5, result); + // CLI-117117 Constraint solving is incomplete inTypeInferFunctions.simple_unannotated_mutual_recursion CHECK(get(result.errors[0])); - CHECK( - toString(result.errors[1]) == - "Type pack '*blocked-tp-1*' could not be converted into 'boolean'; type *blocked-tp-1*.tail() (*blocked-tp-1*) is not a subtype of boolean (boolean)" - ); + // This check is unstable between different machines and different runs of DCR because it depends on string equality between + // blocked type numbers, which is not guaranteed. + bool r = toString(result.errors[1]) == "Type pack '*blocked-tp-1*' could not be converted into 'boolean'; type *blocked-tp-1*.tail() " + "(*blocked-tp-1*) is not a subtype of boolean (boolean)"; + CHECK(r); CHECK( toString(result.errors[2]) == "Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub" @@ -2555,7 +2565,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_return_type") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( function fib(n) @@ -2573,7 +2583,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_arg_type") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( function fib(n, u) @@ -2592,7 +2602,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_arg_type_2") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; // Make sure the error types are cloned to module interface @@ -2645,7 +2655,7 @@ TEST_CASE_FIXTURE(Fixture, "bidirectional_checking_of_callback_property") LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { auto tm = get(result.errors[0]); REQUIRE(tm); @@ -2717,7 +2727,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_infer_overloaded_functions") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("(t1) -> () where t1 = { read FindFirstChild: (t1, string) -> (...unknown) }" == toString(requireType("getR6Attachments"))); else CHECK("(t1) -> () where t1 = {+ FindFirstChild: (t1, string) -> (a...) +}" == toString(requireType("getR6Attachments"))); @@ -2881,7 +2891,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // The new solver should ideally be able to do better here, but this is no worse than the old solver. diff --git a/Dependencies/luau/Luau/tests/TypeInfer.generics.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.generics.test.cpp index 8741da6a..ffd01f24 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.generics.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.generics.test.cpp @@ -7,10 +7,11 @@ #include "Fixture.h" +#include "ScopedFlags.h" #include "doctest.h" LUAU_FASTFLAG(LuauInstantiateInSubtyping); -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); using namespace Luau; @@ -70,7 +71,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "unions_and_generics") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("number | {number}", toString(requireType("res"))); else // in the old solver, this just totally falls apart CHECK_EQ("a", toString(requireType("res"))); @@ -142,6 +143,8 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes") TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( local t: { m: (number)->number } = { m = function(x:number) return x+1 end } local function id(x:a):a return x end @@ -196,7 +199,7 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions") TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_unannotated") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -218,7 +221,7 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_unannotated") TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_errors") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -256,8 +259,10 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_errors") } } -TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types") +TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_old_solver") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( type T = { id: (a) -> a } local x: T = { id = function(x:a):a return x end } @@ -267,8 +272,23 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types") LUAU_REQUIRE_NO_ERRORS(result); } +TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_new_solver") +{ + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + + CheckResult result = check(R"( + type T = { read id: (a) -> a } + local x: T = { id = function(x:a):a return x end } + local y: string = x.id("hi") + local z: number = x.id(37) + )"); + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_CASE_FIXTURE(Fixture, "generic_factories") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( type T = { id: (a) -> a } type Factory = { build: () -> T } @@ -290,6 +310,8 @@ TEST_CASE_FIXTURE(Fixture, "generic_factories") TEST_CASE_FIXTURE(Fixture, "factories_of_generics") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( type T = { id: (a) -> a } type Factory = { build: () -> T } @@ -445,7 +467,14 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_generic_types") local b: boolean = f(true) )"); - LUAU_REQUIRE_ERRORS(result); + if (FFlag::LuauSolverV2) + { + LUAU_REQUIRE_NO_ERRORS(result); + } + else + { + LUAU_REQUIRE_ERRORS(result); + } } TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types") @@ -461,7 +490,14 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types") local y: number = id(37) end )"); - LUAU_REQUIRE_ERRORS(result); + if (FFlag::LuauSolverV2) + { + LUAU_REQUIRE_NO_ERRORS(result); + } + else + { + LUAU_REQUIRE_ERRORS(result); + } } TEST_CASE_FIXTURE(Fixture, "dont_substitute_bound_types") @@ -613,7 +649,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_type_pack_parentheses") // This should really error, but the error from the old solver is wrong. // `a...` is a generic type pack, and we don't know that it will be non-empty, thus this code may not work. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -634,7 +670,7 @@ TEST_CASE_FIXTURE(Fixture, "better_mismatch_error_messages") SwappedGenericTypeParameter* fErr; SwappedGenericTypeParameter* gErr; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(3, result); // The first error here is an unknown symbol that is redundant with the `fErr`. @@ -737,17 +773,19 @@ return exports LUAU_REQUIRE_NO_ERRORS(result); } -TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names") +TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names_old_solver") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( -local function f(a: T, ...: U...) end + local function f(a: T, ...: U...) end -f(1, 2, 3) + f(1, 2, 3) )"); LUAU_REQUIRE_NO_ERRORS(result); - auto ty = findTypeAtPosition(Position(3, 0)); + auto ty = findTypeAtPosition(Position(3, 8)); REQUIRE(ty); ToStringOptions opts; opts.functionTypeArguments = true; @@ -756,6 +794,8 @@ f(1, 2, 3) TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_types") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( type C = () -> () type D = () -> () @@ -771,6 +811,8 @@ local d: D = c TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_pack") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( type C = () -> () type D = () -> () @@ -825,7 +867,7 @@ y.a.c = y LUAU_REQUIRE_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK( toString(result.errors.at(0)) == R"(Type '{ a: { c: nil, d: number }, b: number }' could not be converted into 'T'; type { a: { c: nil, d: number }, b: number }[read "a"][read "c"] (nil) is not exactly T[read "a"][read "c"][0] (T))" @@ -845,6 +887,8 @@ Type 'number' could not be converted into 'string' in an invariant context)"; TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification1") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( --!strict type Dispatcher = { @@ -863,6 +907,8 @@ local TheDispatcher: Dispatcher = { TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification2") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( --!strict type Dispatcher = { @@ -881,6 +927,8 @@ local TheDispatcher: Dispatcher = { TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification3") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( --!strict type Dispatcher = { @@ -899,6 +947,8 @@ local TheDispatcher: Dispatcher = { TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_few") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( function test(a: number) return 1 @@ -916,6 +966,8 @@ wrapper(test) TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_many") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( function test2(a: number, b: string) return 1 @@ -1112,7 +1164,7 @@ local a: Self )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(requireType("a")), "Table
"); else CHECK_EQ(toString(requireType("a")), "Table"); @@ -1131,7 +1183,7 @@ TEST_CASE_FIXTURE(Fixture, "no_stack_overflow_from_quantifying") std::optional t0 = lookupType("t0"); REQUIRE(t0); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("any", toString(*t0)); else CHECK_EQ("*error-type*", toString(*t0)); @@ -1151,7 +1203,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "infer_generic_function_function_argument") { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CheckResult result = check(R"( local function sum(x: a, y: a, f: (a, a) -> add) @@ -1208,7 +1260,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "infer_generic_function_function_argument_3") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) REQUIRE_EQ("{ c: number, s: number } | { c: number, s: number }", toString(requireType("r"))); else REQUIRE_EQ("{ c: number, s: number }", toString(requireType("r"))); @@ -1250,7 +1302,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions") CheckResult result; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { result = check(R"( local function sum(x: a, y: a, f: (a, a) -> a) return f(x, y) end @@ -1368,6 +1420,17 @@ TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics3") LUAU_REQUIRE_NO_ERRORS(result); } +TEST_CASE_FIXTURE(Fixture, "quantify_functions_with_no_generics") +{ + CheckResult result = check(R"( + function foo(f, x) + return f(x) + end + )"); + + CHECK("((a) -> (b...), a) -> (b...)" == toString(requireType("foo"))); +} + TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_generic") { CheckResult result = check(R"( @@ -1379,8 +1442,21 @@ TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_gen CHECK("((X) -> (a...), X) -> (a...)" == toString(requireType("foo"))); } +TEST_CASE_FIXTURE(Fixture, "no_extra_quantification_for_generic_functions") +{ + CheckResult result = check(R"( + function foo(f : (X) -> Y, x: X) + return f(x) + end + )"); + + CHECK("((X) -> Y, X) -> Y" == toString(requireType("foo"))); +} + TEST_CASE_FIXTURE(Fixture, "do_not_always_instantiate_generic_intersection_types") { + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + CheckResult result = check(R"( --!strict type Array = { [number]: T } @@ -1415,6 +1491,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "higher_rank_polymorphism_should_not_accept_instantiated_arguments") { ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, false}, {FFlag::LuauInstantiateInSubtyping, true}, }; @@ -1479,9 +1556,24 @@ TEST_CASE_FIXTURE(Fixture, "missing_generic_type_parameter") REQUIRE(get(result.errors[1])); } +TEST_CASE_FIXTURE(Fixture, "generic_implicit_explicit_name_clash") +{ + ScopedFastFlag _{FFlag::LuauSolverV2, true}; + + auto result = check(R"( + function apply(func, argument: a) + return func(argument) + end + )"); + + CHECK("((a) -> (b...), a) -> (b...)" == toString(requireType("apply"))); +} + TEST_CASE_FIXTURE(BuiltinsFixture, "generic_type_functions_work_in_subtyping") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; + + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( diff --git a/Dependencies/luau/Luau/tests/TypeInfer.intersectionTypes.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.intersectionTypes.test.cpp index baef9245..50e28505 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.intersectionTypes.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.intersectionTypes.test.cpp @@ -9,7 +9,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); TEST_SUITE_BEGIN("IntersectionTypes"); @@ -172,7 +172,7 @@ TEST_CASE_FIXTURE(Fixture, "index_on_an_intersection_type_with_property_guarante LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("(A & B) -> { y: number }" == toString(requireType("f"))); else CHECK("(A & B) -> {| y: number |} & {| y: number |}" == toString(requireType("f"))); @@ -191,7 +191,7 @@ TEST_CASE_FIXTURE(Fixture, "index_on_an_intersection_type_works_at_arbitrary_dep LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("(A & B) -> string", toString(requireType("f"))); else CHECK_EQ("(A & B) -> string & string", toString(requireType("f"))); @@ -210,7 +210,7 @@ TEST_CASE_FIXTURE(Fixture, "index_on_an_intersection_type_with_mixed_types") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("(A & B) -> never", toString(requireType("f"))); else CHECK_EQ("(A & B) -> number & string", toString(requireType("f"))); @@ -333,7 +333,7 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed") TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect") { ScopedFastFlag dcr{ - FFlag::DebugLuauDeferredConstraintResolution, false + FFlag::LuauSolverV2, false }; // CLI-116476 Subtyping between type alias and an equivalent but not named type isn't working. CheckResult result = check(R"( type X = { x: (number) -> number } @@ -348,7 +348,7 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ(toString(result.errors[0]), "Cannot add property 'z' to table 'X & Y'"); @@ -372,7 +372,7 @@ caused by: TEST_CASE_FIXTURE(Fixture, "table_write_sealed_indirect") { - ScopedFastFlag dcr{FFlag::DebugLuauDeferredConstraintResolution, false}; // CLI- + ScopedFastFlag dcr{FFlag::LuauSolverV2, false}; // CLI- // After normalization, previous 'table_intersection_write_sealed_indirect' is identical to this one CheckResult result = check(R"( type XY = { x: (number) -> number, y: (string) -> string } @@ -430,7 +430,7 @@ Type 'number' could not be converted into 'X')"; R"(Type 'number' could not be converted into 'X & Y & Z'; type number (number) is not a subtype of X & Y & Z[0] (X) type number (number) is not a subtype of X & Y & Z[1] (Y) type number (number) is not a subtype of X & Y & Z[2] (Z))"; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(dcrExprected, toString(result.errors[0])); else CHECK_EQ(expected, toString(result.errors[0])); @@ -450,7 +450,7 @@ end )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ( R"(Type pack 'X & Y & Z' could not be converted into 'number'; type X & Y & Z[0][0] (X) is not a subtype of number[0] (number) @@ -503,7 +503,7 @@ TEST_CASE_FIXTURE(Fixture, "intersect_bool_and_false") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ( R"(Type 'boolean & false' could not be converted into 'true'; type boolean & false[0] (boolean) is not a subtype of true (true) @@ -528,7 +528,7 @@ TEST_CASE_FIXTURE(Fixture, "intersect_false_and_bool_and_false") LUAU_REQUIRE_ERROR_COUNT(1, result); // TODO: odd stringification of `false & (boolean & false)`.) - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ( R"(Type 'boolean & false & false' could not be converted into 'true'; type boolean & false & false[0] (false) is not a subtype of true (true) type boolean & false & false[1] (boolean) is not a subtype of true (true) @@ -550,21 +550,21 @@ TEST_CASE_FIXTURE(Fixture, "intersect_saturate_overloaded_functions") local z : (number) -> number = x -- Not OK end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); const std::string expected1 = R"(Type - '(nil) -> nil' + '((number?) -> number?) & ((string?) -> string?)' could not be converted into - '((number?) -> number?) & ((string?) -> string?)'; type (nil) -> nil.arguments()[0] (nil) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[0].arguments()[0][0] (number) - type (nil) -> nil.arguments()[0] (nil) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0][0] (string))"; + '(nil) -> nil'; type ((number?) -> number?) & ((string?) -> string?)[0].returns()[0][0] (number) is not a subtype of (nil) -> nil.returns()[0] (nil) + type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][0] (string) is not a subtype of (nil) -> nil.returns()[0] (nil))"; const std::string expected2 = R"(Type - '(number) -> number' + '((number?) -> number?) & ((string?) -> string?)' could not be converted into - '((number?) -> number?) & ((string?) -> string?)'; type (number) -> number.arguments()[0] (number) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[0].arguments()[0][1] (nil) - type (number) -> number.arguments()[0] (number) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0][0] (string) - type (number) -> number.arguments()[0] (number) is not a supertype of ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0][1] (nil) - type (number) -> number.returns()[0] (number) is not a subtype of ((number?) -> number?) & ((string?) -> string?)[1].returns()[0] (string?))"; + '(number) -> number'; type ((number?) -> number?) & ((string?) -> string?)[0].returns()[0][1] (nil) is not a subtype of (number) -> number.returns()[0] (number) + type ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0] (string?) is not a supertype of (number) -> number.arguments()[0] (number) + type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][0] (string) is not a subtype of (number) -> number.returns()[0] (number) + type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][1] (nil) is not a subtype of (number) -> number.returns()[0] (number))"; CHECK_EQ(expected1, toString(result.errors[0])); CHECK_EQ(expected2, toString(result.errors[1])); } @@ -582,7 +582,7 @@ could not be converted into TEST_CASE_FIXTURE(Fixture, "union_saturate_overloaded_functions") { ScopedFastFlag dcr{ - FFlag::DebugLuauDeferredConstraintResolution, false + FFlag::LuauSolverV2, false }; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions CheckResult result = check(R"( function f(x: ((number) -> number) & ((string) -> string)) @@ -609,13 +609,12 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - const std::string expected = (FFlag::DebugLuauDeferredConstraintResolution) - ? "Type " - "'{ p: number?, q: number?, r: number? } & { p: number?, q: string? }'" - " could not be converted into " - "'{ p: nil }'; none of the intersection parts are compatible" - : - R"(Type + const std::string expected = + (FFlag::LuauSolverV2) + ? R"(Type '{ p: number?, q: number?, r: number? } & { p: number?, q: string? }' could not be converted into '{ p: nil }'; type { p: number?, q: number?, r: number? } & { p: number?, q: string? }[0][read "p"][0] (number) is not exactly { p: nil }[read "p"] (nil) + type { p: number?, q: number?, r: number? } & { p: number?, q: string? }[1][read "p"][0] (number) is not exactly { p: nil }[read "p"] (nil))" + : + R"(Type '{| p: number?, q: number?, r: number? |} & {| p: number?, q: string? |}' could not be converted into '{| p: nil |}'; none of the intersection parts are compatible)"; @@ -631,30 +630,20 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_top_properties") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { - LUAU_REQUIRE_ERROR_COUNT(2, result); - - CHECK_EQ( - toString(result.errors[0]), - "Type '{| p: number?, q: string? |}' could not be converted into '{| p: string?, q: number? |}'\n" - "caused by:\n" - " Property 'p' is not compatible. Type 'number?' could not be converted into 'string?'\n" - "caused by:\n" - " Not all union options are compatible. Type 'number' could not be converted into 'string?'\n" - "caused by:\n" - " None of the union options are compatible. For example: Type 'number' could not be converted into 'string' in an invariant context" - ); - + LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK_EQ( - toString(result.errors[1]), - "Type '{| p: number?, q: string? |}' could not be converted into '{| p: string?, q: number? |}'\n" - "caused by:\n" - " Property 'q' is not compatible. Type 'string?' could not be converted into 'number?'\n" - "caused by:\n" - " Not all union options are compatible. Type 'string' could not be converted into 'number?'\n" - "caused by:\n" - " None of the union options are compatible. For example: Type 'string' could not be converted into 'number' in an invariant context" + R"(Type + '{ p: number?, q: any } & { p: unknown, q: string? }' +could not be converted into + '{ p: string?, q: number? }'; type { p: number?, q: any } & { p: unknown, q: string? }[0][read "p"] (number?) is not exactly { p: string?, q: number? }[read "p"][0] (string) + type { p: number?, q: any } & { p: unknown, q: string? }[0][read "p"][0] (number) is not exactly { p: string?, q: number? }[read "p"] (string?) + type { p: number?, q: any } & { p: unknown, q: string? }[0][read "q"] (any) is not exactly { p: string?, q: number? }[read "q"] (number?) + type { p: number?, q: any } & { p: unknown, q: string? }[1][read "p"] (unknown) is not exactly { p: string?, q: number? }[read "p"] (string?) + type { p: number?, q: any } & { p: unknown, q: string? }[1][read "q"] (string?) is not exactly { p: string?, q: number? }[read "q"][0] (number) + type { p: number?, q: any } & { p: unknown, q: string? }[1][read "q"][0] (string) is not exactly { p: string?, q: number? }[read "q"] (number?))", + toString(result.errors[0]) ); } else @@ -689,18 +678,42 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_returning_intersections") end )"); - LUAU_REQUIRE_ERROR_COUNT(1, result); - const std::string expected = (FFlag::DebugLuauDeferredConstraintResolution) ? - R"(Type + if (FFlag::LuauSolverV2) + { + LUAU_REQUIRE_ERROR_COUNT(2, result); + CHECK_EQ( + R"(Type + '((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })' +could not be converted into + '(nil) -> { p: number, q: number, r: number }'; type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][0] ({ p: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }) + type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][1] ({ q: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }) + type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][0] ({ p: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }) + type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][1] ({ r: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }))", + toString(result.errors[0]) + ); + CHECK_EQ( + R"(Type '((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })' could not be converted into - '(number?) -> { p: number, q: number, r: number }'; none of the intersection parts are compatible)" - : - R"(Type + '(number?) -> { p: number, q: number, r: number }'; type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][0] ({ p: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }) + type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][1] ({ q: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }) + type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].arguments()[0] (string?) is not a supertype of (number?) -> { p: number, q: number, r: number }.arguments()[0][0] (number) + type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][0] ({ p: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }) + type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][1] ({ r: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }))", + toString(result.errors[1]) + ); + } + else + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK_EQ( + R"(Type '((number?) -> {| p: number |} & {| q: number |}) & ((string?) -> {| p: number |} & {| r: number |})' could not be converted into - '(number?) -> {| p: number, q: number, r: number |}'; none of the intersection parts are compatible)"; - CHECK_EQ(expected, toString(result.errors[0])); + '(number?) -> {| p: number, q: number, r: number |}'; none of the intersection parts are compatible)", + toString(result.errors[0]) + ); + } } TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic") @@ -713,13 +726,19 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic") end end )"); - - LUAU_REQUIRE_ERROR_COUNT(1, result); - const std::string expected = R"(Type + if (FFlag::LuauSolverV2) + { + LUAU_REQUIRE_ERROR_COUNT(0, result); + } + else + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + const std::string expected = R"(Type '((number?) -> a | number) & ((string?) -> a | string)' could not be converted into '(number?) -> a'; none of the intersection parts are compatible)"; - CHECK_EQ(expected, toString(result.errors[0])); + CHECK_EQ(expected, toString(result.errors[0])); + } } TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generics") @@ -733,12 +752,20 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generics") end )"); - LUAU_REQUIRE_ERROR_COUNT(1, result); - const std::string expected = R"(Type + + if (FFlag::LuauSolverV2) + { + LUAU_REQUIRE_NO_ERRORS(result); + } + else + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + const std::string expected = R"(Type '((a?) -> a | b) & ((c?) -> b | c)' could not be converted into '(a?) -> (a & c) | b'; none of the intersection parts are compatible)"; - CHECK_EQ(expected, toString(result.errors[0])); + CHECK_EQ(expected, toString(result.errors[0])); + } } TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic_packs") @@ -751,19 +778,41 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic_packs") end end )"); - - LUAU_REQUIRE_ERROR_COUNT(1, result); - const std::string expected = R"(Type + if (FFlag::LuauSolverV2) + { + LUAU_REQUIRE_ERROR_COUNT(2, result); + CHECK_EQ( + R"(Type + '((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))' +could not be converted into + '(nil, a...) -> (nil, b...)'; type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[0].returns()[0][0] (number) is not a subtype of (nil, a...) -> (nil, b...).returns()[0] (nil) + type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[1].returns()[0][0] (string) is not a subtype of (nil, a...) -> (nil, b...).returns()[0] (nil))", + toString(result.errors[0]) + ); + CHECK_EQ( + R"(Type + '((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))' +could not be converted into + '(nil, b...) -> (nil, a...)'; type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[0].returns()[0][0] (number) is not a subtype of (nil, b...) -> (nil, a...).returns()[0] (nil) + type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[1].returns()[0][0] (string) is not a subtype of (nil, b...) -> (nil, a...).returns()[0] (nil))", + toString(result.errors[1]) + ); + } + else + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + const std::string expected = R"(Type '((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))' could not be converted into '(nil, b...) -> (nil, a...)'; none of the intersection parts are compatible)"; - CHECK_EQ(expected, toString(result.errors[0])); + CHECK_EQ(expected, toString(result.errors[0])); + } } TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_result") { ScopedFastFlag dcr{ - FFlag::DebugLuauDeferredConstraintResolution, false + FFlag::LuauSolverV2, false }; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions CheckResult result = check(R"( function f() @@ -785,7 +834,7 @@ could not be converted into TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_arguments") { ScopedFastFlag dcr{ - FFlag::DebugLuauDeferredConstraintResolution, false + FFlag::LuauSolverV2, false }; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions CheckResult result = check(R"( function f() @@ -815,7 +864,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_result") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ( @@ -858,18 +907,40 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_arguments") end )"); - LUAU_REQUIRE_ERROR_COUNT(1, result); - const std::string expected = R"(Type + if (FFlag::LuauSolverV2) + { + LUAU_REQUIRE_ERROR_COUNT(2, result); + const std::string expected1 = R"(Type + '((never) -> string?) & ((number) -> number?)' +could not be converted into + '(never) -> nil'; type ((never) -> string?) & ((number) -> number?)[0].returns()[0][0] (number) is not a subtype of (never) -> nil.returns()[0] (nil) + type ((never) -> string?) & ((number) -> number?)[1].returns()[0][0] (string) is not a subtype of (never) -> nil.returns()[0] (nil))"; + const std::string expected2 = R"(Type + '((never) -> string?) & ((number) -> number?)' +could not be converted into + '(number?) -> nil'; type ((never) -> string?) & ((number) -> number?)[0].arguments()[0] (number) is not a supertype of (number?) -> nil.arguments()[0][1] (nil) + type ((never) -> string?) & ((number) -> number?)[0].returns()[0][0] (number) is not a subtype of (number?) -> nil.returns()[0] (nil) + type ((never) -> string?) & ((number) -> number?)[1].arguments()[0] (never) is not a supertype of (number?) -> nil.arguments()[0][0] (number) + type ((never) -> string?) & ((number) -> number?)[1].arguments()[0] (never) is not a supertype of (number?) -> nil.arguments()[0][1] (nil) + type ((never) -> string?) & ((number) -> number?)[1].returns()[0][0] (string) is not a subtype of (number?) -> nil.returns()[0] (nil))"; + CHECK_EQ(expected1, toString(result.errors[0])); + CHECK_EQ(expected2, toString(result.errors[1])); + } + else + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + const std::string expected = R"(Type '((never) -> string?) & ((number) -> number?)' could not be converted into '(number?) -> nil'; none of the intersection parts are compatible)"; - CHECK_EQ(expected, toString(result.errors[0])); + CHECK_EQ(expected, toString(result.errors[0])); + } } TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_overlapping_results_and_variadics") { ScopedFastFlag dcr{ - FFlag::DebugLuauDeferredConstraintResolution, false + FFlag::LuauSolverV2, false }; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions CheckResult result = check(R"( function f(x : ((string?) -> (string | number)) & ((number?) -> ...number)) @@ -897,7 +968,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_1") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); } @@ -922,7 +993,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_2") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); } @@ -947,7 +1018,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_3") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); } @@ -974,7 +1045,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_4") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ( R"(Type @@ -999,7 +1070,11 @@ could not be converted into TEST_CASE_FIXTURE(BuiltinsFixture, "intersect_metatables") { - if (FFlag::DebugLuauDeferredConstraintResolution) + // CLI-117121 - Intersection of types are not compatible with the equivalent alias + if (FFlag::LuauSolverV2) + return; + + if (FFlag::LuauSolverV2) { CheckResult result = check(R"( function f(a: string?, b: string?) @@ -1089,7 +1164,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "intersect_metatables_with_properties") TEST_CASE_FIXTURE(BuiltinsFixture, "intersect_metatable_with_table") { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CheckResult result = check(R"( local x = setmetatable({ a = 5 }, { p = 5 }) @@ -1155,7 +1230,7 @@ TEST_CASE_FIXTURE(Fixture, "CLI-44817") TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_intersection_types") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1165,15 +1240,14 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_intersection_types") end )"); - LUAU_REQUIRE_NO_ERRORS(result); + LUAU_REQUIRE_ERROR_COUNT(3, result); - // TODO? We do not simplify types from explicit annotations. - CHECK_EQ("({| x: number |} & {| x: string |}) -> {| x: number |} & {| x: string |}", toString(requireType("f"))); + CHECK_EQ("(never) -> { x: number } & { x: string }", toString(requireType("f"))); } TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_intersection_types_2") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1219,7 +1293,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_property_table_intersection_2") TEST_CASE_FIXTURE(Fixture, "cli_80596_simplify_degenerate_intersections") { - ScopedFastFlag dcr{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag dcr{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type A = { @@ -1242,7 +1316,7 @@ TEST_CASE_FIXTURE(Fixture, "cli_80596_simplify_degenerate_intersections") TEST_CASE_FIXTURE(Fixture, "cli_80596_simplify_more_realistic_intersections") { - ScopedFastFlag dcr{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag dcr{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type A = { diff --git a/Dependencies/luau/Luau/tests/TypeInfer.loops.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.loops.test.cpp index 98063d0f..de79654b 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.loops.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.loops.test.cpp @@ -14,7 +14,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauOkWithIteratingOverTableProperties) LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError) @@ -32,7 +32,7 @@ TEST_CASE_FIXTURE(Fixture, "for_loop") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // Luau cannot see that the loop must always run at least once, so we // think that q could be nil. @@ -45,10 +45,10 @@ TEST_CASE_FIXTURE(Fixture, "for_loop") TEST_CASE_FIXTURE(BuiltinsFixture, "iteration_no_table_passed") { // This test may block CI if forced to run outside of DCR. - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type Iterable = typeof(setmetatable( @@ -72,7 +72,7 @@ for a, b in t do end TEST_CASE_FIXTURE(BuiltinsFixture, "iteration_regression_issue_69967") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -93,7 +93,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "iteration_regression_issue_69967") TEST_CASE_FIXTURE(BuiltinsFixture, "iteration_regression_issue_69967_alt") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -114,7 +114,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "iteration_regression_issue_69967_alt") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // It's possible for the loop body to execute 0 times. CHECK("number?" == toString(requireType("x"))); @@ -140,7 +140,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("number?" == toString(requireType("n"))); CHECK("string?" == toString(requireType("s"))); @@ -155,7 +155,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop") TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next") { // CLI-116494 The generics K and V are leaking out of the next() function somehow. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local n @@ -222,7 +222,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_with_just_one_iterator_is_ok") TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_zero_iterators_dcr") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function no_iter() end @@ -267,7 +267,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error") LUAU_REQUIRE_ERROR_COUNT(2, result); TypeId p = requireType("p"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("*error-type*?", toString(p)); else CHECK_EQ("*error-type*", toString(p)); @@ -276,7 +276,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error") TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function") { // We report a spuriouus duplicate error here. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local bad_iter = 5 @@ -293,7 +293,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function") TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_factory_not_returning_the_right_amount_of_values") { // Spurious duplicate errors - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function hasDivisors(value: number, table) @@ -345,7 +345,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_factory_not_returning_t TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_iterator_requiring_args_but_none_given") { // CLI-116496 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function prime_iter(state, index) @@ -419,7 +419,7 @@ TEST_CASE_FIXTURE(Fixture, "while_loop") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("number?" == toString(requireType("i"))); else CHECK_EQ(*builtinTypes->numberType, *requireType("i")); @@ -436,7 +436,7 @@ TEST_CASE_FIXTURE(Fixture, "repeat_loop") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("string?" == toString(requireType("i"))); else CHECK_EQ(*builtinTypes->stringType, *requireType("i")); @@ -478,7 +478,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "varlist_declared_by_for_in_loop_should_be_fr end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); auto err = get(result.errors[0]); @@ -535,7 +535,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "properly_infer_iteratee_is_a_free_table") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // In the new solver, we infer iter: unknown and so we warn on use of its properties. LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -585,7 +585,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "ipairs_produces_integral_indices") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("number?" == toString(requireType("key"))); else REQUIRE_EQ("number", toString(requireType("key"))); @@ -696,7 +696,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "unreachable_code_after_infinite_loop") TEST_CASE_FIXTURE(BuiltinsFixture, "loop_typecheck_crash_on_empty_optional") { // CLI-116498 Sometimes you can iterate over tables with no indexers. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; ScopedFastFlag sff{FFlag::LuauOkWithIteratingOverTableProperties, true}; @@ -753,7 +753,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_basic") // The old solver just infers the wrong type here. // The right type for `key` is `number?` - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { TypeId keyTy = requireType("key"); CHECK("number?" == toString(keyTy)); @@ -765,7 +765,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_basic") TEST_CASE_FIXTURE(Fixture, "loop_iter_trailing_nil") { // CLI-116498 Sometimes you can iterate over tables with no indexers. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local t: {string} = {} @@ -783,7 +783,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer_strict") { // CLI-116498 Sometimes you can iterate over tables with no indexers. ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, {FFlag::LuauOkWithIteratingOverTableProperties, true} }; @@ -810,7 +810,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer_nonstrict") TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_nil") { // CLI-116499 Free types persisting until typechecking time. - if (1 || !FFlag::DebugLuauDeferredConstraintResolution) + if (1 || !FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -826,7 +826,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_nil") TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_not_enough_returns") { // CLI-116500 - if (1 || !FFlag::DebugLuauDeferredConstraintResolution) + if (1 || !FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -848,7 +848,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_not_enough_returns") TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_ok") { // CLI-116500 - if (1 || !FFlag::DebugLuauDeferredConstraintResolution) + if (1 || !FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -865,7 +865,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_ok") TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_ok_with_inference") { // CLI-116500 - if (1 || !FFlag::DebugLuauDeferredConstraintResolution) + if (1 || !FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -916,7 +916,7 @@ TEST_CASE_FIXTURE(Fixture, "for_loop_lower_bound_is_string_3") TEST_CASE_FIXTURE(BuiltinsFixture, "cli_68448_iterators_need_not_accept_nil") { // CLI-116500 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1051,7 +1051,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_fragmented_keys") TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_xpath_candidates") { // CLI-116500 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1071,7 +1071,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_xpath_candidates") TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_on_never_gives_never") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1084,7 +1084,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_on_never_gives_never") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("never?" == toString(requireType("ans"))); // CLI-114134 egraph simplification. Should just be nil. else CHECK(toString(requireType("ans")) == "never"); @@ -1093,7 +1093,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_on_never_gives_never") TEST_CASE_FIXTURE(BuiltinsFixture, "iterate_over_properties") { // CLI-116498 - Sometimes you can iterate over tables with no indexer. - ScopedFastFlag sff0{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff0{FFlag::LuauSolverV2, false}; ScopedFastFlag sff{FFlag::LuauOkWithIteratingOverTableProperties, true}; @@ -1151,7 +1151,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "pairs_should_not_retroactively_add_an_indexe print(prices.wwwww) )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // We regress a little here: The old solver would typecheck the first // access to prices.wwwww on a table that had no indexer, and the second @@ -1186,7 +1186,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "iterate_array_of_singletons") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else LUAU_REQUIRE_ERRORS(result); @@ -1210,7 +1210,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "iter_mm_results_are_lvalue") TEST_CASE_FIXTURE(BuiltinsFixture, "forin_metatable_no_iter_mm") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local t = setmetatable({1, 2, 3}, {}) @@ -1228,7 +1228,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "forin_metatable_no_iter_mm") TEST_CASE_FIXTURE(BuiltinsFixture, "forin_metatable_iter_mm") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type Iterable = typeof(setmetatable({}, {} :: { diff --git a/Dependencies/luau/Luau/tests/TypeInfer.modules.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.modules.test.cpp index 6719f158..42f1229f 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.modules.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.modules.test.cpp @@ -11,7 +11,7 @@ #include "doctest.h" LUAU_FASTFLAG(LuauInstantiateInSubtyping) -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) using namespace Luau; @@ -56,7 +56,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require") return {hooty=hooty} )"; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { fileResolver.source["game/B"] = R"( local Hooty = require(game.A) @@ -237,7 +237,7 @@ local tbl: string = require(game.A) CheckResult result = frontend.check("game/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("Type '{ def: number }' could not be converted into 'string'", toString(result.errors[0])); else CHECK_EQ("Type '{| def: number |}' could not be converted into 'string'", toString(result.errors[0])); @@ -424,7 +424,7 @@ local b: B.T = a CheckResult result = frontend.check("game/C"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string"); else { @@ -465,7 +465,7 @@ local b: B.T = a CheckResult result = frontend.check("game/D"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors.at(0)) == "Type 'T' could not be converted into 'T'; at [read \"x\"], number is not exactly string"); else { diff --git a/Dependencies/luau/Luau/tests/TypeInfer.negations.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.negations.test.cpp index af73607a..a21751ec 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.negations.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.negations.test.cpp @@ -50,6 +50,9 @@ TEST_CASE_FIXTURE(NegationFixture, "string_is_not_a_subtype_of_negated_string") TEST_CASE_FIXTURE(Fixture, "cofinite_strings_can_be_compared_for_equality") { + // CLI-117082 Cofinite strings cannot be compared for equality because normalization produces a large type with cycles + if (FFlag::LuauSolverV2) + return; CheckResult result = check(R"( function f(e) if e == 'strictEqual' then diff --git a/Dependencies/luau/Luau/tests/TypeInfer.oop.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.oop.test.cpp index 29935a08..3ccc04c1 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.oop.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.oop.test.cpp @@ -12,14 +12,14 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); TEST_SUITE_BEGIN("TypeInferOOP"); TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon") { // CLI-116571 method calls are missing arity checking? - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local someTable = {} @@ -37,7 +37,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defi TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2") { // CLI-116571 method calls are missing arity checking? - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local someTable = {} @@ -144,7 +144,7 @@ TEST_CASE_FIXTURE(Fixture, "inferring_hundreds_of_self_calls_should_not_suffocat )"); ModulePtr module = getMainModule(); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_GE(80, module->internalTypes.types.size()); else CHECK_GE(50, module->internalTypes.types.size()); @@ -346,7 +346,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "augmenting_an_unsealed_table_with_a_metatabl end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("{ @metatable { number: number }, { method: (unknown) -> string } }" == toString(requireType("B"), {true})); else CHECK("{ @metatable { number: number }, { method: (a) -> string } }" == toString(requireType("B"), {true})); @@ -505,7 +505,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "promise_type_error_too_complex" * doctest::t TEST_CASE_FIXTURE(Fixture, "method_should_not_create_cyclic_type") { - ScopedFastFlag sff(FFlag::DebugLuauDeferredConstraintResolution, true); + ScopedFastFlag sff(FFlag::LuauSolverV2, true); CheckResult result = check(R"( local Component = {} diff --git a/Dependencies/luau/Luau/tests/TypeInfer.operators.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.operators.test.cpp index e5514db0..d8bf9152 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.operators.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.operators.test.cpp @@ -16,7 +16,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauRemoveBadRelationalOperatorWarning) TEST_SUITE_BEGIN("TypeInferOperators"); @@ -194,7 +194,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_overloaded_multiply_that_is_an_int CHECK("Vec3" == toString(requireType("c"))); CHECK("Vec3" == toString(requireType("d"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("mul" == toString(requireType("e"))); else CHECK_EQ("Vec3", toString(requireType("e"))); @@ -232,7 +232,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_overloaded_multiply_that_is_an_int CHECK("Vec3" == toString(requireType("c"))); CHECK("Vec3" == toString(requireType("d"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("mul" == toString(requireType("e"))); else CHECK_EQ("Vec3", toString(requireType("e"))); @@ -270,7 +270,7 @@ TEST_CASE_FIXTURE(Fixture, "cannot_indirectly_compare_types_that_do_not_have_a_m LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { UninhabitedTypeFunction* utf = get(result.errors[0]); REQUIRE(utf); @@ -300,7 +300,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cannot_indirectly_compare_types_that_do_not_ LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { UninhabitedTypeFunction* utf = get(result.errors[0]); REQUIRE(utf); @@ -404,7 +404,7 @@ TEST_CASE_FIXTURE(Fixture, "compound_assign_mismatch_result") s += 10 )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK_EQ(result.errors[0], (TypeError{Location{{2, 8}, {2, 9}}, TypeMismatch{builtinTypes->numberType, builtinTypes->stringType}})); @@ -441,7 +441,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "compound_assign_metatable") TEST_CASE_FIXTURE(BuiltinsFixture, "compound_assign_metatable_with_changing_return_type") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( --!strict @@ -567,7 +567,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus") CHECK_EQ("string", toString(requireType("a"))); CHECK_EQ("number", toString(requireType("b"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); @@ -607,7 +607,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error") local a = -foo )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); @@ -631,7 +631,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error") TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_len_error") { // CLI-116463 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -702,7 +702,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "disallow_string_and_types_without_metatables LUAU_REQUIRE_ERROR_COUNT(3, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK(get(result.errors[0])); CHECK(Location{{2, 18}, {2, 30}} == result.errors[0].location); @@ -720,7 +720,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "disallow_string_and_types_without_metatables GenericError* gen1 = get(result.errors[1]); REQUIRE(gen1); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(gen1->message, "Operator + is not applicable for '{ value: number }' and 'number' because neither type has a metatable"); else CHECK_EQ(gen1->message, "Binary operator '+' not supported by types 'foo' and 'number'"); @@ -753,7 +753,7 @@ TEST_CASE_FIXTURE(Fixture, "concat_op_on_free_lhs_and_string_rhs") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK("(a) -> concat" == toString(requireType("f"))); @@ -775,7 +775,7 @@ TEST_CASE_FIXTURE(Fixture, "concat_op_on_string_lhs_and_free_rhs") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("(a) -> concat" == toString(requireType("f"))); else CHECK_EQ("(string) -> string", toString(requireType("f"))); @@ -794,7 +794,7 @@ TEST_CASE_FIXTURE(Fixture, "strict_binary_op_where_lhs_unknown") CheckResult result = check(src); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(ops.size(), result); CHECK_EQ( @@ -821,7 +821,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "and_binexps_dont_unify") )"); // This infers a type for `t` of `{unknown}`, and so it makes sense that `t[1].test` would error. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_ERROR_COUNT(1, result); else LUAU_REQUIRE_NO_ERRORS(result); @@ -837,7 +837,7 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { UninhabitedTypeFunction* utf = get(result.errors[0]); REQUIRE(utf); @@ -860,7 +860,7 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato )"); // If DCR is off and the flag to remove this check in the old solver is on, the expected behavior is no errors. - if (!FFlag::DebugLuauDeferredConstraintResolution && FFlag::LuauRemoveBadRelationalOperatorWarning) + if (!FFlag::LuauSolverV2 && FFlag::LuauRemoveBadRelationalOperatorWarning) { LUAU_REQUIRE_NO_ERRORS(result); return; @@ -868,7 +868,7 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { UninhabitedTypeFunction* utf = get(result.errors[0]); REQUIRE(utf); @@ -885,7 +885,7 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato TEST_CASE_FIXTURE(Fixture, "cli_38355_recursive_union") { // There's an extra spurious warning here when the new solver is enabled. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -901,7 +901,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "UnknownGlobalCompoundAssign") { // In non-strict mode, global definition is still allowed { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) { CheckResult result = check(R"( --!nonstrict @@ -928,7 +928,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "UnknownGlobalCompoundAssign") // In non-strict mode, compound assignment is not a definition, it's a modification { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) { CheckResult result = check(R"( --!nonstrict @@ -1047,7 +1047,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_any_in_all_modes_when_lhs_is_unknown") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK(toString(requireType("f")) == "(a, b) -> add"); @@ -1079,7 +1079,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_for_generic_subtraction") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK(toString(requireType("f")) == "(a, b) -> sub"); @@ -1099,7 +1099,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_for_generic_multiplication") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK(toString(requireType("f")) == "(a, b) -> mul"); @@ -1119,7 +1119,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_for_generic_division") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK(toString(requireType("f")) == "(a, b) -> div"); @@ -1139,7 +1139,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_for_generic_floor_division") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK(toString(requireType("f")) == "(a, b) -> idiv"); @@ -1159,7 +1159,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_for_generic_exponentiation") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK(toString(requireType("f")) == "(a, b) -> pow"); @@ -1179,7 +1179,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_for_generic_modulo") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK(toString(requireType("f")) == "(a, b) -> mod"); @@ -1199,7 +1199,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_for_generic_concat") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK(toString(requireType("f")) == "(a, b) -> concat"); @@ -1294,7 +1294,7 @@ TEST_CASE_FIXTURE(Fixture, "unrelated_primitives_cannot_be_compared") TEST_CASE_FIXTURE(BuiltinsFixture, "mm_comparisons_must_return_a_boolean") { // CLI-115687 - if (1 || !FFlag::DebugLuauDeferredConstraintResolution) + if (1 || !FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1341,12 +1341,12 @@ local w = c and 1 CHECK("number?" == toString(requireType("x"))); CHECK("number" == toString(requireType("y"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("false | number" == toString(requireType("z"))); else CHECK("boolean | number" == toString(requireType("z"))); // 'false' widened to boolean - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("number?" == toString(requireType("w"))); else CHECK("(boolean | number)?" == toString(requireType("w"))); @@ -1372,7 +1372,7 @@ local f1 = f or 'f' CHECK("number | string" == toString(requireType("a1"))); CHECK("number" == toString(requireType("b1"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("string | true" == toString(requireType("c1"))); CHECK("string | true" == toString(requireType("d1"))); @@ -1426,7 +1426,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_is_array_simplified") TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_is_array") { // CLI-116480 Subtyping bug: table should probably be a subtype of {[unknown]: unknown} - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -1524,7 +1524,7 @@ return startsWith TEST_CASE_FIXTURE(Fixture, "add_type_function_works") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1584,7 +1584,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "compare_singleton_string_to_string") TEST_CASE_FIXTURE(BuiltinsFixture, "no_infinite_expansion_of_free_type" * doctest::timeout(1.0)) { - ScopedFastFlag sff(FFlag::DebugLuauDeferredConstraintResolution, true); + ScopedFastFlag sff(FFlag::LuauSolverV2, true); check(R"( local tooltip = {} diff --git a/Dependencies/luau/Luau/tests/TypeInfer.primitives.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.primitives.test.cpp index 0f6d6132..c3cce9df 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.primitives.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.primitives.test.cpp @@ -84,7 +84,7 @@ TEST_CASE_FIXTURE(Fixture, "check_methods_of_number") LUAU_REQUIRE_ERROR_COUNT(2, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("Expected type table, got 'number' instead" == toString(result.errors[0])); CHECK("Type 'number' could not be converted into 'string'" == toString(result.errors[1])); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.provisional.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.provisional.test.cpp index 9107c6f4..514c31c8 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.provisional.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.provisional.test.cpp @@ -10,7 +10,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTINT(LuauNormalizeCacheLimit); LUAU_FASTINT(LuauTarjanChildLimit); LUAU_FASTINT(LuauTypeInferIterationLimit); @@ -65,7 +65,7 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") end )"; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(expectedWithNewSolver, decorateWithTypes(code)); else CHECK_EQ(expected, decorateWithTypes(code)); @@ -73,7 +73,7 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") TEST_CASE_FIXTURE(BuiltinsFixture, "luau-polyfill.Array.filter") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; // This test exercises the fact that we should reduce sealed/unsealed/free tables // res is a unsealed table with type {((T & ~nil)?) & any} @@ -172,7 +172,7 @@ TEST_CASE_FIXTURE(Fixture, "it_should_be_agnostic_of_actual_size") // For now, infer it as just a free table. TEST_CASE_FIXTURE(BuiltinsFixture, "setmetatable_constrains_free_type_into_free_table") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local a = {} @@ -192,7 +192,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "setmetatable_constrains_free_type_into_free_ // Luau currently doesn't yet know how to allow assignments when the binding was refined. TEST_CASE_FIXTURE(Fixture, "while_body_are_also_refined") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Node = { value: T, child: Node? } @@ -217,7 +217,7 @@ TEST_CASE_FIXTURE(Fixture, "while_body_are_also_refined") // We should be type checking the metamethod at the call site of setmetatable. TEST_CASE_FIXTURE(BuiltinsFixture, "error_on_eq_metamethod_returning_a_type_other_than_boolean") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local tab = {a = 1} @@ -277,7 +277,7 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_from_x_not_equal_to_nil") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("{ x: string, y: number }", toString(requireTypeAtPosition({5, 28}))); CHECK_EQ("{ x: nil, y: nil }", toString(requireTypeAtPosition({7, 28}))); @@ -359,7 +359,7 @@ TEST_CASE_FIXTURE(Fixture, "do_not_ice_when_trying_to_pick_first_of_generic_type LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("() -> ()" == toString(requireType("f"))); CHECK("() -> ()" == toString(requireType("g"))); @@ -382,7 +382,7 @@ TEST_CASE_FIXTURE(Fixture, "specialization_binds_with_prototypes_too_early") local s2s: (string) -> string = id )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else LUAU_REQUIRE_ERRORS(result); // Should not have any errors. @@ -393,7 +393,7 @@ TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_type_pack") ScopedFastFlag sff[] = { // I'm not sure why this is broken without DCR, but it seems to be fixed // when DCR is enabled. - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; CheckResult result = check(R"( @@ -477,7 +477,7 @@ TEST_CASE_FIXTURE(Fixture, "free_is_not_bound_to_any") TEST_CASE_FIXTURE(Fixture, "dcr_can_partially_dispatch_a_constraint") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; CheckResult result = check(R"( @@ -509,7 +509,7 @@ TEST_CASE_FIXTURE(Fixture, "dcr_can_partially_dispatch_a_constraint") // to be solved later. This should be faster and theoretically less prone // to cyclic constraint dependencies. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("(unknown, number) -> ()" == toString(requireType("prime_iter"))); else CHECK("(a, number) -> ()" == toString(requireType("prime_iter"))); @@ -517,7 +517,7 @@ TEST_CASE_FIXTURE(Fixture, "dcr_can_partially_dispatch_a_constraint") TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; TypeArena arena; TypeId nilType = builtinTypes->nilType; @@ -535,7 +535,7 @@ TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together") Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant}; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) u.enableNewSolver(); u.tryUnify(option1, option2); @@ -553,7 +553,7 @@ TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together") TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_zero_iterators") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function no_iter() end @@ -600,7 +600,7 @@ return wrapStrictTable(Constants, "Constants") std::optional result = first(m->returnType); REQUIRE(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("unknown", toString(*result)); else CHECK_MESSAGE(get(*result), *result); @@ -643,7 +643,7 @@ return wrapStrictTable(Constants, "Constants") std::optional result = first(m->returnType); REQUIRE(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("unknown" == toString(*result)); else CHECK("any" == toString(*result)); @@ -829,7 +829,7 @@ TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_ty end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); // This is wrong. We should be rejecting this assignment. else { @@ -847,7 +847,7 @@ Type 'number?' could not be converted into 'number' in an invariant context)"; TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_with_a_singleton_argument") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function foo(t, x) @@ -864,7 +864,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_with_a_singleton_argument") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{string}", toString(requireType("t"))); else { @@ -904,7 +904,7 @@ TEST_CASE_FIXTURE(Fixture, "expected_type_should_be_a_helpful_deduction_guide_fo local x: Ref = useRef(nil) )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // This bug is fixed in the new solver. LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -921,7 +921,7 @@ TEST_CASE_FIXTURE(Fixture, "expected_type_should_be_a_helpful_deduction_guide_fo TEST_CASE_FIXTURE(Fixture, "floating_generics_should_not_be_allowed") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local assign : (target: T, source0: U?, source1: V?, source2: W?, ...any) -> T & U & V & W = (nil :: any) @@ -945,7 +945,7 @@ TEST_CASE_FIXTURE(Fixture, "floating_generics_should_not_be_allowed") TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; TypeArena arena; TypeId nilType = builtinTypes->nilType; @@ -963,7 +963,7 @@ TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together") Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant}; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) u.enableNewSolver(); u.tryUnify(option1, option2); @@ -992,7 +992,7 @@ TEST_CASE_FIXTURE(Fixture, "unify_more_complex_unions_that_include_nil") TEST_CASE_FIXTURE(Fixture, "optional_class_instances_are_invariant_old_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; createSomeClasses(&frontend); @@ -1010,7 +1010,7 @@ TEST_CASE_FIXTURE(Fixture, "optional_class_instances_are_invariant_old_solver") TEST_CASE_FIXTURE(Fixture, "optional_class_instances_are_invariant_new_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; createSomeClasses(&frontend); @@ -1076,7 +1076,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "table_unification_infinite_recursion") { // The new solver doesn't recurse as heavily in this situation. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; #if defined(_NOOPT) || defined(_DEBUG) ScopedFastInt LuauTypeInferRecursionLimit{FInt::LuauTypeInferRecursionLimit, 100}; @@ -1113,7 +1113,7 @@ tbl:f3() TEST_CASE_FIXTURE(BuiltinsFixture, "normalization_limit_in_unify_with_any") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; // With default limit, this test will take 10 seconds in NoOpt @@ -1148,7 +1148,7 @@ foo(1 :: any) TEST_CASE_FIXTURE(Fixture, "luau_roact_useState_nilable_state_1") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type Dispatch = (A) -> () @@ -1173,7 +1173,7 @@ TEST_CASE_FIXTURE(Fixture, "luau_roact_useState_nilable_state_1") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else { @@ -1189,7 +1189,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_roact_useState_minimization") { // We don't expect this test to work on the old solver, but it also does not yet work on the new solver. // So, we can't just put a scoped fast flag here, or it would block CI. - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1307,12 +1307,10 @@ TEST_CASE_FIXTURE(Fixture, "we_cannot_infer_functions_that_return_inconsistently #else // This is what actually happens right now. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_CHECK_ERROR_COUNT(2, result); - - // The second argument should be unknown. CLI-111111 - CHECK("({T}, 'b) -> number" == toString(requireType("find_first"))); + CHECK("({T}, unknown) -> number" == toString(requireType("find_first"))); } else { diff --git a/Dependencies/luau/Luau/tests/TypeInfer.refinements.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.refinements.test.cpp index 37c5e8d2..9c26f165 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.refinements.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.refinements.test.cpp @@ -7,7 +7,7 @@ #include "doctest.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) using namespace Luau; @@ -255,7 +255,7 @@ TEST_CASE_FIXTURE(Fixture, "a_and_b_or_a_and_c") CHECK_EQ("string", toString(requireTypeAtPosition({3, 28}))); CHECK_EQ("number?", toString(requireTypeAtPosition({4, 28}))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("boolean", toString(requireTypeAtPosition({5, 28}))); else CHECK_EQ("true", toString(requireTypeAtPosition({5, 28}))); // oh no! :( @@ -278,7 +278,7 @@ TEST_CASE_FIXTURE(Fixture, "type_assertion_expr_carry_its_constraints") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("number?", toString(requireTypeAtPosition({3, 26}))); CHECK_EQ("string?", toString(requireTypeAtPosition({4, 26}))); @@ -307,7 +307,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typeguard_in_if_condition_position") LUAU_REQUIRE_NO_ERRORS(result); // DCR changes refinements to preserve error suppression. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("*error-type* | number", toString(requireTypeAtPosition({3, 26}))); else CHECK_EQ("number", toString(requireTypeAtPosition({3, 26}))); @@ -326,7 +326,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typeguard_in_assert_position") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("(a) -> a & number" == toString(requireType("f"))); else CHECK("(a) -> number" == toString(requireType("f"))); @@ -346,7 +346,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table_then_test_a_prop") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else { @@ -376,7 +376,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table_then_test_a_nested_p end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); const UnknownProperty* up = get(result.errors[0]); @@ -410,7 +410,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table_then_test_a_tested_n end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else { @@ -486,7 +486,7 @@ TEST_CASE_FIXTURE(Fixture, "truthy_constraint_on_properties") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-115281 - Types produced by refinements don't always get simplified CHECK("{ x: number? } & { x: ~(false?) }" == toString(requireTypeAtPosition({4, 23}))); @@ -573,7 +573,7 @@ TEST_CASE_FIXTURE(Fixture, "term_is_equal_to_an_lvalue") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ(toString(requireTypeAtPosition({3, 28})), R"("hello")"); // a == "hello" CHECK_EQ(toString(requireTypeAtPosition({5, 28})), R"(((string & ~"hello") | number)?)"); // a ~= "hello" @@ -600,7 +600,7 @@ TEST_CASE_FIXTURE(Fixture, "lvalue_is_not_nil") LUAU_REQUIRE_NO_ERRORS(result); CHECK_EQ(toString(requireTypeAtPosition({3, 28})), "number | string"); // a ~= nil - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(requireTypeAtPosition({5, 28})), "nil"); // a == nil :) else CHECK_EQ(toString(requireTypeAtPosition({5, 28})), "(number | string)?"); // a == nil @@ -618,7 +618,7 @@ TEST_CASE_FIXTURE(Fixture, "free_type_is_equal_to_an_lvalue") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(requireTypeAtPosition({3, 33})), "unknown"); // a == b else CHECK_EQ(toString(requireTypeAtPosition({3, 33})), "a"); // a == b @@ -639,7 +639,7 @@ TEST_CASE_FIXTURE(Fixture, "unknown_lvalue_is_not_synonymous_with_other_on_not_e LUAU_REQUIRE_NO_ERRORS(result); CHECK_EQ(toString(requireTypeAtPosition({3, 33})), "any"); // a ~= b - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(requireTypeAtPosition({3, 36})), "{ x: number }?"); // a ~= b else CHECK_EQ(toString(requireTypeAtPosition({3, 36})), "{| x: number |}?"); // a ~= b @@ -664,7 +664,7 @@ TEST_CASE_FIXTURE(Fixture, "string_not_equal_to_string_or_nil") CHECK_EQ(toString(requireTypeAtPosition({6, 29})), "string"); // a ~= b CHECK_EQ(toString(requireTypeAtPosition({6, 32})), "string?"); // a ~= b - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ(toString(requireTypeAtPosition({8, 29})), "string?"); // a == b CHECK_EQ(toString(requireTypeAtPosition({8, 32})), "string?"); // a == b @@ -703,7 +703,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_narrow_to_vector") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("never", toString(requireTypeAtPosition({3, 28}))); else CHECK_EQ("*error-type*", toString(requireTypeAtPosition({3, 28}))); @@ -729,7 +729,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonoptional_type_can_narrow_to_nil_if_sense_ LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-115281 Types produced by refinements do not consistently get simplified CHECK_EQ("(nil & string)?", toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil" @@ -780,7 +780,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typeguard_narrows_for_table") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ x: number } | { y: boolean }", toString(requireTypeAtPosition({3, 28}))); // type(x) == "table" else CHECK_EQ("{| x: number |} | {| y: boolean |}", toString(requireTypeAtPosition({3, 28}))); // type(x) == "table" @@ -822,7 +822,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_guard_can_filter_for_intersection_of_ta ToStringOptions opts; opts.exhaustive = true; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ x: number } & { y: number }", toString(requireTypeAtPosition({4, 28}), opts)); else CHECK_EQ("{| x: number |} & {| y: number |}", toString(requireTypeAtPosition({4, 28}), opts)); @@ -863,7 +863,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_guard_narrowed_into_nothingness") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-115281 Types produced by refinements do not consistently get simplified CHECK_EQ("{ x: number } & ~table", toString(requireTypeAtPosition({3, 28}))); @@ -955,7 +955,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "either_number_or_string") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("*error-type* | number | string", toString(requireTypeAtPosition({3, 28}))); else CHECK_EQ("number | string", toString(requireTypeAtPosition({3, 28}))); @@ -974,7 +974,7 @@ TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-115281 Types produced by refinements do not consistently get simplified CHECK_EQ("({ x: boolean } & { x: ~(false?) })?", toString(requireTypeAtPosition({3, 28}))); @@ -1082,7 +1082,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_comparison_ifelse_expression") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("*error-type* | number", toString(requireTypeAtPosition({6, 49}))); CHECK_EQ("*error-type* | ~number", toString(requireTypeAtPosition({6, 66}))); @@ -1094,7 +1094,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_comparison_ifelse_expression") } CHECK_EQ("number", toString(requireTypeAtPosition({10, 49}))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("~number", toString(requireTypeAtPosition({10, 66}))); else CHECK_EQ("unknown", toString(requireTypeAtPosition({10, 66}))); @@ -1224,7 +1224,7 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-115281 Types produced by refinements do not consistently get simplified CHECK("{ tag: \"exists\", x: string } & { x: ~(false?) }" == toString(requireTypeAtPosition({5, 28}))); @@ -1360,7 +1360,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "discriminate_from_isa_of_x") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK(R"({ tag: "Part", x: Part })" == toString(requireTypeAtPosition({5, 28}))); CHECK(R"({ tag: "Folder", x: Folder })" == toString(requireTypeAtPosition({7, 28}))); @@ -1375,7 +1375,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "discriminate_from_isa_of_x") TEST_CASE_FIXTURE(RefinementClassFixture, "typeguard_cast_free_table_to_vector") { // CLI-115286 - Refining via type(x) == 'vector' does not work in the new solver - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function f(vec) @@ -1450,7 +1450,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "type_narrow_but_the_discriminant_type LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("never", toString(requireTypeAtPosition({3, 28}))); CHECK_EQ("Instance | Vector3 | number | string", toString(requireTypeAtPosition({5, 28}))); @@ -1500,6 +1500,9 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "narrow_from_subclasses_of_instance_or TEST_CASE_FIXTURE(RefinementClassFixture, "x_as_any_if_x_is_instance_elseif_x_is_table") { + // CLI-117136 - this code doesn't finish constraint solving and has blocked types in the output + if (FFlag::LuauSolverV2) + return; CheckResult result = check(R"( --!nonstrict @@ -1514,7 +1517,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "x_as_any_if_x_is_instance_elseif_x_is LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("Folder & Instance & {- -}", toString(requireTypeAtPosition({5, 28}))); CHECK_EQ("(~Folder | ~Instance) & {- -} & never", toString(requireTypeAtPosition({7, 28}))); @@ -1566,7 +1569,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "isa_type_refinement_must_be_known_ahe { // CLI-115087 - The new solver does not consistently combine tables with // class types when they appear in the upper bounds of a free type. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function f(x): Instance @@ -1588,6 +1591,9 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "isa_type_refinement_must_be_known_ahe TEST_CASE_FIXTURE(RefinementClassFixture, "x_is_not_instance_or_else_not_part") { + // CLI-117135 - RefinementTests.x_is_not_instance_or_else_not_part not correctly applying refinements to a function parameter + if (FFlag::LuauSolverV2) + return; CheckResult result = check(R"( local function f(x: Part | Folder | string) if typeof(x) ~= "Instance" or not x:IsA("Part") then @@ -1635,7 +1641,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknowns") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("string", toString(requireTypeAtPosition({3, 28}))); CHECK_EQ("~string", toString(requireTypeAtPosition({5, 28}))); @@ -1773,7 +1779,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table_then_take_the_length end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK_EQ("table", toString(requireTypeAtPosition({3, 29}))); @@ -1795,7 +1801,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table_then_clone_it") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); } @@ -1807,6 +1813,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table_then_clone_it") TEST_CASE_FIXTURE(RefinementClassFixture, "refine_a_param_that_got_resolved_during_constraint_solving_stage") { + // CLI-117134 - Applying a refinement causes an optional value access error. + if (FFlag::LuauSolverV2) + return; CheckResult result = check(R"( type Id = T @@ -1840,7 +1849,7 @@ TEST_CASE_FIXTURE(RefinementClassFixture, "refine_a_param_that_got_resolved_duri LUAU_REQUIRE_NO_ERRORS(result); CHECK_EQ("Part", toString(requireTypeAtPosition({5, 28}))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("Instance & ~Part", toString(requireTypeAtPosition({7, 28}))); else CHECK_EQ("Instance", toString(requireTypeAtPosition({7, 28}))); @@ -1856,7 +1865,7 @@ TEST_CASE_FIXTURE(Fixture, "refine_a_property_of_some_global") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(3, result); @@ -1899,7 +1908,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dataflow_analysis_can_tell_refinements_when_ CHECK_EQ("nil", toString(requireTypeAtPosition({12, 28}))); CHECK_EQ("string", toString(requireTypeAtPosition({14, 28}))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-115281 - Types produced by refinements don't always get simplified CHECK_EQ("nil & string", toString(requireTypeAtPosition({18, 28}))); @@ -1989,7 +1998,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_annotations_arent_relevant_when_doing_d LUAU_REQUIRE_NO_ERRORS(result); CHECK_EQ("nil", toString(requireTypeAtPosition({8, 28}))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-115478 - This should be never CHECK_EQ("nil", toString(requireTypeAtPosition({9, 28}))); @@ -2000,7 +2009,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_annotations_arent_relevant_when_doing_d TEST_CASE_FIXTURE(BuiltinsFixture, "function_call_with_colon_after_refining_not_to_be_nil") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( --!strict @@ -2040,7 +2049,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinements_should_preserve_error_suppressio end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else LUAU_REQUIRE_NO_ERRORS(result); @@ -2066,7 +2075,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "many_refinements_on_val") TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; // this test is DCR-only as an instance of DCR fixing a bug in the old solver CheckResult result = check(R"( @@ -2088,7 +2097,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refine_unknown_to_table") TEST_CASE_FIXTURE(BuiltinsFixture, "conditional_refinement_should_stay_error_suppressing") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local function test(element: any?) @@ -2109,7 +2118,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "globals_can_be_narrowed_too") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-114134 CHECK("string & typeof(string)" == toString(requireTypeAtPosition(Position{2, 24}))); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.singletons.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.singletons.test.cpp index bec9d953..43b1305e 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.singletons.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.singletons.test.cpp @@ -6,7 +6,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); TEST_SUITE_BEGIN("TypeSingletons"); @@ -46,7 +46,7 @@ TEST_CASE_FIXTURE(Fixture, "string_singletons") TEST_CASE_FIXTURE(Fixture, "string_singleton_function_call") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -153,7 +153,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons") TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons_mismatch") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(g: ((true, string) -> ()) & ((false, number) -> ())) @@ -162,7 +162,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons_mismatch") )"); LUAU_REQUIRE_ERROR_COUNT(2, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("None of the overloads for function that accept 2 arguments are compatible.", toString(result.errors[0])); CHECK_EQ("Available overloads: (true, string) -> (); and (false, number) -> ()", toString(result.errors[1])); @@ -195,7 +195,7 @@ TEST_CASE_FIXTURE(Fixture, "enums_using_singletons_mismatch") LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("Type '\"bang\"' could not be converted into '\"bar\" | \"baz\" | \"foo\"'" == toString(result.errors[0])); else CHECK_EQ( @@ -256,7 +256,7 @@ TEST_CASE_FIXTURE(Fixture, "tagged_unions_immutable_tag") LUAU_REQUIRE_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CannotAssignToNever* tm = get(result.errors[0]); REQUIRE(tm); @@ -343,7 +343,7 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK( "Type\n" " '{ [\"\\n\"]: number }'\n" @@ -368,7 +368,7 @@ local a: Animal = { tag = 'cat', cafood = 'something' } )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("Type '{ cafood: string, tag: \"cat\" }' could not be converted into 'Cat | Dog'" == toString(result.errors[0])); else { @@ -391,7 +391,7 @@ local a: Result = { success = false, result = 'something' } )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("Type '{ result: string, success: boolean }' could not be converted into 'Bad | Good'" == toString(result.errors[0])); else { @@ -406,7 +406,7 @@ Table type 'a' not compatible with type 'Bad' because the former is missing fiel TEST_CASE_FIXTURE(Fixture, "parametric_tagged_union_alias") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; CheckResult result = check(R"( type Ok = {success: true, result: T} @@ -442,7 +442,7 @@ local a: Animal = if true then { tag = 'cat', catfood = 'something' } else { tag TEST_CASE_FIXTURE(Fixture, "widen_the_supertype_if_it_is_free_and_subtype_has_singleton") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function foo(f, x) @@ -462,7 +462,7 @@ TEST_CASE_FIXTURE(Fixture, "widen_the_supertype_if_it_is_free_and_subtype_has_si TEST_CASE_FIXTURE(Fixture, "return_type_of_f_is_not_widened") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function foo(f, x): "hello"? -- anyone there? @@ -488,7 +488,7 @@ TEST_CASE_FIXTURE(Fixture, "widening_happens_almost_everywhere") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(R"("foo")", toString(requireType("copy"))); else CHECK_EQ("string", toString(requireType("copy"))); @@ -613,7 +613,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "singletons_stick_around_under_assignment") print(kind == "Bar") -- type of equality refines to `false` )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else LUAU_REQUIRE_ERROR_COUNT(1, result); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.tables.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.tables.test.cpp index 6256ede6..9d76e7bd 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.tables.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.tables.test.cpp @@ -15,9 +15,10 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); -LUAU_FASTFLAG(LuauInstantiateInSubtyping); -LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering); +LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauInstantiateInSubtyping) +LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering) +LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections) LUAU_DYNAMIC_FASTFLAG(LuauImproveNonFunctionCallError) @@ -25,7 +26,7 @@ TEST_SUITE_BEGIN("TableTests"); TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_shouldnt_seal_table_in_len_function_fn") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( local t = {} @@ -135,7 +136,7 @@ TEST_CASE_FIXTURE(Fixture, "index_expression_is_checked_against_the_indexer_type )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_MESSAGE(get(result.errors[0]), "Expected CannotExtendTable but got " << toString(result.errors[0])); else CHECK(get(result.errors[0])); @@ -162,7 +163,7 @@ TEST_CASE_FIXTURE(Fixture, "cannot_augment_sealed_table") // TODO: better, more robust comparison of type vars auto s = toString(error->tableType, ToStringOptions{/*exhaustive*/ true}); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(s, "{ prop: number }"); else CHECK_EQ(s, "{| prop: number |}"); @@ -317,7 +318,7 @@ TEST_CASE_FIXTURE(Fixture, "call_method_with_explicit_self_argument") TEST_CASE_FIXTURE(Fixture, "used_dot_instead_of_colon") { // CLI-114792 Dot vs colon warnings aren't in the new solver yet. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local T = {} @@ -370,7 +371,7 @@ TEST_CASE_FIXTURE(Fixture, "used_dot_instead_of_colon_but_correctly") TEST_CASE_FIXTURE(Fixture, "used_colon_instead_of_dot") { // CLI-114792 Dot vs colon warnings aren't in the new solver yet. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local T = {} @@ -395,7 +396,7 @@ TEST_CASE_FIXTURE(Fixture, "used_colon_instead_of_dot") TEST_CASE_FIXTURE(Fixture, "open_table_unification_2") { // CLI-114792 We don't report MissingProperties in many places where the old solver does. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local a = {} @@ -479,7 +480,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_param_width_subtyping_2") LUAU_REQUIRE_ERROR_COUNT(1, result); // CLI 114792 We don't report MissingProperties in many places where the old solver does - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { TypeMismatch* error = get(result.errors[0]); REQUIRE_MESSAGE(error != nullptr, "Expected TypeMismatch but got " << toString(result.errors[0])); @@ -512,7 +513,7 @@ TEST_CASE_FIXTURE(Fixture, "table_param_width_subtyping_3") CHECK(result.errors[0].location == Location{Position{6, 8}, Position{6, 9}}); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors[0]) == "Type 'T' could not be converted into '{ read baz: unknown }'"); else { @@ -535,7 +536,7 @@ TEST_CASE_FIXTURE(Fixture, "table_param_width_subtyping_3") TEST_CASE_FIXTURE(Fixture, "table_unification_4") { // CLI-114134 - Use egraphs to simplify types better. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function foo(o) @@ -566,7 +567,7 @@ TEST_CASE_FIXTURE(Fixture, "ok_to_add_property_to_free_table") TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_assignment") { // CLI-114872 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -585,7 +586,7 @@ TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_assignmen TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_function_call") { // CLI-114873 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -690,7 +691,7 @@ TEST_CASE_FIXTURE(Fixture, "indexers_get_quantified_too") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("({unknown}) -> ()" == toString(requireType("swap"))); else { @@ -762,7 +763,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_array_like_table") CHECK_EQ(*builtinTypes->numberType, *indexer.indexType); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-114134 - Use egraphs to simplify types CHECK("string | string | string" == toString(indexer.indexResultType)); @@ -801,7 +802,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_value_property_in_literal") CHECK(bool(retType->indexer)); const TableIndexer& indexer = *retType->indexer; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ __name: string }", toString(indexer.indexType)); else CHECK_EQ("{| __name: string |}", toString(indexer.indexType)); @@ -810,7 +811,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_value_property_in_literal") TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_its_variable_type_and_unifiable") { // This code is totally different in the new solver. We instead create a new type state for t2. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local t1: { [string]: string } = {} @@ -850,7 +851,7 @@ TEST_CASE_FIXTURE(Fixture, "indexer_mismatch") TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm != nullptr); CHECK(toString(tm->wantedType) == "{number}"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(tm->givenType) == "{ [string]: string }"); else CHECK(toString(tm->givenType) == "{| [string]: string |}"); @@ -892,7 +893,7 @@ TEST_CASE_FIXTURE(Fixture, "sealed_table_value_can_infer_an_indexer") TEST_CASE_FIXTURE(Fixture, "array_factory_function") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function empty() return {} end @@ -912,7 +913,7 @@ TEST_CASE_FIXTURE(Fixture, "sealed_table_indexers_must_unify") LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-114879 - Error path reporting is not great CHECK( @@ -929,7 +930,7 @@ TEST_CASE_FIXTURE(Fixture, "indexer_on_sealed_table_must_unify_with_free_table") // CLI-114134 What should be happening here is that the type of `t` should // be reduced from `{number} & {string}` to `never`, but that's not // happening. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function F(t): {number} @@ -992,7 +993,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_from_a_table_should_prefer_properti TEST_CASE_FIXTURE(Fixture, "any_when_indexing_into_an_unsealed_table_with_no_indexer_in_nonstrict_mode") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict @@ -1029,7 +1030,7 @@ TEST_CASE_FIXTURE(Fixture, "disallow_indexing_into_an_unsealed_table_with_no_ind local k1 = getConstant("key1") )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("unknown" == toString(requireType("k1"))); else CHECK("any" == toString(requireType("k1"))); @@ -1144,7 +1145,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_inferred") TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_both_ways") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type VectorMt = { __add: (Vector, number) -> Vector } @@ -1164,7 +1165,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_both_ways") TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_both_ways_lti") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local vectorMt = {} @@ -1522,7 +1523,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "found_multiple_like_keys") TEST_CASE_FIXTURE(BuiltinsFixture, "dont_suggest_exact_match_keys") { // CLI-114977 Unsealed table writes don't account for order properly - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local t = {} @@ -1565,7 +1566,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_returns_pointer_to_metatable") TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_mismatch_should_fail") { // This test is invalid because we now create a new type state for t1 at the assignment. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local t1 = {x = 1} @@ -1609,7 +1610,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "property_lookup_through_tabletypevar_metatab TEST_CASE_FIXTURE(BuiltinsFixture, "missing_metatable_for_sealed_tables_do_not_get_inferred") { // This test is invalid because we now create a new type state for t at the assignment. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local t = {x = 1} @@ -1664,7 +1665,7 @@ TEST_CASE_FIXTURE(Fixture, "right_table_missing_key") TEST_CASE_FIXTURE(Fixture, "right_table_missing_key2") { // CLI-114792 We don't report MissingProperties - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(t: {}): { [string]: string, a: string } @@ -1697,7 +1698,7 @@ TEST_CASE_FIXTURE(Fixture, "casting_unsealed_tables_with_props_into_table_with_i TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("{ [string]: string }", toString(tm->wantedType, o)); CHECK_EQ("{ [string]: number }", toString(tm->givenType, o)); @@ -1725,7 +1726,7 @@ TEST_CASE_FIXTURE(Fixture, "casting_sealed_tables_with_props_into_table_with_ind ToStringOptions o{/* exhaustive= */ true}; TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("{ [string]: string }", toString(tm->wantedType, o)); CHECK_EQ("{ foo: number }", toString(tm->givenType, o)); @@ -1760,7 +1761,7 @@ TEST_CASE_FIXTURE(Fixture, "casting_tables_with_props_into_table_with_indexer3") TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("string" == toString(tm->wantedType)); CHECK("number" == toString(tm->givenType)); @@ -1795,7 +1796,7 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multi LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ( "Type pack '{ x: number }' could not be converted into '{ x: number, y: number, z: number }';" @@ -1824,7 +1825,7 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multi local t: MixedTable = {"fail"} )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -1863,7 +1864,7 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_extra_props_dont_report_multipl TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("{{ x: number }}", toString(tm->wantedType)); CHECK_EQ("{{ x: number, y: number, z: number }}", toString(tm->givenType)); @@ -1901,7 +1902,7 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_on_massive_table_is_cut_short") TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("{ a: number, b: number, c: number, d: number, e: number, ... 1 more ... }" == toString(requireType("t"))); CHECK_EQ("number", toString(tm->givenType)); @@ -1926,7 +1927,7 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_on_massive_table_is_cut_short") TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr") { // CLI-100076 Assigning nil to an indexer should always succeed - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function f(): { [string]: number } @@ -1948,7 +1949,7 @@ TEST_CASE_FIXTURE(Fixture, "ok_to_provide_a_subtype_during_construction") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-114134 Use egraphs to simplify types more consistently CHECK("{number | number | string}" == toString(requireType("t"), {/*exhaustive*/ true})); @@ -1968,7 +1969,7 @@ TEST_CASE_FIXTURE(Fixture, "reasonable_error_when_adding_a_nonexistent_property_ LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CannotExtendTable* cet = get(result.errors[0]); REQUIRE_MESSAGE(cet, "Expected CannotExtendTable but got " << result.errors[0]); @@ -2012,7 +2013,7 @@ TEST_CASE_FIXTURE(Fixture, "only_ascribe_synthetic_names_at_module_scope") CHECK_EQ("TopLevel", toString(requireType("TopLevel"))); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{number}?", toString(requireType("foo"))); else CHECK_EQ("{number}", toString(requireType("foo"))); @@ -2036,7 +2037,7 @@ TEST_CASE_FIXTURE(Fixture, "hide_table_error_properties") LUAU_REQUIRE_ERROR_COUNT(2, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("Cannot add property 'a' to table '{ x: number }'", toString(result.errors[0])); CHECK_EQ("Cannot add property 'b' to table '{ x: number }'", toString(result.errors[1])); @@ -2095,7 +2096,7 @@ local Test: {Table} = { TEST_CASE_FIXTURE(Fixture, "common_table_element_general") { // CLI-115275 - Bidirectional inference does not always propagate indexer types into the expression - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Table = { @@ -2217,7 +2218,7 @@ foo({ TEST_CASE_FIXTURE(Fixture, "common_table_element_union_in_call_tail") { // CLI-115239 - Bidirectional checking does not work for __call metamethods - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Foo = {x: number | string} @@ -2264,7 +2265,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table local c : string = t.m("hi") )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); @@ -2319,7 +2320,7 @@ local b: B = a LUAU_REQUIRE_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors.at(0)) == R"(Type 'A' could not be converted into 'B'; at [read "y"], number is not exactly string)"); else { @@ -2346,7 +2347,7 @@ local b: B = a LUAU_REQUIRE_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors.at(0)) == R"(Type 'A' could not be converted into 'B'; at [read "b"][read "y"], number is not exactly string)"); else { @@ -2395,7 +2396,7 @@ Type could not be converted into '(a) -> ()'; different number of generic type parameters)"; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // The assignment of c2 to b2 is, surprisingly, allowed under the new // solver for two reasons: @@ -2460,7 +2461,7 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_key") LUAU_REQUIRE_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("Type 'A' could not be converted into 'B'; at indexer(), number is not exactly string" == toString(result.errors[0])); } @@ -2486,7 +2487,7 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_value") LUAU_REQUIRE_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("Type 'A' could not be converted into 'B'; at indexResult(), number is not exactly string" == toString(result.errors[0])); } @@ -2503,7 +2504,7 @@ Type 'number' could not be converted into 'string' in an invariant context)"; TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table") { // Table properties like HasSuper.p must be invariant. The new solver rightly rejects this program. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -2535,7 +2536,7 @@ local y: number = tmp.p.y LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK( "Type 'tmp' could not be converted into 'HasSuper'; at [read \"p\"], { x: number, y: number } is not exactly Super" == toString(result.errors[0]) @@ -2553,7 +2554,7 @@ Table type '{ x: number, y: number }' not compatible with type 'Super' because t TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer") { // CLI-114791 Bidirectional inference should be able to cause the inference engine to forget that a table literal has some property - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -2571,7 +2572,7 @@ TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer") TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_metatable_type_call") { // CLI-114782 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local b @@ -2703,7 +2704,7 @@ TEST_CASE_FIXTURE(Fixture, "confusing_indexing") local foo = f({p = "string"}) )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-114781 Bidirectional checking can't see through the intersection LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -2729,7 +2730,7 @@ TEST_CASE_FIXTURE(Fixture, "pass_a_union_of_tables_to_a_function_that_requires_a LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) REQUIRE_EQ("{ y: number }", toString(requireType("b"))); else REQUIRE_EQ("{- y: number -}", toString(requireType("b"))); @@ -2750,7 +2751,7 @@ TEST_CASE_FIXTURE(Fixture, "pass_a_union_of_tables_to_a_function_that_requires_a LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) REQUIRE_EQ("{ y: number }", toString(requireType("b"))); else REQUIRE_EQ("{- y: number -}", toString(requireType("b"))); @@ -2826,7 +2827,7 @@ TEST_CASE_FIXTURE(Fixture, "table_length") TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_indexer") { // CLI-100076 - Assigning a table key to `nil` in the presence of an indexer should always be permitted - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check("local a = {} a[0] = 7 a[0] = nil"); LUAU_REQUIRE_ERROR_COUNT(0, result); @@ -2932,7 +2933,7 @@ TEST_CASE_FIXTURE(Fixture, "tables_get_names_from_their_locals") TEST_CASE_FIXTURE(Fixture, "should_not_unblock_table_type_twice") { // don't run this when the DCR flag isn't set - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; check(R"( @@ -2972,7 +2973,7 @@ TEST_CASE_FIXTURE(Fixture, "generalize_table_argument") const TableType* fooArg1Table = get(follow(*fooArg1)); REQUIRE(fooArg1Table); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(fooArg1Table->state, TableState::Sealed); else CHECK_EQ(fooArg1Table->state, TableState::Generic); @@ -3058,7 +3059,7 @@ TEST_CASE_FIXTURE(Fixture, "inferring_crazy_table_should_also_be_quick") )"); ModulePtr module = getMainModule(); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_GE(500, module->internalTypes.types.size()); else CHECK_GE(100, module->internalTypes.types.size()); @@ -3118,7 +3119,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_crash_when_setmetatable_does_not_produc { CheckResult result = check("local x = setmetatable({})"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-114665: Generic parameters should not also be optional. LUAU_REQUIRE_NO_ERRORS(result); @@ -3223,7 +3224,7 @@ local baz = foo[bar] TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_basic") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -3238,7 +3239,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_basic") local foo = a(12) )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK(get(result.errors[0])); @@ -3260,7 +3261,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_must_be_callable") LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { if (DFFlag::LuauImproveNonFunctionCallError) CHECK("Cannot call a value of type a" == toString(result.errors[0])); @@ -3299,7 +3300,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_generic") TEST_CASE_FIXTURE(BuiltinsFixture, "table_simple_call") { // The new solver can see that this function is safe to oversaturate. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local a = setmetatable({ x = 2 }, { @@ -3333,7 +3334,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "access_index_metamethod_that_returns_variadi ToStringOptions o; o.exhaustive = true; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ x: string }", toString(requireType("foo"), o)); else CHECK_EQ("{| x: string |}", toString(requireType("foo"), o)); @@ -3385,7 +3386,7 @@ TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early") LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("Value of type '{ x: number? }?' could be nil", toString(result.errors[0])); CHECK_EQ("number | { x: number }", toString(requireType("u"))); @@ -3405,7 +3406,7 @@ TEST_CASE_FIXTURE(Fixture, "accidentally_checked_prop_in_opposite_branch") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("Value of type '{ x: number? }?' could be nil", toString(result.errors[0])); else CHECK_EQ("Value of type '{| x: number? |}?' could be nil", toString(result.errors[0])); @@ -3484,7 +3485,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_leak_free_table_props") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("({ read blah: unknown }) -> ()", toString(requireType("a"))); CHECK_EQ("({ read gwar: unknown }) -> ()", toString(requireType("b"))); @@ -3504,7 +3505,7 @@ TEST_CASE_FIXTURE(Fixture, "mixed_tables_with_implicit_numbered_keys") local t: { [string]: number } = { 5, 6, 7 } )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK( @@ -3571,7 +3572,7 @@ TEST_CASE_FIXTURE(Fixture, "prop_access_on_unions_of_indexers_where_key_whose_ty )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("Type '{ [boolean]: number } | {number}' does not have key 'x'", toString(result.errors[0])); else CHECK_EQ("Type '{number} | {| [boolean]: number |}' does not have key 'x'", toString(result.errors[0])); @@ -3592,7 +3593,7 @@ local b = a.x TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shape_type") { // CLI-115087 The new solver cannot infer that a table-like type is actually string - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function f(s) @@ -3619,7 +3620,7 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_ f("baz" :: "bar" | "baz") )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // CLI-115090 Error reporting is quite bad in this case. @@ -3681,7 +3682,7 @@ Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutel TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compatible") { // CLI-115087 The new solver cannot infer that a table-like type is actually string - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function f(s): string @@ -3703,7 +3704,7 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_ end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(4, result); @@ -3736,7 +3737,7 @@ Table type 'typeof(string)' not compatible with type 't1 where t1 = {+ absolutel TEST_CASE_FIXTURE(BuiltinsFixture, "a_free_shape_can_turn_into_a_scalar_directly") { // We need egraphs to simplify the type of `out` here. CLI-114134 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function stringByteList(str) @@ -3774,7 +3775,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table local c : string = t.m("hi") )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // FIXME. We really should be reporting just one error in this case. CLI-114509 LUAU_REQUIRE_ERROR_COUNT(3, result); @@ -3812,7 +3813,7 @@ local g : ({ p : number, q : string }) -> ({ p : number, r : boolean }) = f LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { const TypeMismatch* error = get(result.errors[0]); REQUIRE_MESSAGE(error, "Expected TypeMismatch but got " << result.errors[0]); @@ -3832,7 +3833,7 @@ local g : ({ p : number, q : string }) -> ({ p : number, r : boolean }) = f TEST_CASE_FIXTURE(BuiltinsFixture, "setmetatable_has_a_side_effect") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -3970,7 +3971,7 @@ TEST_CASE_FIXTURE(Fixture, "when_augmenting_an_unsealed_table_with_an_indexer_ap CHECK(tt->props.empty()); REQUIRE(tt->indexer); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("unknown" == toString(tt->indexer->indexType)); else CHECK("string" == toString(tt->indexer->indexType)); @@ -3998,7 +3999,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_extend_unsealed_tables_in_rvalue_position") CHECK(0 == ttv->props.count("")); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_ERROR_COUNT(1, result); else LUAU_REQUIRE_NO_ERRORS(result); @@ -4151,7 +4152,7 @@ TEST_CASE_FIXTURE(Fixture, "cli_84607_missing_prop_in_array_or_dict") LUAU_REQUIRE_ERROR_COUNT(2, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { const TypeMismatch* err1 = get(result.errors[0]); REQUIRE_MESSAGE(err1, "Expected TypeMismatch but got " << result.errors[0]); @@ -4199,7 +4200,7 @@ TEST_CASE_FIXTURE(Fixture, "simple_method_definition") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ m: (unknown) -> number }", toString(getMainModule()->returnType, ToStringOptions{true})); else CHECK_EQ("{| m: (a) -> number |}", toString(getMainModule()->returnType, ToStringOptions{true})); @@ -4207,7 +4208,7 @@ TEST_CASE_FIXTURE(Fixture, "simple_method_definition") TEST_CASE_FIXTURE(Fixture, "identify_all_problematic_table_fields") { - ScopedFastFlag sff_DebugLuauDeferredConstraintResolution{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff_LuauSolverV2{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type T = { @@ -4235,7 +4236,7 @@ TEST_CASE_FIXTURE(Fixture, "identify_all_problematic_table_fields") TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; CheckResult result = check(R"( @@ -4275,7 +4276,7 @@ TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported") TEST_CASE_FIXTURE(Fixture, "infer_write_property") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function f(t) @@ -4307,7 +4308,7 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression") // the new solver reports specifically the inner mismatch, rather than the whole table // honestly not sure which of these is a better developer experience. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ(*tm->wantedType, *builtinTypes->stringType); CHECK_EQ(*tm->givenType, *builtinTypes->numberType); @@ -4321,7 +4322,7 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression") TEST_CASE_FIXTURE(Fixture, "write_to_read_only_property") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function f(t: {read x: number}) @@ -4343,7 +4344,7 @@ TEST_CASE_FIXTURE(Fixture, "write_to_read_only_property") TEST_CASE_FIXTURE(Fixture, "write_to_unusually_named_read_only_property") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function f(t: {read ["hello world"]: number}) @@ -4358,7 +4359,7 @@ TEST_CASE_FIXTURE(Fixture, "write_to_unusually_named_read_only_property") TEST_CASE_FIXTURE(Fixture, "write_annotations_are_unsupported_even_with_the_new_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( function f(t: {write foo: number}) @@ -4373,7 +4374,7 @@ TEST_CASE_FIXTURE(Fixture, "write_annotations_are_unsupported_even_with_the_new_ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported") { - ScopedFastFlag sff[] = {{FFlag::DebugLuauDeferredConstraintResolution, false}}; + ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, false}}; CheckResult result = check(R"( type W = {read x: number} @@ -4397,7 +4398,7 @@ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported") { - ScopedFastFlag sff[] = {{FFlag::DebugLuauDeferredConstraintResolution, false}}; + ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, false}}; CheckResult result = check(R"( type T = {read [string]: number} @@ -4414,10 +4415,10 @@ TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported") TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; - ScopedFastFlag sff[] = {{FFlag::DebugLuauDeferredConstraintResolution, true}}; + ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, true}}; CheckResult result = check(R"( function oc(player, speaker) @@ -4473,7 +4474,7 @@ TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array") TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_string") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -4492,7 +4493,7 @@ TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_string") TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_another_parameter") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -4511,7 +4512,7 @@ TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_another_ TEST_CASE_FIXTURE(Fixture, "write_to_union_property_not_all_present") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type Animal = {tag: "Cat", meow: boolean} | {tag: "Dog", woof: boolean} @@ -4631,7 +4632,7 @@ TEST_CASE_FIXTURE(Fixture, "cant_index_this") TEST_CASE_FIXTURE(Fixture, "setindexer_multiple_tables_intersection") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local function f(t: { [string]: number } & { [thread]: boolean }, x) @@ -4657,7 +4658,7 @@ TEST_CASE_FIXTURE(Fixture, "insert_a_and_f_of_a_into_table_res_in_a_loop") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK(get(result.errors[0])); @@ -4677,7 +4678,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "ipairs_adds_an_unbounded_indexer") // The old solver erroneously leaves a free type dangling here. The new // solver does better. - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("{unknown}" == toString(requireType("a"), {true})); else CHECK("{a}" == toString(requireType("a"), {true})); @@ -4794,4 +4795,41 @@ end LUAU_REQUIRE_NO_ERRORS(result); } + +TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_branching_table") +{ + ScopedFastFlag sff{FFlag::LuauAcceptIndexingTableUnionsIntersections, true}; + + CheckResult result = check(R"( + local test = if true then { "meow", "woof" } else { 4, 81 } + local test2 = test[1] + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + + // unfortunate type duplication in the union + if (FFlag::LuauSolverV2) + CHECK("number | string | string" == toString(requireType("test2"))); + else + CHECK("number | string" == toString(requireType("test2"))); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_branching_table2") +{ + ScopedFastFlag sff{FFlag::LuauAcceptIndexingTableUnionsIntersections, true}; + + CheckResult result = check(R"( + local test = if true then {} else {} + local test2 = test[1] + )"); + + LUAU_REQUIRE_NO_ERRORS(result); + + // unfortunate type duplication in the union + if (FFlag::LuauSolverV2) + CHECK("unknown | unknown" == toString(requireType("test2"))); + else + CHECK("any" == toString(requireType("test2"))); +} + TEST_SUITE_END(); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.test.cpp index 1140976f..09c6c05b 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.test.cpp @@ -17,7 +17,7 @@ #include LUAU_FASTFLAG(LuauFixLocationSpanTableIndexExpr); -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauInstantiateInSubtyping); LUAU_FASTINT(LuauCheckRecursionLimit); LUAU_FASTINT(LuauNormalizeCacheLimit); @@ -49,7 +49,7 @@ TEST_CASE_FIXTURE(Fixture, "tc_error") { CheckResult result = check("local a = 7 local b = 'hi' a = b"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK("number | string" == toString(requireType("a"))); @@ -69,7 +69,7 @@ TEST_CASE_FIXTURE(Fixture, "tc_error_2") { CheckResult result = check("local a = 7 a = 'hi'"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); CHECK("number | string" == toString(requireType("a"))); @@ -96,7 +96,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_locals_with_nil_value") CheckResult result = check("local f = nil; f = 'hello world'"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("string?" == toString(requireType("f"))); } @@ -128,7 +128,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_locals_via_assignment_from_its_call_site") f("foo") )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("unknown" == toString(requireType("a"))); CHECK("(unknown) -> ()" == toString(requireType("f"))); @@ -146,7 +146,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_locals_via_assignment_from_its_call_site") TEST_CASE_FIXTURE(Fixture, "infer_in_nocheck_mode") { ScopedFastFlag sff[]{ - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; CheckResult result = check(R"( @@ -194,7 +194,7 @@ TEST_CASE_FIXTURE(Fixture, "if_statement") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK("string?" == toString(requireType("a"))); CHECK("number?" == toString(requireType("b"))); @@ -224,7 +224,7 @@ TEST_CASE_FIXTURE(Fixture, "statements_are_topologically_sorted") TEST_CASE_FIXTURE(Fixture, "unify_nearly_identical_recursive_types") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local o @@ -265,7 +265,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "weird_case") TEST_CASE_FIXTURE(Fixture, "dont_ice_when_failing_the_occurs_check") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -318,7 +318,7 @@ TEST_CASE_FIXTURE(Fixture, "type_errors_infer_types") CHECK_EQ("x", err->key); // TODO: Should we assert anything about these tests when DCR is being used? - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) { CHECK_EQ("*error-type*", toString(requireType("c"))); CHECK_EQ("*error-type*", toString(requireType("d"))); @@ -384,7 +384,7 @@ TEST_CASE_FIXTURE(Fixture, "exponential_blowup_from_copying_types") // checker. We also want it to somewhat match up with production values, so we push up the parser recursion limit a little bit instead. TEST_CASE_FIXTURE(Fixture, "check_type_infer_recursion_count") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; #if defined(LUAU_ENABLE_ASAN) int limit = 250; @@ -442,7 +442,7 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit") TEST_CASE_FIXTURE(Fixture, "globals") { // The new solver does not permit assignments to globals like this. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict @@ -456,7 +456,7 @@ TEST_CASE_FIXTURE(Fixture, "globals") TEST_CASE_FIXTURE(Fixture, "globals2") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!nonstrict @@ -506,7 +506,7 @@ TEST_CASE_FIXTURE(Fixture, "correctly_scope_locals_do") TEST_CASE_FIXTURE(Fixture, "checking_should_not_ice") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CHECK_NOTHROW(check(R"( --!nonstrict @@ -600,7 +600,7 @@ TEST_CASE_FIXTURE(Fixture, "tc_after_error_recovery_no_assert") TEST_CASE_FIXTURE(BuiltinsFixture, "tc_after_error_recovery_no_replacement_name_in_error") { { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -622,7 +622,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tc_after_error_recovery_no_replacement_name_ } { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -697,7 +697,7 @@ TEST_CASE_FIXTURE(Fixture, "cli_39932_use_unifier_in_ensure_methods") TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstStatError") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( foo @@ -708,7 +708,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstStatError") TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstExprError") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local a = foo: @@ -759,7 +759,7 @@ TEST_CASE_FIXTURE(Fixture, "no_stack_overflow_from_isoptional") std::optional t0 = lookupType("t0"); REQUIRE(t0); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("any" == toString(*t0)); else CHECK_EQ("*error-type*", toString(*t0)); @@ -819,7 +819,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_heap_use_after_free_error") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) LUAU_REQUIRE_NO_ERRORS(result); else LUAU_REQUIRE_ERRORS(result); @@ -1099,7 +1099,7 @@ end TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -1177,7 +1177,7 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_no_ice") LUAU_REQUIRE_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("Type contains a self-recursive construct that cannot be resolved" == toString(result.errors[0])); else CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0])); @@ -1199,7 +1199,7 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer") CHECK(1 == result.errors.size()); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(Location{{3, 22}, {3, 42}} == result.errors[0].location); else CHECK(Location{{3, 12}, {3, 46}} == result.errors[0].location); @@ -1222,7 +1222,7 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_cache_limit_normalizer") TEST_CASE_FIXTURE(Fixture, "follow_on_new_types_in_substitution") { // CLI-114134 - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local obj = {} @@ -1358,7 +1358,7 @@ end TEST_CASE_FIXTURE(Fixture, "dcr_delays_expansion_of_function_containing_blocked_parameter_type") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, }; CheckResult result = check(R"( @@ -1392,7 +1392,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_function_that_invokes_itself_with_ end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("(unknown) -> ()" == toString(requireType("readValue"))); else CHECK("(a) -> ()" == toString(requireType("readValue"))); @@ -1408,7 +1408,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_function_that_invokes_itself_with_ end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("(unknown) -> ()" == toString(requireType("readValue"))); else CHECK("(number) -> ()" == toString(requireType("readValue"))); @@ -1527,7 +1527,7 @@ TEST_CASE_FIXTURE(Fixture, "promote_tail_type_packs") TEST_CASE_FIXTURE(BuiltinsFixture, "lti_must_record_contributing_locations") { - ScopedFastFlag sff_DebugLuauDeferredConstraintResolution{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff_LuauSolverV2{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local function f(a) @@ -1563,7 +1563,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "lti_must_record_contributing_locations") */ TEST_CASE_FIXTURE(BuiltinsFixture, "be_sure_to_use_active_txnlog_when_evaluating_a_variadic_overload") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function concat(target: {T}, ...: {T} | T): {T} @@ -1615,7 +1615,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_iter_metamethod") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.tryUnify.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.tryUnify.test.cpp index 7db4512b..10ddd097 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.tryUnify.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.tryUnify.test.cpp @@ -11,13 +11,13 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauUnifierRecursionOnRestart); struct TryUnifyFixture : Fixture { // Cannot use `TryUnifyFixture` under DCR. - ScopedFastFlag noDcr{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag noDcr{FFlag::LuauSolverV2, false}; TypeArena arena; ScopePtr globalScope{new Scope{arena.addTypePack({TypeId{}})}}; @@ -154,7 +154,7 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_intersection_sub_anything") TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_never") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(arg : { prop : string & number }) : never @@ -166,7 +166,7 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_never") TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_anything") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(arg : { prop : string & number }) : boolean @@ -178,7 +178,7 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_anything") TEST_CASE_FIXTURE(Fixture, "members_of_failed_typepack_unification_are_unified_with_errorType") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(arg: number) end @@ -195,7 +195,7 @@ TEST_CASE_FIXTURE(Fixture, "members_of_failed_typepack_unification_are_unified_w TEST_CASE_FIXTURE(Fixture, "result_of_failed_typepack_unification_is_constrained") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(arg: number) return arg end @@ -278,7 +278,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cli_41095_concat_log_in_sealed_table_unifica LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ(toString(result.errors[0]), "No overload for function accepts 0 arguments."); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(result.errors[1]), "Available overloads: ({V}, V) -> (); and ({V}, number, V) -> ()"); else CHECK_EQ(toString(result.errors[1]), "Available overloads: ({a}, a) -> (); and ({a}, number, a) -> ()"); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.typePacks.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.typePacks.test.cpp index f23dfb47..8b489c44 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.typePacks.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.typePacks.test.cpp @@ -9,7 +9,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauInstantiateInSubtyping); TEST_SUITE_BEGIN("TypePackTests"); @@ -316,7 +316,7 @@ local c: Packed tf = lookupType("Packed"); REQUIRE(tf); CHECK_EQ(toString(*tf), "Packed"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(*tf, {true}), "{ f: (T, U...) -> (T, U...) }"); else CHECK_EQ(toString(*tf, {true}), "{| f: (T, U...) -> (T, U...) |}"); @@ -324,7 +324,7 @@ local c: Packed auto ttvA = get(requireType("a")); REQUIRE(ttvA); CHECK_EQ(toString(requireType("a")), "Packed"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(requireType("a"), {true}), "{ f: (number) -> number }"); else CHECK_EQ(toString(requireType("a"), {true}), "{| f: (number) -> number |}"); @@ -336,7 +336,7 @@ local c: Packed auto ttvB = get(requireType("b")); REQUIRE(ttvB); CHECK_EQ(toString(requireType("b")), "Packed"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(requireType("b"), {true}), "{ f: (string, number) -> (string, number) }"); else CHECK_EQ(toString(requireType("b"), {true}), "{| f: (string, number) -> (string, number) |}"); @@ -348,7 +348,7 @@ local c: Packed auto ttvC = get(requireType("c")); REQUIRE(ttvC); CHECK_EQ(toString(requireType("c")), "Packed"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(requireType("c"), {true}), "{ f: (string, number, boolean) -> (string, number, boolean) }"); else CHECK_EQ(toString(requireType("c"), {true}), "{| f: (string, number, boolean) -> (string, number, boolean) |}"); @@ -381,7 +381,7 @@ local d: { a: typeof(c) } REQUIRE(tf); CHECK_EQ(toString(*tf), "Packed"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ(toString(*tf, {true}), "{ a: T, b: (U...) -> () }"); @@ -421,12 +421,12 @@ type C = Import.Packed auto tf = lookupType("Alias"); REQUIRE(tf); CHECK_EQ(toString(*tf), "Alias"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(*tf, {true}), "{ a: S, b: (T, R...) -> () }"); else CHECK_EQ(toString(*tf, {true}), "{| a: S, b: (T, R...) -> () |}"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(requireType("a"), {true}), "{ a: string, b: (number, boolean) -> () }"); else CHECK_EQ(toString(requireType("a"), {true}), "{| a: string, b: (number, boolean) -> () |}"); @@ -434,7 +434,7 @@ type C = Import.Packed tf = lookupType("B"); REQUIRE(tf); CHECK_EQ(toString(*tf), "B"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(*tf, {true}), "{ a: string, b: (X...) -> () }"); else CHECK_EQ(toString(*tf, {true}), "{| a: string, b: (X...) -> () |}"); @@ -442,7 +442,7 @@ type C = Import.Packed tf = lookupType("C"); REQUIRE(tf); CHECK_EQ(toString(*tf), "C"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(*tf, {true}), "{ a: string, b: (number, X...) -> () }"); else CHECK_EQ(toString(*tf, {true}), "{| a: string, b: (number, X...) -> () |}"); @@ -787,7 +787,7 @@ local d: Y ()> TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Y = { a: T } @@ -811,7 +811,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors2") TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors3") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Y = { a: (T) -> U... } @@ -824,7 +824,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors3") TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors4") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type Packed = (T) -> T @@ -935,7 +935,7 @@ type R = { m: F } LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(*lookupType("R"), {true}), "t1 where t1 = { m: (t1) -> (t1) -> () }"); else CHECK_EQ(toString(*lookupType("R"), {true}), "t1 where t1 = {| m: (t1) -> (t1) -> () |}"); @@ -951,7 +951,7 @@ a = b LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { const std::string expected = "Type\n" @@ -1065,7 +1065,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "detect_cyclic_typepacks2") TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function foo(...: string): number @@ -1094,7 +1094,7 @@ TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments_free") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK( toString(result.errors.at(0)) == "Type pack '...number' could not be converted into 'boolean'; type ...number.tail() (...number) is not a subtype of boolean (boolean)" @@ -1106,7 +1106,7 @@ TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments_free") TEST_CASE_FIXTURE(BuiltinsFixture, "type_packs_with_tails_in_vararg_adjustment") { std::optional sff; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) sff = {FFlag::LuauInstantiateInSubtyping, true}; CheckResult result = check(R"( @@ -1127,7 +1127,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_packs_with_tails_in_vararg_adjustment") TEST_CASE_FIXTURE(BuiltinsFixture, "generalize_expectedTypes_with_proper_scope") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::LuauInstantiateInSubtyping, true}, }; diff --git a/Dependencies/luau/Luau/tests/TypeInfer.typestates.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.typestates.test.cpp index 19117447..0bce7546 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.typestates.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.typestates.test.cpp @@ -3,7 +3,7 @@ #include "doctest.h" -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) using namespace Luau; @@ -11,7 +11,7 @@ namespace { struct TypeStateFixture : BuiltinsFixture { - ScopedFastFlag dcr{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag dcr{FFlag::LuauSolverV2, true}; }; } // namespace @@ -73,7 +73,7 @@ TEST_CASE_FIXTURE(TypeStateFixture, "parameter_x_was_constrained_by_two_types") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { // `y` is annotated `string | number` which is explicitly not compatible with `string?` // as such, we produce an error here for that mismatch. @@ -392,7 +392,7 @@ TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions") TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_future_assignments") { // early return if the flag isn't set since this is blocking gated commits - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -459,7 +459,9 @@ TEST_CASE_FIXTURE(TypeStateFixture, "typestates_preserve_error_suppression") TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_preserve_error_suppression_properties") { // early return if the flag isn't set since this is blocking gated commits - if (!FFlag::DebugLuauDeferredConstraintResolution) + // unconditional return + // CLI-117098 Type states with error suppressing properties doesn't infer the correct type for properties. + if (!FFlag::LuauSolverV2 || FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -475,7 +477,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_preserve_error_suppression_proper TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_do_not_apply_to_the_initial_local_definition") { // early return if the flag isn't set since this is blocking gated commits - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -493,7 +495,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_do_not_apply_to_the_initial_local TEST_CASE_FIXTURE(Fixture, "typestate_globals") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; loadDefinition(R"( declare foo: string | number @@ -510,7 +512,7 @@ TEST_CASE_FIXTURE(Fixture, "typestate_globals") TEST_CASE_FIXTURE(Fixture, "typestate_unknown_global") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( x = 5 diff --git a/Dependencies/luau/Luau/tests/TypeInfer.unionTypes.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.unionTypes.test.cpp index 3a880d6d..6cdec4af 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.unionTypes.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.unionTypes.test.cpp @@ -8,7 +8,8 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections) TEST_SUITE_BEGIN("UnionTypes"); @@ -35,7 +36,7 @@ TEST_CASE_FIXTURE(Fixture, "return_types_can_be_disjoint") { // CLI-114134 We need egraphs to consistently reduce the cyclic union // introduced by the increment here. - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local count = 0 @@ -121,7 +122,7 @@ TEST_CASE_FIXTURE(Fixture, "optional_arguments") TEST_CASE_FIXTURE(Fixture, "optional_arguments_table") { // CLI-115588 - Bidirectional inference does not happen for assignments - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local a:{a:string, b:string?} @@ -236,7 +237,7 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_missing_property") REQUIRE(mup); CHECK_EQ("Key 'x' is missing from 'B' in the type 'A | B'", toString(result.errors[0])); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("(A | B) -> number", toString(requireType("f"))); else CHECK_EQ("(A | B) -> *error-type*", toString(requireType("f"))); @@ -410,7 +411,7 @@ TEST_CASE_FIXTURE(Fixture, "optional_assignment_errors_2") LUAU_REQUIRE_ERROR_COUNT(1, result); auto s = toString(result.errors[0]); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("Value of type '({ x: number } & { y: number })?' could be nil", s); else CHECK_EQ("Value of type '({| x: number |} & {| y: number |})?' could be nil", s); @@ -472,7 +473,7 @@ end TEST_CASE_FIXTURE(Fixture, "unify_unsealed_table_union_check") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local x = { x = 3 } @@ -533,7 +534,7 @@ end LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ( toString(result.errors[0]), @@ -565,7 +566,7 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_union_all") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK(toString(result.errors[0]) == "Type '{ w: number }' could not be converted into 'X | Y | Z'"); else CHECK_EQ(toString(result.errors[0]), R"(Type 'a' could not be converted into 'X | Y | Z'; none of the union options are compatible)"); @@ -580,7 +581,7 @@ local a: X? = { w = 4 } )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("Type '{ w: number }' could not be converted into 'X?'" == toString(result.errors[0])); else { @@ -636,12 +637,17 @@ TEST_CASE_FIXTURE(Fixture, "indexing_into_a_cyclic_union_doesnt_crash") end )"); - LUAU_REQUIRE_ERROR_COUNT(1, result); + // this is a cyclic union of number arrays, so it _is_ a table, even if it's a nonsense type. + // no need to generate a NotATable error here. + if (FFlag::LuauAcceptIndexingTableUnionsIntersections) + LUAU_REQUIRE_NO_ERRORS(result); + else + LUAU_REQUIRE_ERROR_COUNT(1, result); } TEST_CASE_FIXTURE(BuiltinsFixture, "table_union_write_indirect") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type A = { x: number, y: (number) -> string } | { z: number, y: (number) -> string } @@ -717,7 +723,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_generic_typepack_functions") TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generics") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f() @@ -737,7 +743,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generics") TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generic_typepacks") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f() @@ -758,7 +764,7 @@ could not be converted into TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_arities") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(x : (number) -> number?) @@ -777,7 +783,7 @@ could not be converted into TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_arities") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(x : () -> (number | string)) @@ -796,7 +802,7 @@ could not be converted into TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_variadics") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(x : (...nil) -> (...number?)) @@ -823,7 +829,7 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_variadics") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK(R"(Type '(number) -> ()' @@ -842,7 +848,7 @@ could not be converted into TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_variadics") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(x : () -> (number?, ...number)) @@ -861,7 +867,7 @@ could not be converted into TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -878,7 +884,7 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types") TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types_2") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -910,7 +916,7 @@ TEST_CASE_FIXTURE(Fixture, "union_table_any_property") TEST_CASE_FIXTURE(Fixture, "union_function_any_args") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(sup : ((...any) -> (...any))?, sub : ((number) -> (...any))) @@ -934,7 +940,7 @@ TEST_CASE_FIXTURE(Fixture, "optional_any") TEST_CASE_FIXTURE(Fixture, "generic_function_with_optional_arg") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function f(x : T?) : {T} @@ -968,7 +974,7 @@ TEST_CASE_FIXTURE(Fixture, "lookup_prop_of_intersection_containing_unions") TEST_CASE_FIXTURE(Fixture, "suppress_errors_for_prop_lookup_of_a_union_that_includes_error") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; registerHiddenTypes(&frontend); diff --git a/Dependencies/luau/Luau/tests/TypeInfer.unknownnever.test.cpp b/Dependencies/luau/Luau/tests/TypeInfer.unknownnever.test.cpp index 42007078..0c62d0b6 100644 --- a/Dependencies/luau/Luau/tests/TypeInfer.unknownnever.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeInfer.unknownnever.test.cpp @@ -6,7 +6,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); TEST_SUITE_BEGIN("TypeInferUnknownNever"); @@ -118,7 +118,7 @@ TEST_CASE_FIXTURE(Fixture, "type_packs_containing_never_is_itself_uninhabitable" local x, y, z = f() )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK("Function only returns 2 values, but 3 are required here" == toString(result.errors[0])); @@ -149,7 +149,7 @@ TEST_CASE_FIXTURE(Fixture, "type_packs_containing_never_is_itself_uninhabitable2 LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ("string", toString(requireType("x1"))); CHECK_EQ("never", toString(requireType("x2"))); @@ -193,12 +193,20 @@ TEST_CASE_FIXTURE(Fixture, "call_never") TEST_CASE_FIXTURE(Fixture, "assign_to_local_which_is_never") { + // CLI-117119 - What do we do about assigning to never? CheckResult result = check(R"( local t: never t = 3 )"); - LUAU_REQUIRE_NO_ERRORS(result); + if (FFlag::LuauSolverV2) + { + LUAU_REQUIRE_ERROR_COUNT(1, result); + } + else + { + LUAU_REQUIRE_NO_ERRORS(result); + } } TEST_CASE_FIXTURE(Fixture, "assign_to_global_which_is_never") @@ -257,6 +265,9 @@ TEST_CASE_FIXTURE(Fixture, "pick_never_from_variadic_type_pack") TEST_CASE_FIXTURE(Fixture, "index_on_union_of_tables_for_properties_that_is_never") { + // CLI-117116 - We are erroneously warning when passing a valid table literal where we expect a union of tables. + if (FFlag::LuauSolverV2) + return; CheckResult result = check(R"( type Disjoint = {foo: never, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"} @@ -274,6 +285,9 @@ TEST_CASE_FIXTURE(Fixture, "index_on_union_of_tables_for_properties_that_is_neve TEST_CASE_FIXTURE(Fixture, "index_on_union_of_tables_for_properties_that_is_sorta_never") { + // CLI-117116 - We are erroneously warning when passing a valid table literal where we expect a union of tables. + if (FFlag::LuauSolverV2) + return; CheckResult result = check(R"( type Disjoint = {foo: string, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"} @@ -321,7 +335,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_unify_operands_if_one_of_the_operand_is_never_i LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("(nil, unknown) -> boolean", toString(requireType("ord"))); else CHECK_EQ("(nil, a) -> boolean", toString(requireType("ord"))); @@ -335,7 +349,7 @@ TEST_CASE_FIXTURE(Fixture, "math_operators_and_never") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK(get(result.errors[0])); @@ -365,7 +379,7 @@ TEST_CASE_FIXTURE(Fixture, "compare_never") TEST_CASE_FIXTURE(Fixture, "lti_error_at_declaration_for_never_normalizations") { - ScopedFastFlag sff_DebugLuauDeferredConstraintResolution{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff_LuauSolverV2{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local function num(x: number) end @@ -389,7 +403,7 @@ TEST_CASE_FIXTURE(Fixture, "lti_error_at_declaration_for_never_normalizations") TEST_CASE_FIXTURE(Fixture, "lti_permit_explicit_never_annotation") { - ScopedFastFlag sff_DebugLuauDeferredConstraintResolution{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff_LuauSolverV2{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( local function num(x: number) end diff --git a/Dependencies/luau/Luau/tests/TypePath.test.cpp b/Dependencies/luau/Luau/tests/TypePath.test.cpp index 54ec14b8..2481f27a 100644 --- a/Dependencies/luau/Luau/tests/TypePath.test.cpp +++ b/Dependencies/luau/Luau/tests/TypePath.test.cpp @@ -15,17 +15,17 @@ using namespace Luau; using namespace Luau::TypePath; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_DYNAMIC_FASTINT(LuauTypePathMaximumTraverseSteps); struct TypePathFixture : Fixture { - ScopedFastFlag sff1{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff1{FFlag::LuauSolverV2, true}; }; struct TypePathBuiltinsFixture : BuiltinsFixture { - ScopedFastFlag sff1{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff1{FFlag::LuauSolverV2, true}; }; TEST_SUITE_BEGIN("TypePathManipulation"); @@ -522,7 +522,7 @@ TEST_SUITE_BEGIN("TypePathToString"); TEST_CASE("field") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; CHECK(toString(PathBuilder().prop("foo").build()) == R"(["foo"])"); @@ -551,7 +551,7 @@ TEST_CASE("empty_path") TEST_CASE("prop") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, }; Path p = PathBuilder().prop("foo").build(); @@ -592,7 +592,7 @@ TEST_CASE("fields") TEST_CASE("chained") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CHECK( PathBuilder().index(0).readProp("foo").mt().readProp("bar").args().index(1).build() == diff --git a/Dependencies/luau/Luau/tests/TypeVar.test.cpp b/Dependencies/luau/Luau/tests/TypeVar.test.cpp index 17c0c34b..9e21b1e0 100644 --- a/Dependencies/luau/Luau/tests/TypeVar.test.cpp +++ b/Dependencies/luau/Luau/tests/TypeVar.test.cpp @@ -304,7 +304,7 @@ TEST_CASE_FIXTURE(Fixture, "substitution_skip_failure") REQUIRE(!anyification.normalizationTooComplex); REQUIRE(any.has_value()); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK_EQ("{ f: t1 } where t1 = () -> { f: () -> { f: ({ f: t1 }) -> (), signal: { f: (any) -> () } } }", toString(*any)); else CHECK_EQ("{| f: t1 |} where t1 = () -> {| f: () -> {| f: ({| f: t1 |}) -> (), signal: {| f: (any) -> () |} |} |}", toString(*any)); diff --git a/Dependencies/luau/Luau/tests/Unifier2.test.cpp b/Dependencies/luau/Luau/tests/Unifier2.test.cpp index 8efb2870..65734103 100644 --- a/Dependencies/luau/Luau/tests/Unifier2.test.cpp +++ b/Dependencies/luau/Luau/tests/Unifier2.test.cpp @@ -12,7 +12,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) struct Unifier2Fixture { @@ -23,7 +23,7 @@ struct Unifier2Fixture Unifier2 u2{NotNull{&arena}, NotNull{&builtinTypes}, NotNull{&scope}, NotNull{&iceReporter}}; ToStringOptions opts; - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; std::pair freshType() { diff --git a/Dependencies/luau/Luau/tests/VisitType.test.cpp b/Dependencies/luau/Luau/tests/VisitType.test.cpp index a1a17e8e..186afaa5 100644 --- a/Dependencies/luau/Luau/tests/VisitType.test.cpp +++ b/Dependencies/luau/Luau/tests/VisitType.test.cpp @@ -9,21 +9,34 @@ using namespace Luau; LUAU_FASTINT(LuauVisitRecursionLimit); -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) TEST_SUITE_BEGIN("VisitType"); TEST_CASE_FIXTURE(Fixture, "throw_when_limit_is_exceeded") { - ScopedFastInt sfi{FInt::LuauVisitRecursionLimit, 3}; - - CheckResult result = check(R"( - local t : {a: {b: {c: {d: {e: boolean}}}}} - )"); - - TypeId tType = requireType("t"); - - CHECK_THROWS_AS(toString(tType), RecursionLimitException); + if (FFlag::LuauSolverV2) + { + CheckResult result = check(R"( + local t : {a: {b: {c: {d: {e: boolean}}}}} + )"); + ScopedFastInt sfi{FInt::LuauVisitRecursionLimit, 3}; + TypeId tType = requireType("t"); + + CHECK_THROWS_AS(toString(tType), RecursionLimitException); + } + else + { + ScopedFastInt sfi{FInt::LuauVisitRecursionLimit, 3}; + + CheckResult result = check(R"( + local t : {a: {b: {c: {d: {e: boolean}}}}} + )"); + + TypeId tType = requireType("t"); + + CHECK_THROWS_AS(toString(tType), RecursionLimitException); + } } TEST_CASE_FIXTURE(Fixture, "dont_throw_when_limit_is_high_enough") @@ -48,7 +61,7 @@ TEST_CASE_FIXTURE(Fixture, "some_free_types_do_not_have_bounds") TEST_CASE_FIXTURE(Fixture, "some_free_types_have_bounds") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; Scope scope{builtinTypes->anyTypePack}; Type t{FreeType{&scope, builtinTypes->neverType, builtinTypes->numberType}}; diff --git a/Dependencies/luau/Luau/tests/conformance/vector.lua b/Dependencies/luau/Luau/tests/conformance/vector.lua index 7e4a9a3e..3c70cfb0 100644 --- a/Dependencies/luau/Luau/tests/conformance/vector.lua +++ b/Dependencies/luau/Luau/tests/conformance/vector.lua @@ -164,4 +164,23 @@ do assert(larget[vector(-0, 0, 0)] == 42) end +local function numvectemporary() + local proptab = {} + + proptab.vec3compsum = function(vec: vector) + local num = vec.X + vec.Y + local tmp = vec / num + local num2 = num * 2 + return tmp, num2 + end + + local a, b = proptab.vec3compsum(vector(2, 6, 0)) + + assert(a.X == 0.25) + assert(a.Y == 0.75) + assert(b == 16) +end + +numvectemporary() + return 'OK' diff --git a/Dependencies/luau/Luau/tests/main.cpp b/Dependencies/luau/Luau/tests/main.cpp index 42c3ad80..4612829b 100644 --- a/Dependencies/luau/Luau/tests/main.cpp +++ b/Dependencies/luau/Luau/tests/main.cpp @@ -51,7 +51,7 @@ static bool skipFastFlag(const char* flagName) if (strncmp(flagName, "Debug", 5) == 0) return true; - if (strcmp(flagName, "StudioReportLuauAny") == 0) + if (strcmp(flagName, "StudioReportLuauAny2") == 0) return true; return false; diff --git a/Dependencies/luau/Luau/tests/require/with_config/src/.luaurc b/Dependencies/luau/Luau/tests/require/with_config/src/.luaurc index 87064bbc..8c1ae683 100644 --- a/Dependencies/luau/Luau/tests/require/with_config/src/.luaurc +++ b/Dependencies/luau/Luau/tests/require/with_config/src/.luaurc @@ -1,6 +1,7 @@ { "paths": ["../ProjectLuauLibraries"], "aliases": { - "dep": "dependency" + "dep": "dependency", + "subdir": "subdirectory" } } diff --git a/Dependencies/luau/Luau/tests/require/with_config/src/directory_alias_requirer.luau b/Dependencies/luau/Luau/tests/require/with_config/src/directory_alias_requirer.luau new file mode 100644 index 00000000..3b19d4ff --- /dev/null +++ b/Dependencies/luau/Luau/tests/require/with_config/src/directory_alias_requirer.luau @@ -0,0 +1 @@ +return(require("@subdir/subdirectory_dependency")) diff --git a/Dependencies/luau/Luau/tests/require/with_config/src/subdirectory/subdirectory_dependency.luau b/Dependencies/luau/Luau/tests/require/with_config/src/subdirectory/subdirectory_dependency.luau new file mode 100644 index 00000000..8bbd0beb --- /dev/null +++ b/Dependencies/luau/Luau/tests/require/with_config/src/subdirectory/subdirectory_dependency.luau @@ -0,0 +1 @@ +return {"result from subdirectory_dependency"} diff --git a/Dependencies/luau/Luau/tools/faillist.txt b/Dependencies/luau/Luau/tools/faillist.txt index d785d14d..e69de29b 100644 --- a/Dependencies/luau/Luau/tools/faillist.txt +++ b/Dependencies/luau/Luau/tools/faillist.txt @@ -1,45 +0,0 @@ -AutocompleteTest.anonymous_autofilled_generic_on_argument_type_pack_vararg -AutocompleteTest.anonymous_autofilled_generic_type_pack_vararg -AutocompleteTest.autocomplete_string_singletons -AutocompleteTest.suggest_table_keys -AutocompleteTest.type_correct_suggestion_for_overloads -AutocompleteTest.type_correct_suggestion_in_table -GenericsTests.do_not_always_instantiate_generic_intersection_types -GenericsTests.error_detailed_function_mismatch_generic_pack -GenericsTests.error_detailed_function_mismatch_generic_types -GenericsTests.factories_of_generics -GenericsTests.generic_argument_count_too_few -GenericsTests.generic_argument_count_too_many -GenericsTests.generic_factories -GenericsTests.generic_functions_in_types -GenericsTests.generic_type_functions_work_in_subtyping -GenericsTests.generic_type_pack_unification1 -GenericsTests.generic_type_pack_unification2 -GenericsTests.generic_type_pack_unification3 -GenericsTests.higher_rank_polymorphism_should_not_accept_instantiated_arguments -GenericsTests.instantiated_function_argument_names -GenericsTests.properties_can_be_instantiated_polytypes -GenericsTests.quantify_functions_even_if_they_have_an_explicit_generic -IntersectionTypes.intersect_metatables -IntersectionTypes.intersect_saturate_overloaded_functions -IntersectionTypes.intersection_of_tables -IntersectionTypes.intersection_of_tables_with_top_properties -IntersectionTypes.less_greedy_unification_with_intersection_types -IntersectionTypes.overloaded_functions_mentioning_generic -IntersectionTypes.overloaded_functions_mentioning_generic_packs -IntersectionTypes.overloaded_functions_mentioning_generics -IntersectionTypes.overloaded_functions_returning_intersections -IntersectionTypes.overloadeded_functions_with_never_arguments -ModuleTests.clone_self_property -Negations.cofinite_strings_can_be_compared_for_equality -Normalize.higher_order_function_with_annotation -RefinementTest.refine_a_param_that_got_resolved_during_constraint_solving_stage -RefinementTest.x_as_any_if_x_is_instance_elseif_x_is_table -RefinementTest.x_is_not_instance_or_else_not_part -TypeInferFunctions.simple_unannotated_mutual_recursion -TypeInferUnknownNever.assign_to_local_which_is_never -TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_never -TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_sorta_never -TypePackTests.fuzz_typepack_iter_follow_2 -TypeStatesTest.typestates_preserve_error_suppression_properties -VisitType.throw_when_limit_is_exceeded diff --git a/Dependencies/luau/Luau/tools/fuzz/requirements.txt b/Dependencies/luau/Luau/tools/fuzz/requirements.txt index 0f591a2b..297ba324 100644 --- a/Dependencies/luau/Luau/tools/fuzz/requirements.txt +++ b/Dependencies/luau/Luau/tools/fuzz/requirements.txt @@ -1,2 +1,2 @@ -Jinja2==3.1.2 +Jinja2==3.1.4 MarkupSafe==2.1.3 diff --git a/Dependencies/luau/Luau/tools/lldb_formatters.py b/Dependencies/luau/Luau/tools/lldb_formatters.py index 9be6cade..7e957c75 100644 --- a/Dependencies/luau/Luau/tools/lldb_formatters.py +++ b/Dependencies/luau/Luau/tools/lldb_formatters.py @@ -371,7 +371,7 @@ def luau_typepath_property_summary(valobj, internal_dict, options): read_write = False try: fflag_valobj = valobj.GetFrame().GetValueForVariablePath( - "FFlag::DebugLuauDeferredConstraintResolution::value") + "FFlag::LuauSolverV2::value") read_write = fflag_valobj.GetValue() == "true" except Exception as e: diff --git a/Dependencies/luau/Luau/tools/test_dcr.py b/Dependencies/luau/Luau/tools/test_dcr.py index 30940c63..3de92b37 100644 --- a/Dependencies/luau/Luau/tools/test_dcr.py +++ b/Dependencies/luau/Luau/tools/test_dcr.py @@ -136,7 +136,7 @@ def main(): failList = loadFailList() - flags = ["true", "DebugLuauDeferredConstraintResolution"] + flags = ["true", "LuauSolverV2"] commandLine = [args.path, "--reporters=xml", "--fflags=" + ",".join(flags)] diff --git a/Dependencies/luau/Version-0.639 b/Dependencies/luau/Version-0.643 similarity index 100% rename from Dependencies/luau/Version-0.639 rename to Dependencies/luau/Version-0.643