diff --git a/Analysis/include/Luau/BuiltinDefinitions.h b/Analysis/include/Luau/BuiltinDefinitions.h index 94b0d87f..71e50580 100644 --- a/Analysis/include/Luau/BuiltinDefinitions.h +++ b/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/Analysis/include/Luau/Set.h b/Analysis/include/Luau/Set.h index 274375cf..613e5aa5 100644 --- a/Analysis/include/Luau/Set.h +++ b/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/Analysis/include/Luau/Type.h b/Analysis/include/Luau/Type.h index 2b2cb960..a43dbff9 100644 --- a/Analysis/include/Luau/Type.h +++ b/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/Analysis/include/Luau/TypeChecker2.h b/Analysis/include/Luau/TypeChecker2.h index 981fdfe6..0faf036d 100644 --- a/Analysis/include/Luau/TypeChecker2.h +++ b/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/Analysis/include/Luau/VisitType.h b/Analysis/include/Luau/VisitType.h index e588d06b..e943cced 100644 --- a/Analysis/include/Luau/VisitType.h +++ b/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/Analysis/src/AnyTypeSummary.cpp b/Analysis/src/AnyTypeSummary.cpp index ca59814c..85f567af 100644 --- a/Analysis/src/AnyTypeSummary.cpp +++ b/Analysis/src/AnyTypeSummary.cpp @@ -692,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/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index 243834f8..c8470373 100644 --- a/Analysis/src/AstQuery.cpp +++ b/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/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index 03c9a956..ee865edd 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/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,7 +142,7 @@ 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}}; @@ -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/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index e565f6bf..21ae0f11 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/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/Analysis/src/Clone.cpp b/Analysis/src/Clone.cpp index 7446846a..4af3e7f8 100644 --- a/Analysis/src/Clone.cpp +++ b/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 @@ private: Property shallowClone(const Property& p) { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { std::optional cloneReadTy; if (auto ty = p.readTy) diff --git a/Analysis/src/DataFlowGraph.cpp b/Analysis/src/DataFlowGraph.cpp index 73c2193c..9c42e4d8 100644 --- a/Analysis/src/DataFlowGraph.cpp +++ b/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)) @@ -547,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 @@ -781,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); @@ -1064,7 +1064,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) 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) @@ -1531,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/Analysis/src/Instantiation.cpp b/Analysis/src/Instantiation.cpp index 0c610aba..4b6d1115 100644 --- a/Analysis/src/Instantiation.cpp +++ b/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/Analysis/src/Linter.cpp b/Analysis/src/Linter.cpp index 23457f4c..c4f46c84 100644 --- a/Analysis/src/Linter.cpp +++ b/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 @@ private: bool visit(AstTypeTable* node) override { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { struct Rec { diff --git a/Analysis/src/Module.cpp b/Analysis/src/Module.cpp index f9a3f67a..dc6c3fc0 100644 --- a/Analysis/src/Module.cpp +++ b/Analysis/src/Module.cpp @@ -14,7 +14,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAGVARIABLE(LuauSkipEmptyInstantiations, false); namespace Luau @@ -220,7 +220,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/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index 408d8cba..2db2f40c 100644 --- a/Analysis/src/Normalize.cpp +++ b/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,7 +3427,7 @@ 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}}; @@ -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/Analysis/src/Scope.cpp b/Analysis/src/Scope.cpp index 791167c8..27894505 100644 --- a/Analysis/src/Scope.cpp +++ b/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/Analysis/src/Simplify.cpp b/Analysis/src/Simplify.cpp index ca9d5898..9138b50e 100644 --- a/Analysis/src/Simplify.cpp +++ b/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 @@ -1398,7 +1398,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 +1413,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 +1424,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/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index 3eeb2095..526d8212 100644 --- a/Analysis/src/Substitution.cpp +++ b/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/Analysis/src/Symbol.cpp b/Analysis/src/Symbol.cpp index 4b808f19..5e5b9d8c 100644 --- a/Analysis/src/Symbol.cpp +++ b/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/Analysis/src/ToDot.cpp b/Analysis/src/ToDot.cpp index aa2dc1e3..4408063f 100644 --- a/Analysis/src/ToDot.cpp +++ b/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/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index 879ddfab..f0850835 100644 --- a/Analysis/src/ToString.cpp +++ b/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/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index ffc4a97e..b024fdd2 100644 --- a/Analysis/src/Type.cpp +++ b/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/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index beb8e650..ee3d7a9f 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/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,2948 +264,6 @@ struct InternalTypeFunctionFinder : TypeOnceVisitor } }; -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 - ) - : 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) - { - } - - static bool allowsNoReturnValues(const TypePackId tp) - { - for (TypeId ty : tp) - { - if (!get(follow(ty))) - return false; - } - - return true; - } - - static Location 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); - } - - return loc; - } - - bool 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") - { - // assert() will error because it is missing the first argument - if (call->args.size == 0) - 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 (AstStatIf* stat = node->as()) - { - if (hasBreak(stat->thenbody)) - return true; - - if (stat->elsebody && hasBreak(stat->elsebody)) - return true; - - return false; - } - - 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) - { - 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 (getFallthrough(stat->body.data[i]) == nullptr) - return nullptr; - } - - 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 (stat->elsebody) - { - if (const AstStat* elsef = getFallthrough(stat->elsebody)) - return elsef; - - return nullptr; - } - else - return stat; - } - - if (node->is()) - return nullptr; - - if (const AstStatExpr* stat = node->as()) - { - if (AstExprCall* call = stat->expr->as(); call && isErrorCall(call)) - 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; - } - - if (const AstStatRepeat* stat = node->as()) - { - if (AstExprConstantBool* expr = stat->condition->as()) - { - if (!expr->value && !hasBreak(stat->body)) - return nullptr; - } - - if (getFallthrough(stat->body) == nullptr) - return nullptr; - - return node; - } - - return node; - } - - std::optional pushStack(AstNode* node) - { - if (Scope** scope = module->astScopes.find(node)) - return StackPusher{stack, *scope}; - else - return std::nullopt; - } - - void checkForInternalTypeFunction(TypeId ty, Location location) - { - InternalTypeFunctionFinder finder(functionDeclStack); - finder.traverse(ty); - - for (TypeId internal : finder.internalFunctions) - reportError(WhereClauseNeeded{internal}, location); - - for (TypePackId internal : finder.internalPackFunctions) - reportError(PackWhereClauseNeeded{internal}, location); - } - - TypeId checkForTypeFunctionInhabitance(TypeId instance, Location location) - { - if (seenTypeFunctionInstances.find(instance)) - return instance; - seenTypeFunctionInstances.insert(instance); - - 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 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; - } - - 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); - - TypePackId* tp = module->astTypePacks.find(expr); - if (tp) - return checkForTypeFunctionInhabitance(flattenPack(*tp), expr->location); - - return builtinTypes->anyType; - } - - TypeId lookupAnnotation(AstType* annotation) - { - if (FFlag::DebugLuauMagicTypes) - { - if (auto ref = annotation->as(); ref && ref->name == "_luau_print" && ref->parameters.size > 0) - { - 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* 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 lookupExpectedType(AstExpr* expr) - { - if (TypeId* ty = module->astExpectedTypes.find(expr)) - return follow(*ty); - - return builtinTypes->anyType; - } - - TypePackId lookupExpectedPack(AstExpr* expr, TypeArena& arena) - { - if (TypeId* ty = module->astExpectedTypes.find(expr)) - return arena.addTypePack(TypePack{{follow(*ty)}, std::nullopt}); - - return builtinTypes->anyTypePack; - } - - TypePackId reconstructPack(AstArray exprs, TypeArena& arena) - { - if (exprs.size == 0) - return arena.addTypePack(TypePack{{}, std::nullopt}); - - std::vector head; - - for (size_t i = 0; i < exprs.size - 1; ++i) - { - head.push_back(lookupType(exprs.data[i])); - } - - TypePackId tail = lookupPack(exprs.data[exprs.size - 1]); - return arena.addTypePack(TypePack{head, tail}); - } - - Scope* findInnermostScope(Location location) - { - Scope* bestScope = module->getModuleScope().get(); - - bool didNarrow; - do - { - didNarrow = false; - for (auto scope : bestScope->children) - { - if (scope->location.encloses(location)) - { - 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"); - } - - void visit(AstStatBlock* block) - { - auto StackPusher = pushStack(block); - - for (AstStat* statement : block->body) - visit(statement); - } - - void visit(AstStatIf* ifStatement) - { - { - InConditionalContext flipper{&typeContext}; - visit(ifStatement->condition, ValueContext::RValue); - } - - visit(ifStatement->thenbody); - if (ifStatement->elsebody) - visit(ifStatement->elsebody); - } - - void visit(AstStatWhile* whileStatement) - { - visit(whileStatement->condition, ValueContext::RValue); - visit(whileStatement->body); - } - - void visit(AstStatRepeat* repeatStatement) - { - visit(repeatStatement->body); - visit(repeatStatement->condition, ValueContext::RValue); - } - - void visit(AstStatBreak*) {} - - void visit(AstStatContinue*) {} - - void visit(AstStatReturn* ret) - { - Scope* scope = findInnermostScope(ret->location); - TypePackId expectedRetType = scope->returnType; - - TypeArena* arena = &module->internalTypes; - TypePackId actualRetType = reconstructPack(ret->list, *arena); - - testIsSubtype(actualRetType, expectedRetType, ret->location); - - for (AstExpr* expr : ret->list) - visit(expr, ValueContext::RValue); - } - - void visit(AstStatExpr* expr) - { - visit(expr->expr, ValueContext::RValue); - } - - void visit(AstStatLocal* local) - { - 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) - { - AstLocal* var = i < local->vars.size ? local->vars.data[i] : nullptr; - - 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); - - 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); - } - } - - 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 - ); - } - } - } - } - - void visit(AstStatFor* forStatement) - { - if (forStatement->var->annotation) - { - visit(forStatement->var->annotation); - - TypeId annotatedType = lookupAnnotation(forStatement->var->annotation); - testIsSubtype(builtinTypes->numberType, annotatedType, forStatement->var->location); - } - - auto checkNumber = [this](AstExpr* expr) - { - if (!expr) - return; - - visit(expr, ValueContext::RValue); - testIsSubtype(lookupType(expr), builtinTypes->numberType, expr->location); - }; - - checkNumber(forStatement->from); - checkNumber(forStatement->to); - checkNumber(forStatement->step); - - visit(forStatement->body); - } - - void visit(AstStatForIn* forInStatement) - { - for (AstLocal* local : forInStatement->vars) - { - if (local->annotation) - visit(local->annotation); - } - - for (AstExpr* expr : forInStatement->values) - visit(expr, ValueContext::RValue); - - visit(forInStatement->body); - - // 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; - - 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]; - - // 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)); - } - - // 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])); - } - - // 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 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]); - - auto checkFunction = [this, &arena, &forInStatement, &variableTypes](const FunctionType* iterFtv, std::vector iterTys, bool isMm) - { - 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. - - // It may be invoked with 0 or 1 argument on the first iteration. - // This depends on the types in iterateePack and therefore - // iteratorTypes. - - // 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); - - 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); - } - - 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 (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); - } - - 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 - 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 (const FunctionType* iterMmFtv = get(*instantiatedIterMmTy)) - { - TypePackId argPack = arena.addTypePack({iteratorTy}); - testIsSubtype(argPack, iterMmFtv->argTypes, forInStatement->values.data[0]->location); - - 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; - } - - TypeId nextFn = follow(mmIteratorTypes.head[0]); - - 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); - } - } - else - { - reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location); - } - } - else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *iterMmTy)) - { - // 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 - { - reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location); - } - } - else if (iteratorNorm && iteratorNorm->hasTables()) - { - // Ok. All tables can be iterated. - } - else if (!iteratorNorm || !iteratorNorm->shouldSuppressErrors()) - { - reportError(CannotCallNonFunction{iteratorTy}, forInStatement->values.data[0]->location); - } - } - - std::optional 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; - } - - // this should only be called if the type of `lhs` is `never`. - void reportErrorsFromAssigningToNever(AstExpr* lhs, TypeId rhsType) - { - - if (auto indexName = lhs->as()) - { - TypeId indexedType = lookupType(indexName->expr); - - // if it's already never, I don't think we have anything to do here. - if (get(indexedType)) - 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; - - 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); - } - } - - void 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); - - AstExpr* rhs = assign->values.data[i]; - visit(rhs, ValueContext::RValue); - TypeId rhsType = lookupType(rhs); - - if (get(lhsType)) - { - reportErrorsFromAssigningToNever(lhs, rhsType); - continue; - } - - bool ok = testIsSubtype(rhsType, lhsType, 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); - - testIsSubtype(*resultTy, varTy, stat->location); - } - - void visit(AstStatFunction* stat) - { - visit(stat->name, ValueContext::LValue); - visit(stat->func); - } - - void visit(AstStatLocalFunction* stat) - { - visit(stat->func); - } - - void visit(const AstTypeList* typeList) - { - for (AstType* ty : typeList->types) - visit(ty); - - if (typeList->tailType) - visit(typeList->tailType); - } - - void visit(AstStatTypeAlias* stat) - { - visitGenerics(stat->generics, stat->genericPacks); - visit(stat->type); - } - - void visit(AstStatTypeFunction* stat) - { - // TODO: add type checking for user-defined type functions - - reportError(TypeError{stat->location, GenericError{"This syntax is not supported"}}); - } - - void visit(AstTypeList types) - { - for (AstType* type : types.types) - visit(type); - if (types.tailType) - visit(types.tailType); - } - - void visit(AstStatDeclareFunction* stat) - { - visitGenerics(stat->generics, stat->genericPacks); - visit(stat->params); - visit(stat->retTypes); - } - - void visit(AstStatDeclareGlobal* stat) - { - visit(stat->type); - } - - void visit(AstStatDeclareClass* stat) - { - for (const AstDeclaredClassProp& prop : stat->props) - visit(prop.ty); - } - - void visit(AstStatError* stat) - { - for (AstExpr* expr : stat->expressions) - visit(expr, ValueContext::RValue); - - for (AstStat* s : stat->statements) - visit(s); - } - - void 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 visit(AstExprConstantNil* expr) - { -#if defined(LUAU_ENABLE_ASSERT) - TypeId actualType = lookupType(expr); - TypeId expectedType = builtinTypes->nilType; - NotNull scope{findInnermostScope(expr->location)}; - - 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. - - 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, scope); - if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType)) - reportError(TypeMismatch{inferredType, bestType}, expr->location); - } - - void visit(AstExprConstantNumber* expr) - { -#if defined(LUAU_ENABLE_ASSERT) - const TypeId bestType = builtinTypes->numberType; - const TypeId inferredType = lookupType(expr); - NotNull scope{findInnermostScope(expr->location)}; - - 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); - NotNull scope{findInnermostScope(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(AstExprLocal* expr) - { - // TODO! - } - - void visit(AstExprGlobal* expr) - { - NotNull scope = stack.back(); - if (!scope->lookup(expr->name)) - reportError(UnknownSymbol{expr->name.value, UnknownSymbol::Binding}, expr->location); - } - - void visit(AstExprVarargs* expr) - { - // TODO! - } - - // 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); - - 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) - { - NotNull scope{findInnermostScope(call->location)}; - SubtypingResult result = subtyping->isSubtype(*originalCallTy, *selectedOverloadTy, scope); - if (result.isSubtype) - fnTy = follow(*selectedOverloadTy); - - if (result.normalizationTooComplex) - { - 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); - } - - 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 (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); - } - - - - 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 - { - 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 - { - 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 (resolver.nonviableOverloads.size() <= 1 && resolver.arityMismatches.size() <= 1) - return; - - std::string s = "Available overloads: "; - - std::vector overloads; - if (resolver.nonviableOverloads.empty()) - { - for (const auto& [ty, p] : resolver.resolution) - { - if (p.first == OverloadResolver::TypeIsNotAFunction) - continue; - - overloads.push_back(ty); - } - } - else - { - for (const auto& [ty, _] : resolver.nonviableOverloads) - overloads.push_back(ty); - } - - if (overloads.size() <= 1) - return; - - for (size_t i = 0; i < overloads.size(); ++i) - { - if (i > 0) - s += (i == overloads.size() - 1) ? "; and " : "; "; - - s += toString(overloads[i]); - } - - reportError(ExtraInformation{std::move(s)}, call->func->location); - } - - void visit(AstExprCall* call) - { - visit(call->func, ValueContext::RValue); - - for (AstExpr* arg : call->args) - visit(arg, ValueContext::RValue); - - visitCall(call); - } - - std::optional tryStripUnionFromNil(TypeId ty) - { - if (const UnionType* utv = get(ty)) - { - if (!std::any_of(begin(utv), end(utv), isNil)) - return ty; - - std::vector result; - - for (TypeId option : utv) - { - if (!isNil(option)) - result.push_back(option); - } - - if (result.empty()) - return std::nullopt; - - return result.size() == 1 ? result[0] : module->internalTypes.addType(UnionType{std::move(result)}); - } - - return std::nullopt; - } - - TypeId stripFromNilAndReport(TypeId ty, const Location& location) - { - ty = follow(ty); - - if (auto utv = get(ty)) - { - if (!std::any_of(begin(utv), end(utv), isNil)) - return ty; - } - - 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 ty; - } - - void 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 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 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 - { - LUAU_ASSERT(tt || get(follow(metaTable->table))); - - reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location); - } - } - - void visit(AstExprIndexExpr* indexExpr, ValueContext context) - { - if (auto str = indexExpr->index->as()) - { - TypeId astIndexExprType = lookupType(indexExpr->index); - const std::string stringValue(str->value.data, str->value.size); - visitExprName(indexExpr->expr, indexExpr->location, stringValue, context, astIndexExprType); - return; - } - - 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 (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 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); - } - - void visit(AstExprFunction* fn) - { - auto StackPusher = pushStack(fn); - - visitGenerics(fn->generics, fn->genericPacks); - - TypeId inferredFnTy = lookupType(fn); - functionDeclStack.push_back(inferredFnTy); - - std::shared_ptr normalizedFnTy = normalizer.normalize(inferredFnTy); - if (!normalizedFnTy) - { - reportError(CodeTooComplex{}, fn->location); - } - else if (get(normalizedFnTy->errors)) - { - // 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); - - const FunctionType* inferredFtv = get(normalizedFnTy->functions.parts.front()); - LUAU_ASSERT(inferredFtv); - - // 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; - - for (const auto& arg : fn->args) - { - if (argIt == end(inferredFtv->argTypes)) - break; - - TypeId inferredArgTy = *argIt; - - if (arg->annotation) - { - // we need to typecheck any argument annotations themselves. - visit(arg->annotation); - - 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) - { - 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. - 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( - 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)); - } - - visit(fn->body); - - // 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) - { - 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 - ); - } - } - } - - functionDeclStack.pop_back(); - } - - void visit(AstExprTable* expr) - { - // TODO! - for (const AstExprTable::Item& item : expr->items) - { - if (item.key) - visit(item.key, ValueContext::LValue); - visit(item.value, ValueContext::RValue); - } - } - - void visit(AstExprUnary* expr) - { - visit(expr->expr, ValueContext::RValue); - - TypeId operandType = lookupType(expr->expr); - TypeId resultType = lookupType(expr); - - if (isErrorSuppressing(expr->expr->location, operandType)) - return; - - if (auto it = kUnaryOpMetamethods.find(expr->op); it != kUnaryOpMetamethods.end()) - { - std::optional mm = findMetatableEntry(builtinTypes, module->errors, operandType, it->second, expr->location); - if (mm) - { - if (const FunctionType* ftv = get(follow(*mm))) - { - if (std::optional ret = first(ftv->retTypes)) - { - if (expr->op == AstExprUnary::Op::Len) - { - testIsSubtype(follow(*ret), builtinTypes->numberType, expr->location); - } - } - else - { - 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; - } - - 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; - } - - return; - } - } - - if (expr->op == AstExprUnary::Op::Len) - { - DenseHashSet seen{nullptr}; - int recursionCount = 0; - std::shared_ptr nty = normalizer.normalize(operandType); - - if (nty && nty->shouldSuppressErrors()) - return; - - switch (normalizer.isInhabited(nty.get())) - { - case NormalizationResult::True: - break; - case NormalizationResult::False: - return; - case NormalizationResult::HitLimits: - reportError(NormalizationTooComplex{}, expr->location); - 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"); - } - } - - TypeId visit(AstExprBinary* expr, AstNode* overrideKey = nullptr) - { - visit(expr->left, ValueContext::RValue); - visit(expr->right, ValueContext::RValue); - - 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; - - 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) - { - auto testUnion = [&matches, builtinTypes = this->builtinTypes](const UnionType* utv, std::optional otherMt) - { - for (TypeId option : utv) - { - if (getMetatable(follow(option), builtinTypes) == otherMt) - { - 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) - { - 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(); - } - - 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); - } - - if (mm) - { - AstNode* key = expr; - if (overrideKey != nullptr) - key = overrideKey; - - 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}); - } - - 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{})}); - } - - TypeId expectedTy = module->internalTypes.addType(FunctionType(expectedArgs, expectedRets)); - - testIsSubtype(follow(*mm), expectedTy, expr->location); - - 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); - } - - 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); - } - - return builtinTypes->errorRecoveryType(); - } - } - else - { - reportError(CannotCallNonFunction{*mm}, 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 - ); - } - - 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(); - } - } - } - - 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()) - { - 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(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))) - { - case ErrorSuppression::Suppress: - return; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, expr->location); - return; - case ErrorSuppression::DoNotSuppress: - break; - } - - switch (normalizer.isInhabited(computedType)) - { - case NormalizationResult::True: - break; - case NormalizationResult::False: - return; - case NormalizationResult::HitLimits: - reportError(NormalizationTooComplex{}, expr->location); - return; - } - - switch (normalizer.isIntersectionInhabited(computedType, annotationType)) - { - case NormalizationResult::True: - return; - case NormalizationResult::False: - reportError(TypesAreUnrelated{computedType, annotationType}, expr->location); - break; - case NormalizationResult::HitLimits: - reportError(NormalizationTooComplex{}, expr->location); - break; - } - } - - void visit(AstExprIfElse* expr) - { - // TODO! - visit(expr->condition, ValueContext::RValue); - visit(expr->trueExpr, ValueContext::RValue); - visit(expr->falseExpr, ValueContext::RValue); - } - - void visit(AstExprInterpString* interpString) - { - for (AstExpr* expr : interpString->expressions) - visit(expr, ValueContext::RValue); - } - - void visit(AstExprError* expr) - { - // TODO! - for (AstExpr* e : expr->expressions) - visit(e, ValueContext::RValue); - } - - /** 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); - - 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}); - - TypePack* resultPack = emplaceTypePack(asMutable(pack)); - resultPack->head.assign(1, result); - resultPack->tail = freeTail; - - 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!"); - } - - void visitGenerics(AstArray generics, AstArray genericPacks) - { - DenseHashSet seen{AstName{}}; - - 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); - } - - for (const auto& g : genericPacks) - { - 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) - { - 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); - } - - 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; - - for (const AstTypeOrPack& param : ty->parameters) - { - if (param.type) - visit(param.type); - else - visit(param.typePack); - } - - 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); - - 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 hasDefaultPacks = std::any_of( - alias->typePackParams.begin(), - alias->typePackParams.end(), - [](auto&& el) - { - return el.defaultValue.has_value(); - } - ); - - if (!ty->hasParameterList) - { - if ((!alias->typeParams.empty() && !hasDefaultTypes) || (!alias->typePackParams.empty() && !hasDefaultPacks)) - { - reportError(GenericError{"Type parameter list is required"}, ty->location); - } - } - - size_t typesProvided = 0; - size_t extraTypes = 0; - size_t packsProvided = 0; - - for (const AstTypeOrPack& p : ty->parameters) - { - if (p.type) - { - if (packsProvided != 0) - { - reportError(GenericError{"Type parameters must come before type pack parameters"}, ty->location); - continue; - } - - if (typesProvided < typesRequired) - { - typesProvided += 1; - } - else - { - extraTypes += 1; - } - } - else if (p.typePack) - { - 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; - } - } - } - - 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; - } - - for (size_t i = typesProvided; i < typesRequired; ++i) - { - if (alias->typeParams[i].defaultValue) - { - typesProvided += 1; - } - } - - for (size_t i = packsProvided; i < packsRequired; ++i) - { - if (alias->typePackParams[i].defaultValue) - { - packsProvided += 1; - } - } - - if (extraTypes == 0 && packsProvided + 1 == packsRequired) - { - packsProvided += 1; - } - - if (typesProvided != typesRequired || packsProvided != packsRequired) - { - reportError( - IncorrectGenericParameterCount{ - /* name */ ty->name.value, - /* typeFun */ *alias, - /* actualParameters */ typesProvided, - /* actualPackParameters */ packsProvided, - }, - ty->location - ); - } - } - else - { - 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; - - reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location); - } - } - } - - void visit(AstTypeTable* table) - { - // TODO! - - for (const AstTableProp& prop : table->props) - visit(prop.type); - - if (table->indexer) - { - visit(table->indexer->indexType); - visit(table->indexer->resultType); - } - } - - void visit(AstTypeFunction* ty) - { - visitGenerics(ty->generics, ty->genericPacks); - visit(ty->argTypes); - visit(ty->returnTypes); - } - - void visit(AstTypeTypeof* ty) - { - visit(ty->expr, ValueContext::RValue); - } - - void visit(AstTypeUnion* ty) - { - // TODO! - for (AstType* type : ty->types) - visit(type); - } - - void visit(AstTypeIntersection* ty) - { - // TODO! - for (AstType* type : ty->types) - visit(type); - } - - void 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); - } - - void visit(AstTypePackExplicit* tp) - { - // TODO! - for (AstType* type : tp->typeList.types) - visit(type); - - if (tp->typeList.tailType) - visit(tp->typeList.tailType); - } - - void visit(AstTypePackVariadic* tp) - { - // TODO! - visit(tp->variadicType); - } - - void visit(AstTypePackGeneric* tp) - { - Scope* scope = findInnermostScope(tp->location); - LUAU_ASSERT(scope); - - 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); - } - } - } - - 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; - } - }; - - template - Reasonings explainReasonings(TID subTy, TID superTy, Location location, const SubtypingResult& r) - { - if (r.reasoning.empty()) - return {}; - - std::vector reasons; - bool suppressed = true; - for (const SubtypingReasoning& reasoning : r.reasoning) - { - if (reasoning.subPath.empty() && reasoning.superPath.empty()) - continue; - - std::optional optSubLeaf = traverse(subTy, reasoning.subPath, builtinTypes); - std::optional optSuperLeaf = traverse(superTy, reasoning.superPath, builtinTypes); - - if (!optSubLeaf || !optSuperLeaf) - ice->ice("Subtyping test returned a reasoning with an invalid path", location); - - const TypeOrPack& subLeaf = *optSubLeaf; - const TypeOrPack& superLeaf = *optSuperLeaf; - - auto subLeafTy = get(subLeaf); - auto superLeafTy = get(superLeaf); - - auto subLeafTp = get(subLeaf); - auto superLeafTp = get(superLeaf); - - 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); - - std::string relation = "a subtype of"; - if (reasoning.variance == SubtypingVariance::Invariant) - relation = "exactly"; - else if (reasoning.variance == SubtypingVariance::Contravariant) - relation = "a supertype of"; - - 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); - - // 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); - } - } - - return {std::move(reasons), suppressed}; - } - - - void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result) - { - switch (shouldSuppressErrors(NotNull{&normalizer}, subTy).orElse(shouldSuppressErrors(NotNull{&normalizer}, superTy))) - { - case ErrorSuppression::Suppress: - return; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, location); - case ErrorSuppression::DoNotSuppress: - break; - } - - Reasonings reasonings = explainReasonings(subTy, superTy, location, result); - - if (!reasonings.suppressed) - reportError(TypeMismatch{superTy, subTy, reasonings.toString()}, location); - } - - void explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result) - { - switch (shouldSuppressErrors(NotNull{&normalizer}, subTy).orElse(shouldSuppressErrors(NotNull{&normalizer}, superTy))) - { - case ErrorSuppression::Suppress: - return; - case ErrorSuppression::NormalizationFailed: - reportError(NormalizationTooComplex{}, location); - case ErrorSuppression::DoNotSuppress: - break; - } - - Reasonings reasonings = explainReasonings(subTy, superTy, location, result); - - if (!reasonings.suppressed) - reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location); - } - - bool testIsSubtype(TypeId subTy, TypeId superTy, Location location) - { - NotNull scope{findInnermostScope(location)}; - SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope); - - if (r.normalizationTooComplex) - reportError(NormalizationTooComplex{}, location); - - if (!r.isSubtype) - explainError(subTy, superTy, location, r); - - return r.isSubtype; - } - - bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location) - { - NotNull scope{findInnermostScope(location)}; - SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope); - - if (r.normalizationTooComplex) - reportError(NormalizationTooComplex{}, location); - - if (!r.isSubtype) - explainError(subTy, superTy, location, r); - - return r.isSubtype; - } - - void reportError(TypeErrorData data, const Location& location) - { - if (auto utk = get_if(&data)) - diagnoseMissingTableKey(utk, data); - - module->errors.emplace_back(location, module->name, std::move(data)); - - if (logger) - logger->captureTypeCheckError(module->errors.back()); - } - - void reportError(TypeError e) - { - reportError(std::move(e.data), e.location); - } - - void reportErrors(ErrorVec errors) - { - for (TypeError e : errors) - reportError(std::move(e)); - } - - 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(); - } - }; - - /* 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 - ) - { - std::vector typesOfProp; - std::vector typesMissingTheProp; - - // this is `false` if we ever hit the resource limits during any of our uses of `fetch`. - bool normValid = true; - - auto fetch = [&](TypeId ty) - { - 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; - } - - if (res.present == NormalizationResult::True && res.result) - typesOfProp.emplace_back(*res.result); - - if (res.present == NormalizationResult::False) - typesMissingTheProp.push_back(ty); - }; - - if (normValid) - fetch(norm->tops); - if (normValid) - fetch(norm->booleans); - - if (normValid) - { - for (const auto& [ty, _negations] : norm->classes.classes) - { - fetch(ty); - - 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) - { - for (TypeId ty : norm->tables) - { - fetch(ty); - - if (!normValid) - break; - } - } - - 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 - { - 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) - { - for (const auto& [tyvar, intersect] : norm->tyvars) - { - if (get(intersect->tops)) - { - TypeId ty = normalizer.typeFromNormal(*intersect); - fetch(module->internalTypes.addType(IntersectionType{{tyvar, ty}})); - } - else - fetch(follow(tyvar)); - - if (!normValid) - break; - } - } - - return {typesOfProp, typesMissingTheProp}; - } - - // 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) - { - std::shared_ptr norm = normalizer.normalize(tableTy); - if (!norm) - { - reportError(NormalizationTooComplex{}, location); - 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); - - 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)) - { - 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 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); - - if (get(ty) || get(ty) || get(ty)) - return {NormalizationResult::True, {ty}}; - - if (isString(ty)) - { - std::optional mtIndex = Luau::findMetatableEntry(builtinTypes, errors, builtinTypes->stringType, "__index", location); - LUAU_ASSERT(mtIndex); - ty = *mtIndex; - } - - 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 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, {}}; - } - 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::True, propTy}; - } - else if (const IntersectionType* itv = get(ty)) - { - for (TypeId part : itv) - { - PropertyType result = hasIndexTypeFromType(part, prop, context, location, seen, astIndexExprType, errors); - if (result.present != NormalizationResult::False) - return result; - } - - 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, {}}; - } - - void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const - { - std::string_view sv(utk->key); - std::set candidates; - - auto accumulate = [&](const TableType::Props& props) - { - for (const auto& [name, ty] : props) - { - if (sv != name && equalsLower(sv, name)) - candidates.insert(name); - } - }; - - if (auto ttv = getTableType(utk->table)) - accumulate(ttv->props); - else if (auto ctv = get(follow(utk->table))) - { - while (ctv) - { - accumulate(ctv->props); - - if (!ctv->parent) - break; - - ctv = get(*ctv->parent); - LUAU_ASSERT(ctv); - } - } - - if (!candidates.empty()) - data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, candidates}); - } - - 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) - { - return isErrorSuppressing(loc1, ty1) || isErrorSuppressing(loc2, ty2); - } - - bool 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; - }; - - LUAU_ASSERT(false); - return false; // UNREACHABLE - } - - bool isErrorSuppressing(Location loc1, TypePackId tp1, Location loc2, TypePackId tp2) - { - return isErrorSuppressing(loc1, tp1) || isErrorSuppressing(loc2, tp2); - } -}; - void check( NotNull builtinTypes, NotNull unifierState, @@ -3201,4 +290,2879 @@ void check( freeze(module->interfaceTypes); } +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) +{ +} + +bool TypeChecker2::allowsNoReturnValues(const TypePackId tp) +{ + for (TypeId ty : tp) + { + if (!get(follow(ty))) + return false; + } + + 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); + } + + 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") + { + // assert() will error because it is missing the first argument + if (call->args.size == 0) + return true; + + if (AstExprConstantBool* expr = call->args.data[0]->as()) + if (!expr->value) + return true; + } + + return false; +} + +bool TypeChecker2::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 (AstStatIf* stat = node->as()) + { + if (hasBreak(stat->thenbody)) + return true; + + if (stat->elsebody && hasBreak(stat->elsebody)) + return true; + + return false; + } + + return false; +} + +const AstStat* TypeChecker2::getFallthrough(const AstStat* node) +{ + 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 (getFallthrough(stat->body.data[i]) == nullptr) + return nullptr; + } + + 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 (stat->elsebody) + { + if (const AstStat* elsef = getFallthrough(stat->elsebody)) + return elsef; + + return nullptr; + } + else + return stat; + } + + if (node->is()) + return nullptr; + + if (const AstStatExpr* stat = node->as()) + { + if (AstExprCall* call = stat->expr->as(); call && isErrorCall(call)) + 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; + } + + if (const AstStatRepeat* stat = node->as()) + { + if (AstExprConstantBool* expr = stat->condition->as()) + { + if (!expr->value && !hasBreak(stat->body)) + return nullptr; + } + + if (getFallthrough(stat->body) == nullptr) + return nullptr; + + return node; + } + + return node; +} + +std::optional TypeChecker2::pushStack(AstNode* node) +{ + if (Scope** scope = module->astScopes.find(node)) + return StackPusher{stack, *scope}; + else + return std::nullopt; +} + +void TypeChecker2::checkForInternalTypeFunction(TypeId ty, Location location) +{ + InternalTypeFunctionFinder finder(functionDeclStack); + finder.traverse(ty); + + for (TypeId internal : finder.internalFunctions) + reportError(WhereClauseNeeded{internal}, location); + + for (TypePackId internal : finder.internalPackFunctions) + reportError(PackWhereClauseNeeded{internal}, location); +} + +TypeId TypeChecker2::checkForTypeFunctionInhabitance(TypeId instance, Location location) +{ + if (seenTypeFunctionInstances.find(instance)) + return instance; + seenTypeFunctionInstances.insert(instance); + + 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 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; +} + +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 TypeChecker2::lookupAnnotation(AstType* annotation) +{ + if (FFlag::DebugLuauMagicTypes) + { + if (auto ref = annotation->as(); ref && ref->name == "_luau_print" && ref->parameters.size > 0) + { + 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* ty = module->astResolvedTypes.find(annotation); + LUAU_ASSERT(ty); + return checkForTypeFunctionInhabitance(follow(*ty), annotation->location); +} + +std::optional TypeChecker2::lookupPackAnnotation(AstTypePack* annotation) +{ + TypePackId* tp = module->astResolvedTypePacks.find(annotation); + if (tp != nullptr) + return {follow(*tp)}; + return {}; +} + +TypeId TypeChecker2::lookupExpectedType(AstExpr* expr) +{ + if (TypeId* ty = module->astExpectedTypes.find(expr)) + return follow(*ty); + + return builtinTypes->anyType; +} + +TypePackId TypeChecker2::lookupExpectedPack(AstExpr* expr, TypeArena& arena) +{ + if (TypeId* ty = module->astExpectedTypes.find(expr)) + return arena.addTypePack(TypePack{{follow(*ty)}, std::nullopt}); + + return builtinTypes->anyTypePack; +} + +TypePackId TypeChecker2::reconstructPack(AstArray exprs, TypeArena& arena) +{ + if (exprs.size == 0) + return arena.addTypePack(TypePack{{}, std::nullopt}); + + std::vector head; + + for (size_t i = 0; i < exprs.size - 1; ++i) + { + head.push_back(lookupType(exprs.data[i])); + } + + 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 + { + didNarrow = false; + for (auto scope : bestScope->children) + { + if (scope->location.encloses(location)) + { + bestScope = scope.get(); + didNarrow = true; + break; + } + } + } while (didNarrow && bestScope->children.size() > 0); + + return bestScope; +} + +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 TypeChecker2::visit(AstStatBlock* block) +{ + auto StackPusher = pushStack(block); + + for (AstStat* statement : block->body) + visit(statement); +} + +void TypeChecker2::visit(AstStatIf* ifStatement) +{ + { + InConditionalContext flipper{&typeContext}; + visit(ifStatement->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 TypeChecker2::visit(AstStatRepeat* repeatStatement) +{ + visit(repeatStatement->body); + visit(repeatStatement->condition, ValueContext::RValue); +} + +void TypeChecker2::visit(AstStatBreak*) {} + +void TypeChecker2::visit(AstStatContinue*) {} + +void TypeChecker2::visit(AstStatReturn* ret) +{ + Scope* scope = findInnermostScope(ret->location); + TypePackId expectedRetType = scope->returnType; + + TypeArena* arena = &module->internalTypes; + TypePackId actualRetType = reconstructPack(ret->list, *arena); + + testIsSubtype(actualRetType, expectedRetType, ret->location); + + for (AstExpr* expr : ret->list) + visit(expr, ValueContext::RValue); +} + +void TypeChecker2::visit(AstStatExpr* expr) +{ + visit(expr->expr, ValueContext::RValue); +} + +void TypeChecker2::visit(AstStatLocal* local) +{ + 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) + { + AstLocal* var = i < local->vars.size ? local->vars.data[i] : nullptr; + + 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); + + 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); + } + } + + 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 + ); + } + } + } +} + +void TypeChecker2::visit(AstStatFor* forStatement) +{ + if (forStatement->var->annotation) + { + visit(forStatement->var->annotation); + + TypeId annotatedType = lookupAnnotation(forStatement->var->annotation); + testIsSubtype(builtinTypes->numberType, annotatedType, forStatement->var->location); + } + + auto checkNumber = [this](AstExpr* expr) + { + if (!expr) + return; + + visit(expr, ValueContext::RValue); + testIsSubtype(lookupType(expr), builtinTypes->numberType, expr->location); + }; + + checkNumber(forStatement->from); + checkNumber(forStatement->to); + checkNumber(forStatement->step); + + visit(forStatement->body); +} + +void TypeChecker2::visit(AstStatForIn* forInStatement) +{ + for (AstLocal* local : forInStatement->vars) + { + if (local->annotation) + visit(local->annotation); + } + + for (AstExpr* expr : forInStatement->values) + visit(expr, ValueContext::RValue); + + visit(forInStatement->body); + + // 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; + + 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]; + + // 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)); + } + + // 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])); + } + + // 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 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]); + + auto checkFunction = [this, &arena, &forInStatement, &variableTypes](const FunctionType* iterFtv, std::vector iterTys, bool isMm) + { + 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. + + // It may be invoked with 0 or 1 argument on the first iteration. + // This depends on the types in iterateePack and therefore + // iteratorTypes. + + // 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); + + 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); + } + + 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 (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); + } + + 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 + 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 (const FunctionType* iterMmFtv = get(*instantiatedIterMmTy)) + { + TypePackId argPack = arena.addTypePack({iteratorTy}); + testIsSubtype(argPack, iterMmFtv->argTypes, forInStatement->values.data[0]->location); + + 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; + } + + TypeId nextFn = follow(mmIteratorTypes.head[0]); + + 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); + } + } + else + { + reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location); + } + } + else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *iterMmTy)) + { + // 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 + { + reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location); + } + } + else if (iteratorNorm && iteratorNorm->hasTables()) + { + // Ok. All tables can be iterated. + } + else if (!iteratorNorm || !iteratorNorm->shouldSuppressErrors()) + { + reportError(CannotCallNonFunction{iteratorTy}, forInStatement->values.data[0]->location); + } +} + +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; +} + +void TypeChecker2::reportErrorsFromAssigningToNever(AstExpr* lhs, TypeId rhsType) +{ + + if (auto indexName = lhs->as()) + { + TypeId indexedType = lookupType(indexName->expr); + + // if it's already never, I don't think we have anything to do here. + if (get(indexedType)) + 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; + + 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); + } +} + +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); + + AstExpr* rhs = assign->values.data[i]; + visit(rhs, ValueContext::RValue); + TypeId rhsType = lookupType(rhs); + + if (get(lhsType)) + { + reportErrorsFromAssigningToNever(lhs, rhsType); + continue; + } + + bool ok = testIsSubtype(rhsType, lhsType, rhs->location); + + // If rhsType bindingType = getBindingType(lhs); + if (bindingType) + testIsSubtype(rhsType, *bindingType, rhs->location); + } + } +} + +void TypeChecker2::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); + + testIsSubtype(*resultTy, varTy, stat->location); +} + +void TypeChecker2::visit(AstStatFunction* stat) +{ + visit(stat->name, ValueContext::LValue); + visit(stat->func); +} + +void TypeChecker2::visit(AstStatLocalFunction* stat) +{ + visit(stat->func); +} + +void TypeChecker2::visit(const AstTypeList* typeList) +{ + for (AstType* ty : typeList->types) + visit(ty); + + if (typeList->tailType) + visit(typeList->tailType); +} + +void TypeChecker2::visit(AstStatTypeAlias* stat) +{ + visitGenerics(stat->generics, stat->genericPacks); + visit(stat->type); +} + +void TypeChecker2::visit(AstStatTypeFunction* stat) +{ + // TODO: add type checking for user-defined type functions + + reportError(TypeError{stat->location, GenericError{"This syntax is not supported"}}); +} + +void TypeChecker2::visit(AstTypeList types) +{ + for (AstType* type : types.types) + visit(type); + if (types.tailType) + visit(types.tailType); +} + +void TypeChecker2::visit(AstStatDeclareFunction* stat) +{ + visitGenerics(stat->generics, stat->genericPacks); + visit(stat->params); + visit(stat->retTypes); +} + +void TypeChecker2::visit(AstStatDeclareGlobal* stat) +{ + visit(stat->type); +} + +void TypeChecker2::visit(AstStatDeclareClass* stat) +{ + for (const AstDeclaredClassProp& prop : stat->props) + visit(prop.ty); +} + +void TypeChecker2::visit(AstStatError* stat) +{ + for (AstExpr* expr : stat->expressions) + visit(expr, ValueContext::RValue); + + for (AstStat* s : stat->statements) + visit(s); +} + +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 TypeChecker2::visit(AstExprGroup* expr, ValueContext context) +{ + visit(expr->expr, context); +} + +void TypeChecker2::visit(AstExprConstantNil* expr) +{ +#if defined(LUAU_ENABLE_ASSERT) + TypeId actualType = lookupType(expr); + TypeId expectedType = builtinTypes->nilType; + NotNull scope{findInnermostScope(expr->location)}; + + SubtypingResult r = subtyping->isSubtype(actualType, expectedType, scope); + LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, actualType)); +#endif +} + +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); + NotNull scope{findInnermostScope(expr->location)}; + + const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope); + if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType)) + reportError(TypeMismatch{inferredType, bestType}, expr->location); +} + +void TypeChecker2::visit(AstExprConstantNumber* expr) +{ +#if defined(LUAU_ENABLE_ASSERT) + const TypeId bestType = builtinTypes->numberType; + const TypeId inferredType = lookupType(expr); + NotNull scope{findInnermostScope(expr->location)}; + + const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope); + LUAU_ASSERT(r.isSubtype || isErrorSuppressing(expr->location, inferredType)); +#endif +} + +void TypeChecker2::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); + NotNull scope{findInnermostScope(expr->location)}; + + const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope); + if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType)) + reportError(TypeMismatch{inferredType, bestType}, expr->location); +} + +void TypeChecker2::visit(AstExprLocal* 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); +} + +void TypeChecker2::visit(AstExprVarargs* expr) +{ + // TODO! +} + +void TypeChecker2::visitCall(AstExprCall* call) +{ + TypePack args; + std::vector argExprs; + NotNull scope{findInnermostScope(call->location)}; + argExprs.reserve(call->args.size + 1); + + 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) + { + SubtypingResult result = subtyping->isSubtype(*originalCallTy, *selectedOverloadTy, scope); + if (result.isSubtype) + fnTy = follow(*selectedOverloadTy); + + if (result.normalizationTooComplex) + { + 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); + } + + 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 (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); + } + + 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; + } + } + + + 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 + { + 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 + { + 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 (resolver.nonviableOverloads.size() <= 1 && resolver.arityMismatches.size() <= 1) + return; + + std::string s = "Available overloads: "; + + std::vector overloads; + if (resolver.nonviableOverloads.empty()) + { + for (const auto& [ty, p] : resolver.resolution) + { + if (p.first == OverloadResolver::TypeIsNotAFunction) + continue; + + overloads.push_back(ty); + } + } + else + { + for (const auto& [ty, _] : resolver.nonviableOverloads) + overloads.push_back(ty); + } + + if (overloads.size() <= 1) + return; + + for (size_t i = 0; i < overloads.size(); ++i) + { + if (i > 0) + s += (i == overloads.size() - 1) ? "; and " : "; "; + + s += toString(overloads[i]); + } + + reportError(ExtraInformation{std::move(s)}, call->func->location); +} + +void TypeChecker2::visit(AstExprCall* call) +{ + visit(call->func, ValueContext::RValue); + + 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; + + std::vector result; + + for (TypeId option : utv) + { + if (!isNil(option)) + result.push_back(option); + } + + if (result.empty()) + return std::nullopt; + + return result.size() == 1 ? result[0] : module->internalTypes.addType(UnionType{std::move(result)}); + } + + return std::nullopt; +} + +TypeId TypeChecker2::stripFromNilAndReport(TypeId ty, const Location& location) +{ + ty = follow(ty); + + if (auto utv = get(ty)) + { + if (!std::any_of(begin(utv), end(utv), isNil)) + return ty; + } + + 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 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 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 + { + LUAU_ASSERT(tt || get(follow(metaTable->table))); + + reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location); + } +} + +void TypeChecker2::visit(AstExprIndexExpr* indexExpr, ValueContext context) +{ + if (auto str = indexExpr->index->as()) + { + TypeId astIndexExprType = lookupType(indexExpr->index); + const std::string stringValue(str->value.data, str->value.size); + visitExprName(indexExpr->expr, indexExpr->location, stringValue, context, astIndexExprType); + return; + } + + 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 (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 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); +} + +void TypeChecker2::visit(AstExprFunction* fn) +{ + auto StackPusher = pushStack(fn); + + visitGenerics(fn->generics, fn->genericPacks); + + TypeId inferredFnTy = lookupType(fn); + functionDeclStack.push_back(inferredFnTy); + + std::shared_ptr normalizedFnTy = normalizer.normalize(inferredFnTy); + if (!normalizedFnTy) + { + reportError(CodeTooComplex{}, fn->location); + } + else if (get(normalizedFnTy->errors)) + { + // 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); + + const FunctionType* inferredFtv = get(normalizedFnTy->functions.parts.front()); + LUAU_ASSERT(inferredFtv); + + // 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; + + for (const auto& arg : fn->args) + { + if (argIt == end(inferredFtv->argTypes)) + break; + + TypeId inferredArgTy = *argIt; + + if (arg->annotation) + { + // we need to typecheck any argument annotations themselves. + visit(arg->annotation); + + 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) + { + 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. + 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( + 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)); + } + + visit(fn->body); + + // 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) + { + 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 + ); + } + } + } + + functionDeclStack.pop_back(); +} + +void TypeChecker2::visit(AstExprTable* expr) +{ + // TODO! + for (const AstExprTable::Item& item : expr->items) + { + if (item.key) + visit(item.key, ValueContext::LValue); + visit(item.value, ValueContext::RValue); + } +} + +void TypeChecker2::visit(AstExprUnary* expr) +{ + visit(expr->expr, ValueContext::RValue); + + TypeId operandType = lookupType(expr->expr); + TypeId resultType = lookupType(expr); + + if (isErrorSuppressing(expr->expr->location, operandType)) + return; + + if (auto it = kUnaryOpMetamethods.find(expr->op); it != kUnaryOpMetamethods.end()) + { + std::optional mm = findMetatableEntry(builtinTypes, module->errors, operandType, it->second, expr->location); + if (mm) + { + if (const FunctionType* ftv = get(follow(*mm))) + { + if (std::optional ret = first(ftv->retTypes)) + { + if (expr->op == AstExprUnary::Op::Len) + { + testIsSubtype(follow(*ret), builtinTypes->numberType, expr->location); + } + } + else + { + 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; + } + + 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; + } + + return; + } + } + + if (expr->op == AstExprUnary::Op::Len) + { + DenseHashSet seen{nullptr}; + int recursionCount = 0; + std::shared_ptr nty = normalizer.normalize(operandType); + + if (nty && nty->shouldSuppressErrors()) + return; + + switch (normalizer.isInhabited(nty.get())) + { + case NormalizationResult::True: + break; + case NormalizationResult::False: + return; + case NormalizationResult::HitLimits: + reportError(NormalizationTooComplex{}, expr->location); + 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"); + } +} + +TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) +{ + visit(expr->left, ValueContext::RValue); + visit(expr->right, ValueContext::RValue); + + 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; + + 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) + { + auto testUnion = [&matches, builtinTypes = this->builtinTypes](const UnionType* utv, std::optional otherMt) + { + for (TypeId option : utv) + { + if (getMetatable(follow(option), builtinTypes) == otherMt) + { + 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) + { + 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(); + } + + 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); + } + + if (mm) + { + AstNode* key = expr; + if (overrideKey != nullptr) + key = overrideKey; + + 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}); + } + + 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{})}); + } + + TypeId expectedTy = module->internalTypes.addType(FunctionType(expectedArgs, expectedRets)); + + testIsSubtype(follow(*mm), expectedTy, expr->location); + + 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); + } + + 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); + } + + return builtinTypes->errorRecoveryType(); + } + } + else + { + reportError(CannotCallNonFunction{*mm}, 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 + ); + } + + 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(); + } + } + } + + 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()) + { + 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 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))) + { + case ErrorSuppression::Suppress: + return; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, expr->location); + return; + case ErrorSuppression::DoNotSuppress: + break; + } + + switch (normalizer.isInhabited(computedType)) + { + case NormalizationResult::True: + break; + case NormalizationResult::False: + return; + case NormalizationResult::HitLimits: + reportError(NormalizationTooComplex{}, expr->location); + return; + } + + switch (normalizer.isIntersectionInhabited(computedType, annotationType)) + { + case NormalizationResult::True: + return; + case NormalizationResult::False: + reportError(TypesAreUnrelated{computedType, annotationType}, expr->location); + break; + case NormalizationResult::HitLimits: + reportError(NormalizationTooComplex{}, expr->location); + break; + } +} + +void TypeChecker2::visit(AstExprIfElse* expr) +{ + // TODO! + visit(expr->condition, ValueContext::RValue); + visit(expr->trueExpr, ValueContext::RValue); + visit(expr->falseExpr, ValueContext::RValue); +} + +void TypeChecker2::visit(AstExprInterpString* interpString) +{ + for (AstExpr* expr : interpString->expressions) + visit(expr, ValueContext::RValue); +} + +void TypeChecker2::visit(AstExprError* expr) +{ + // TODO! + for (AstExpr* e : expr->expressions) + visit(e, ValueContext::RValue); +} + +TypeId TypeChecker2::flattenPack(TypePackId pack) +{ + pack = follow(pack); + + 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}); + + TypePack* resultPack = emplaceTypePack(asMutable(pack)); + resultPack->head.assign(1, result); + resultPack->tail = freeTail; + + 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!"); +} + +void TypeChecker2::visitGenerics(AstArray generics, AstArray genericPacks) +{ + DenseHashSet seen{AstName{}}; + + 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); + } + + for (const auto& g : genericPacks) + { + if (seen.contains(g.name)) + reportError(DuplicateGenericParameter{g.name.value}, g.location); + else + seen.insert(g.name); + + if (g.defaultValue) + visit(g.defaultValue); + } +} + +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); +} + +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); + + 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(); + + 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(); + } + ); + + if (!ty->hasParameterList) + { + if ((!alias->typeParams.empty() && !hasDefaultTypes) || (!alias->typePackParams.empty() && !hasDefaultPacks)) + { + reportError(GenericError{"Type parameter list is required"}, ty->location); + } + } + + size_t typesProvided = 0; + size_t extraTypes = 0; + size_t packsProvided = 0; + + for (const AstTypeOrPack& p : ty->parameters) + { + if (p.type) + { + if (packsProvided != 0) + { + reportError(GenericError{"Type parameters must come before type pack parameters"}, ty->location); + continue; + } + + if (typesProvided < typesRequired) + { + typesProvided += 1; + } + else + { + extraTypes += 1; + } + } + else if (p.typePack) + { + 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; + } + } + } + + 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; + } + + for (size_t i = typesProvided; i < typesRequired; ++i) + { + if (alias->typeParams[i].defaultValue) + { + typesProvided += 1; + } + } + + for (size_t i = packsProvided; i < packsRequired; ++i) + { + if (alias->typePackParams[i].defaultValue) + { + packsProvided += 1; + } + } + + if (extraTypes == 0 && packsProvided + 1 == packsRequired) + { + packsProvided += 1; + } + + if (typesProvided != typesRequired || packsProvided != packsRequired) + { + reportError( + IncorrectGenericParameterCount{ + /* name */ ty->name.value, + /* typeFun */ *alias, + /* actualParameters */ typesProvided, + /* actualPackParameters */ packsProvided, + }, + ty->location + ); + } + } + else + { + 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; + + reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location); + } + } +} + +void TypeChecker2::visit(AstTypeTable* table) +{ + // TODO! + + for (const AstTableProp& prop : table->props) + visit(prop.type); + + if (table->indexer) + { + visit(table->indexer->indexType); + visit(table->indexer->resultType); + } +} + +void TypeChecker2::visit(AstTypeFunction* ty) +{ + visitGenerics(ty->generics, ty->genericPacks); + visit(ty->argTypes); + visit(ty->returnTypes); +} + +void TypeChecker2::visit(AstTypeTypeof* ty) +{ + visit(ty->expr, ValueContext::RValue); +} + +void TypeChecker2::visit(AstTypeUnion* ty) +{ + // TODO! + for (AstType* type : ty->types) + visit(type); +} + +void TypeChecker2::visit(AstTypeIntersection* ty) +{ + // TODO! + for (AstType* type : ty->types) + visit(type); +} + +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); +} + +void TypeChecker2::visit(AstTypePackExplicit* tp) +{ + // TODO! + for (AstType* type : tp->typeList.types) + visit(type); + + if (tp->typeList.tailType) + visit(tp->typeList.tailType); +} + +void TypeChecker2::visit(AstTypePackVariadic* tp) +{ + // TODO! + visit(tp->variadicType); +} + +void TypeChecker2::visit(AstTypePackGeneric* tp) +{ + Scope* scope = findInnermostScope(tp->location); + LUAU_ASSERT(scope); + + 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); + } + } +} + +template +Reasonings TypeChecker2::explainReasonings_(TID subTy, TID superTy, Location location, const SubtypingResult& r) +{ + if (r.reasoning.empty()) + return {}; + + std::vector reasons; + bool suppressed = true; + for (const SubtypingReasoning& reasoning : r.reasoning) + { + if (reasoning.subPath.empty() && reasoning.superPath.empty()) + continue; + + std::optional optSubLeaf = traverse(subTy, reasoning.subPath, builtinTypes); + std::optional optSuperLeaf = traverse(superTy, reasoning.superPath, builtinTypes); + + if (!optSubLeaf || !optSuperLeaf) + ice->ice("Subtyping test returned a reasoning with an invalid path", location); + + const TypeOrPack& subLeaf = *optSubLeaf; + const TypeOrPack& superLeaf = *optSuperLeaf; + + auto subLeafTy = get(subLeaf); + auto superLeafTy = get(superLeaf); + + auto subLeafTp = get(subLeaf); + auto superLeafTp = get(superLeaf); + + 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); + + std::string relation = "a subtype of"; + if (reasoning.variance == SubtypingVariance::Invariant) + relation = "exactly"; + else if (reasoning.variance == SubtypingVariance::Contravariant) + relation = "a supertype of"; + + 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); + + // 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); + } + } + + return {std::move(reasons), suppressed}; +} + +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 TypeChecker2::explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result) +{ + switch (shouldSuppressErrors(NotNull{&normalizer}, subTy).orElse(shouldSuppressErrors(NotNull{&normalizer}, superTy))) + { + case ErrorSuppression::Suppress: + return; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, location); + case ErrorSuppression::DoNotSuppress: + break; + } + + Reasonings reasonings = explainReasonings(subTy, superTy, location, result); + + if (!reasonings.suppressed) + reportError(TypeMismatch{superTy, subTy, reasonings.toString()}, location); +} + +void TypeChecker2::explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result) +{ + switch (shouldSuppressErrors(NotNull{&normalizer}, subTy).orElse(shouldSuppressErrors(NotNull{&normalizer}, superTy))) + { + case ErrorSuppression::Suppress: + return; + case ErrorSuppression::NormalizationFailed: + reportError(NormalizationTooComplex{}, location); + case ErrorSuppression::DoNotSuppress: + break; + } + + Reasonings reasonings = explainReasonings(subTy, superTy, location, result); + + if (!reasonings.suppressed) + reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location); +} + +bool TypeChecker2::testIsSubtype(TypeId subTy, TypeId superTy, Location location) +{ + NotNull scope{findInnermostScope(location)}; + SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope); + + if (r.normalizationTooComplex) + reportError(NormalizationTooComplex{}, location); + + if (!r.isSubtype) + explainError(subTy, superTy, location, r); + + return r.isSubtype; +} + +bool TypeChecker2::testIsSubtype(TypePackId subTy, TypePackId superTy, Location location) +{ + NotNull scope{findInnermostScope(location)}; + SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope); + + if (r.normalizationTooComplex) + reportError(NormalizationTooComplex{}, location); + + if (!r.isSubtype) + explainError(subTy, superTy, location, r); + + return r.isSubtype; +} + +void TypeChecker2::reportError(TypeErrorData data, const Location& location) +{ + if (auto utk = get_if(&data)) + diagnoseMissingTableKey(utk, data); + + module->errors.emplace_back(location, module->name, std::move(data)); + + if (logger) + logger->captureTypeCheckError(module->errors.back()); +} + +void TypeChecker2::reportError(TypeError e) +{ + reportError(std::move(e.data), e.location); +} + +void TypeChecker2::reportErrors(ErrorVec errors) +{ + for (TypeError e : errors) + reportError(std::move(e)); +} + +/* 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; + + // this is `false` if we ever hit the resource limits during any of our uses of `fetch`. + bool normValid = true; + + auto fetch = [&](TypeId ty) + { + 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; + } + + if (res.present == NormalizationResult::True && res.result) + typesOfProp.emplace_back(*res.result); + + if (res.present == NormalizationResult::False) + typesMissingTheProp.push_back(ty); + }; + + if (normValid) + fetch(norm->tops); + if (normValid) + fetch(norm->booleans); + + if (normValid) + { + for (const auto& [ty, _negations] : norm->classes.classes) + { + fetch(ty); + + 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) + { + for (TypeId ty : norm->tables) + { + fetch(ty); + + if (!normValid) + break; + } + } + + 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 + { + 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) + { + for (const auto& [tyvar, intersect] : norm->tyvars) + { + if (get(intersect->tops)) + { + TypeId ty = normalizer.typeFromNormal(*intersect); + fetch(module->internalTypes.addType(IntersectionType{{tyvar, ty}})); + } + else + fetch(follow(tyvar)); + + if (!normValid) + break; + } + } + + 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) + { + reportError(NormalizationTooComplex{}, location); + 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); + + 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)) + { + 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); + } +} + +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); + + if (get(ty) || get(ty) || get(ty)) + return {NormalizationResult::True, {ty}}; + + if (isString(ty)) + { + std::optional mtIndex = Luau::findMetatableEntry(builtinTypes, errors, builtinTypes->stringType, "__index", location); + LUAU_ASSERT(mtIndex); + ty = *mtIndex; + } + + 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 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, {}}; + } + 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::True, propTy}; + } + else if (const IntersectionType* itv = get(ty)) + { + for (TypeId part : itv) + { + PropertyType result = hasIndexTypeFromType(part, prop, context, location, seen, astIndexExprType, errors); + if (result.present != NormalizationResult::False) + return result; + } + + 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, {}}; +} + + + +void TypeChecker2::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data) const +{ + std::string_view sv(utk->key); + std::set candidates; + + auto accumulate = [&](const TableType::Props& props) + { + for (const auto& [name, ty] : props) + { + if (sv != name && equalsLower(sv, name)) + candidates.insert(name); + } + }; + + if (auto ttv = getTableType(utk->table)) + accumulate(ttv->props); + else if (auto ctv = get(follow(utk->table))) + { + while (ctv) + { + accumulate(ctv->props); + + if (!ctv->parent) + break; + + ctv = get(*ctv->parent); + LUAU_ASSERT(ctv); + } + } + + if (!candidates.empty()) + data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, candidates}); +} + +bool TypeChecker2::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 TypeChecker2::isErrorSuppressing(Location loc1, TypeId ty1, Location loc2, TypeId ty2) +{ + return isErrorSuppressing(loc1, ty1) || isErrorSuppressing(loc2, ty2); +} + +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; + }; + + 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/Analysis/src/TypePack.cpp b/Analysis/src/TypePack.cpp index 9f3924f0..7e11d462 100644 --- a/Analysis/src/TypePack.cpp +++ b/Analysis/src/TypePack.cpp @@ -6,7 +6,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); namespace Luau { diff --git a/Analysis/src/TypePath.cpp b/Analysis/src/TypePath.cpp index 29f5cfb5..d2113ee3 100644 --- a/Analysis/src/TypePath.cpp +++ b/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/Analysis/src/TypeUtils.cpp b/Analysis/src/TypeUtils.cpp index ac265cc6..f1c60f06 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/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,7 +301,7 @@ TypePack extendTypePack( TypePack newPack; newPack.tail = arena.freshTypePack(ftp->scope); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) result.tail = newPack.tail; size_t overridesIndex = 0; while (result.head.size() < length) @@ -313,7 +313,7 @@ TypePack extendTypePack( } else { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { FreeType ft{ftp->scope, builtinTypes->neverType, builtinTypes->unknownType}; t = arena.addType(ft); @@ -426,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/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index 3dc66d1d..fa7ff876 100644 --- a/Analysis/src/Unifier.cpp +++ b/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/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 59533bae..1683023e 100644 --- a/Ast/src/Parser.cpp +++ b/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) diff --git a/Common/include/Luau/Bytecode.h b/Common/include/Luau/Bytecode.h index 1603d205..82185e7f 100644 --- a/Common/include/Luau/Bytecode.h +++ b/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/Common/include/Luau/ExperimentalFlags.h b/Common/include/Luau/ExperimentalFlags.h index 2541a965..c534bcb4 100644 --- a/Common/include/Luau/ExperimentalFlags.h +++ b/Common/include/Luau/ExperimentalFlags.h @@ -14,6 +14,7 @@ inline bool isFlagExperimental(const char* flag) "LuauInstantiateInSubtyping", // requires some fixes to lua-apps code "LuauFixIndexerSubtypingOrdering", // requires some small fixes to lua-apps code since this fixes a false negative "StudioReportLuauAny2", // takes telemetry data for usage of any types + "LuauSolverV2", // makes sure we always have at least one entry nullptr, }; diff --git a/Compiler/src/BytecodeBuilder.cpp b/Compiler/src/BytecodeBuilder.cpp index 2e9bd88c..685d94fa 100644 --- a/Compiler/src/BytecodeBuilder.cpp +++ b/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]; + 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) ? "?" : ""; + 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]; - - 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, name, optional, l.startpc, l.endpc); - } + formatAppend(result, "R%d: %s%s [argument]\n", i - 2, name, optional); } - else + + for (size_t i = 0; i < typedUpvals.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 TypedUpval& l = typedUpvals[i]; - const char* base = getBaseTypeString(et); - const char* optional = (et & 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 [argument]\n", i - 2, base, optional); - } + formatAppend(result, "U%d: %s%s\n", int(i), name, optional); + } - for (size_t i = 0; i < typedUpvals.size(); ++i) - { - const TypedUpval& l = typedUpvals[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, "U%d: %s%s\n", int(i), base, optional); - } - - 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) ? "?" : ""; - - 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/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index 93c31252..7ed70d14 100644 --- a/Compiler/src/Compiler.cpp +++ b/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/Compiler/src/Types.cpp b/Compiler/src/Types.cpp index 0e7a3242..18dc248f 100644 --- a/Compiler/src/Types.cpp +++ b/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/fuzz/proto.cpp b/fuzz/proto.cpp index 9114f824..9c2ab35c 100644 --- a/fuzz/proto.cpp +++ b/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/tests/AnyTypeSummary.test.cpp b/tests/AnyTypeSummary.test.cpp index 24004df0..5c3b4aa3 100644 --- a/tests/AnyTypeSummary.test.cpp +++ b/tests/AnyTypeSummary.test.cpp @@ -14,7 +14,7 @@ using namespace Luau; using Pattern = AnyTypeSummary::Pattern; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(StudioReportLuauAny2) @@ -35,7 +35,7 @@ TEST_SUITE_BEGIN("AnyTypeSummaryTest"); TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -56,7 +56,7 @@ type A = (number, string) -> ...any TEST_CASE_FIXTURE(ATSFixture, "export_alias") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -77,7 +77,7 @@ export type t8 = t0 &((true | any)->('')) TEST_CASE_FIXTURE(ATSFixture, "typepacks") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -103,7 +103,7 @@ end TEST_CASE_FIXTURE(ATSFixture, "typepacks_no_ret") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -128,7 +128,7 @@ end TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any_gen_table") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -149,7 +149,7 @@ type Pair = {first: T, second: any} TEST_CASE_FIXTURE(ATSFixture, "assign_uneq") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -173,7 +173,7 @@ local x, y, z = greetings("Dibri") -- mismatch TEST_CASE_FIXTURE(ATSFixture, "var_typepack_any_gen") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -195,7 +195,7 @@ type Pair = (boolean, T) -> ...any TEST_CASE_FIXTURE(ATSFixture, "typeof_any_in_func") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -219,7 +219,7 @@ TEST_CASE_FIXTURE(ATSFixture, "typeof_any_in_func") TEST_CASE_FIXTURE(ATSFixture, "generic_types") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -249,7 +249,7 @@ foo(addNumbers) TEST_CASE_FIXTURE(ATSFixture, "no_annot") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -268,7 +268,7 @@ local character = script.Parent TEST_CASE_FIXTURE(ATSFixture, "if_any") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -302,7 +302,7 @@ end TEST_CASE_FIXTURE(ATSFixture, "variadic_any") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -327,7 +327,7 @@ TEST_CASE_FIXTURE(ATSFixture, "variadic_any") TEST_CASE_FIXTURE(ATSFixture, "type_alias_intersection") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -351,7 +351,7 @@ TEST_CASE_FIXTURE(ATSFixture, "type_alias_intersection") TEST_CASE_FIXTURE(ATSFixture, "var_func_arg") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -379,7 +379,7 @@ TEST_CASE_FIXTURE(ATSFixture, "var_func_arg") TEST_CASE_FIXTURE(ATSFixture, "var_func_apps") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -404,7 +404,7 @@ TEST_CASE_FIXTURE(ATSFixture, "var_func_apps") TEST_CASE_FIXTURE(ATSFixture, "CannotExtendTable") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -431,7 +431,7 @@ end TEST_CASE_FIXTURE(ATSFixture, "unknown_symbol") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -455,7 +455,7 @@ end TEST_CASE_FIXTURE(ATSFixture, "racing_3_short") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -496,7 +496,7 @@ initialize() TEST_CASE_FIXTURE(ATSFixture, "racing_collision_2") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -579,7 +579,7 @@ initialize() TEST_CASE_FIXTURE(ATSFixture, "racing_spawning_1") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -651,7 +651,7 @@ initialize() TEST_CASE_FIXTURE(ATSFixture, "mutually_recursive_generic") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -676,7 +676,7 @@ TEST_CASE_FIXTURE(ATSFixture, "mutually_recursive_generic") TEST_CASE_FIXTURE(ATSFixture, "explicit_pack") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -698,7 +698,7 @@ type Bar = Foo<(number, any)> TEST_CASE_FIXTURE(ATSFixture, "local_val") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -719,7 +719,7 @@ local a, b, c = 1 :: any TEST_CASE_FIXTURE(ATSFixture, "var_any_local") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -743,7 +743,7 @@ local x: number, y: any, z, h: nil = 1, nil TEST_CASE_FIXTURE(ATSFixture, "table_uses_any") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -766,7 +766,7 @@ TEST_CASE_FIXTURE(ATSFixture, "table_uses_any") TEST_CASE_FIXTURE(ATSFixture, "typeof_any") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -789,7 +789,7 @@ TEST_CASE_FIXTURE(ATSFixture, "typeof_any") TEST_CASE_FIXTURE(ATSFixture, "table_type_assigned") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -813,7 +813,7 @@ TEST_CASE_FIXTURE(ATSFixture, "table_type_assigned") TEST_CASE_FIXTURE(ATSFixture, "simple_func_wo_ret") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -835,7 +835,7 @@ TEST_CASE_FIXTURE(ATSFixture, "simple_func_wo_ret") TEST_CASE_FIXTURE(ATSFixture, "simple_func_w_ret") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -858,7 +858,7 @@ TEST_CASE_FIXTURE(ATSFixture, "simple_func_w_ret") TEST_CASE_FIXTURE(ATSFixture, "nested_local") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -882,7 +882,7 @@ TEST_CASE_FIXTURE(ATSFixture, "nested_local") TEST_CASE_FIXTURE(ATSFixture, "generic_func") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -905,7 +905,7 @@ TEST_CASE_FIXTURE(ATSFixture, "generic_func") TEST_CASE_FIXTURE(ATSFixture, "type_alias_any") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -927,7 +927,7 @@ TEST_CASE_FIXTURE(ATSFixture, "type_alias_any") TEST_CASE_FIXTURE(ATSFixture, "multi_module_any") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; @@ -960,7 +960,7 @@ TEST_CASE_FIXTURE(ATSFixture, "multi_module_any") TEST_CASE_FIXTURE(ATSFixture, "cast_on_cyclic_req") { ScopedFastFlag sff[] = { - {FFlag::DebugLuauDeferredConstraintResolution, true}, + {FFlag::LuauSolverV2, true}, {FFlag::StudioReportLuauAny2, true}, }; diff --git a/tests/AstQuery.test.cpp b/tests/AstQuery.test.cpp index ec32c5f1..6822ce6d 100644 --- a/tests/AstQuery.test.cpp +++ b/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/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index 1a0e4b42..7f020b18 100644 --- a/tests/Autocomplete.test.cpp +++ b/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,7 +1607,7 @@ return target(a.@1 TEST_CASE_FIXTURE(ACFixture, "type_correct_suggestion_in_table") { - if (FFlag::DebugLuauDeferredConstraintResolution) // CLI-116815 Autocomplete cannot suggest keys while autocompleting inside of a table + if (FFlag::LuauSolverV2) // CLI-116815 Autocomplete cannot suggest keys while autocompleting inside of a table return; check(R"( @@ -2213,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"))); @@ -2264,7 +2264,7 @@ local ec = e(f@5) TEST_CASE_FIXTURE(ACFixture, "type_correct_suggestion_for_overloads") { - if (FFlag::DebugLuauDeferredConstraintResolution) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly + if (FFlag::LuauSolverV2) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly // (overloads and singletons) return; check(R"( @@ -2614,7 +2614,7 @@ end TEST_CASE_FIXTURE(ACFixture, "suggest_table_keys") { - if (FFlag::DebugLuauDeferredConstraintResolution) // CLI-116812 AutocompleteTest.suggest_table_keys needs to populate expected types for nested + if (FFlag::LuauSolverV2) // CLI-116812 AutocompleteTest.suggest_table_keys needs to populate expected types for nested // tables without an annotation return; @@ -3101,7 +3101,7 @@ TEST_CASE_FIXTURE(ACBuiltinsFixture, "autocomplete_on_string_singletons") TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons") { - if (FFlag::DebugLuauDeferredConstraintResolution) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly + if (FFlag::LuauSolverV2) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly // (overloads and singletons) return; @@ -3210,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"( @@ -3294,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"( @@ -3578,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); @@ -3760,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); @@ -3787,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 @@ -3869,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); @@ -4262,7 +4262,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; check(R"( local function foo(a: (...A) -> number, ...: A) @@ -4294,7 +4294,7 @@ foo(@1) )"); const std::optional EXPECTED_INSERT = - FFlag::DebugLuauDeferredConstraintResolution ? "function(...: number): number end" : "function(...): number end"; + FFlag::LuauSolverV2 ? "function(...: number): number end" : "function(...): number end"; auto ac = autocomplete('1'); diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index 58f52a08..48bd45d7 100644 --- a/tests/Compiler.test.cpp +++ b/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/tests/ConstraintGeneratorFixture.cpp b/tests/ConstraintGeneratorFixture.cpp index 5c16258e..7f168465 100644 --- a/tests/ConstraintGeneratorFixture.cpp +++ b/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/tests/ConstraintSolver.test.cpp b/tests/ConstraintSolver.test.cpp index 170510bd..b83fb345 100644 --- a/tests/ConstraintSolver.test.cpp +++ b/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/tests/DataFlowGraph.test.cpp b/tests/DataFlowGraph.test.cpp index 07d64e96..4ea656ee 100644 --- a/tests/DataFlowGraph.test.cpp +++ b/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/tests/Differ.test.cpp b/tests/Differ.test.cpp index a10056a7..a2b2280b 100644 --- a/tests/Differ.test.cpp +++ b/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/tests/Error.test.cpp b/tests/Error.test.cpp index cc178a83..15c317fc 100644 --- a/tests/Error.test.cpp +++ b/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/tests/Fixture.cpp b/tests/Fixture.cpp index 83454aab..4b6f0d88 100644 --- a/tests/Fixture.cpp +++ b/tests/Fixture.cpp @@ -22,7 +22,7 @@ static const char* mainModuleName = "MainModule"; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(DebugLuauFreezeArena); LUAU_FASTFLAG(DebugLuauLogSolverToJsonFile) @@ -204,7 +204,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 +371,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/tests/Frontend.test.cpp b/tests/Frontend.test.cpp index d7fc1956..88f91708 100644 --- a/tests/Frontend.test.cpp +++ b/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/tests/Generalization.test.cpp b/tests/Generalization.test.cpp index 55af4919..1388b900 100644 --- a/tests/Generalization.test.cpp +++ b/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/tests/IrLowering.test.cpp b/tests/IrLowering.test.cpp index 721969e2..51b572f5 100644 --- a/tests/IrLowering.test.cpp +++ b/tests/IrLowering.test.cpp @@ -15,9 +15,6 @@ #include #include -LUAU_FASTFLAG(LuauCompileUserdataInfo) -LUAU_FASTFLAG(LuauCompileFastcall3) - static std::string getCodegenAssembly(const char* source, bool includeIrTypes = false, int debugLevel = 1) { Luau::CodeGen::AssemblyOptions options; @@ -942,8 +939,6 @@ bb_bytecode_0: TEST_CASE("FastcallTypeInferThroughLocal") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -994,8 +989,6 @@ bb_bytecode_1: TEST_CASE("FastcallTypeInferThroughUpvalue") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1124,8 +1117,6 @@ bb_bytecode_4: TEST_CASE("ArgumentTypeRefinement") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1433,8 +1424,6 @@ bb_2: TEST_CASE("UnaryTypeResolve") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}}; - CHECK_EQ( "\n" + getCodegenHeader(R"( local function foo(a, b: vector, c) @@ -1634,37 +1623,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) @@ -1685,8 +1649,6 @@ TEST_CASE("CustomUserdataPropertyAccess") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1724,8 +1686,6 @@ TEST_CASE("CustomUserdataPropertyAccess2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1765,8 +1725,6 @@ TEST_CASE("CustomUserdataNamecall1") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1815,8 +1773,6 @@ TEST_CASE("CustomUserdataNamecall2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1868,8 +1824,6 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1904,8 +1858,6 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1938,8 +1890,6 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow3") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1972,8 +1922,6 @@ TEST_CASE("CustomUserdataMetamethod") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}}; - CHECK_EQ( "\n" + getCodegenAssembly( R"( diff --git a/tests/Linter.test.cpp b/tests/Linter.test.cpp index 6fd70626..8647777a 100644 --- a/tests/Linter.test.cpp +++ b/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/tests/Module.test.cpp b/tests/Module.test.cpp index 03aef1de..4519ba82 100644 --- a/tests/Module.test.cpp +++ b/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); @@ -317,7 +317,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; fileResolver.source["Module/A"] = R"( --!nonstrict @@ -415,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/tests/NonStrictTypeChecker.test.cpp b/tests/NonStrictTypeChecker.test.cpp index b7198a9d..ffb44049 100644 --- a/tests/NonStrictTypeChecker.test.cpp +++ b/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/tests/NonstrictMode.test.cpp b/tests/NonstrictMode.test.cpp index a22e1e16..3acd3909 100644 --- a/tests/NonstrictMode.test.cpp +++ b/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/tests/Normalize.test.cpp b/tests/Normalize.test.cpp index 1d135d54..23b4f133 100644 --- a/tests/Normalize.test.cpp +++ b/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,7 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "error_suppression") CHECK(!isSubtype(any, unk)); } - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK(isSubtype(err, str)); } @@ -454,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); @@ -670,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") @@ -711,7 +711,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; check(R"( function apply(f: (a) -> b, x) @@ -781,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>"))); @@ -878,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"))); @@ -887,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>"))); } @@ -928,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})); @@ -936,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})); @@ -944,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})); @@ -1009,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/tests/Parser.test.cpp b/tests/Parser.test.cpp index b285a318..dfcf0ded 100644 --- a/tests/Parser.test.cpp +++ b/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/tests/RuntimeLimits.test.cpp b/tests/RuntimeLimits.test.cpp index 41418aa8..b4acf138 100644 --- a/tests/RuntimeLimits.test.cpp +++ b/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/tests/Simplify.test.cpp b/tests/Simplify.test.cpp index 08b2fc4f..92ac68de 100644 --- a/tests/Simplify.test.cpp +++ b/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/tests/Subtyping.test.cpp b/tests/Subtyping.test.cpp index f4ce7eb1..a59312ac 100644 --- a/tests/Subtyping.test.cpp +++ b/tests/Subtyping.test.cpp @@ -15,7 +15,7 @@ #include -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); using namespace Luau; @@ -67,7 +67,7 @@ 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)}; @@ -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)}})); } diff --git a/tests/Symbol.test.cpp b/tests/Symbol.test.cpp index 526dd24f..83482c03 100644 --- a/tests/Symbol.test.cpp +++ b/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/tests/ToDot.test.cpp b/tests/ToDot.test.cpp index 4f0ae809..fd72579b 100644 --- a/tests/ToDot.test.cpp +++ b/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/tests/ToString.test.cpp b/tests/ToString.test.cpp index 67e247f2..81666c45 100644 --- a/tests/ToString.test.cpp +++ b/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,7 +841,7 @@ 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"))); } @@ -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/tests/TxnLog.test.cpp b/tests/TxnLog.test.cpp index d050fb9d..b4b18353 100644 --- a/tests/TxnLog.test.cpp +++ b/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/tests/TypeFunction.test.cpp b/tests/TypeFunction.test.cpp index 7b45a69c..9d7aeb6d 100644 --- a/tests/TypeFunction.test.cpp +++ b/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"( @@ -390,7 +390,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables") 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 +408,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 +436,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 +457,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 +472,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 +493,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 +516,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 +534,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 +555,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 +570,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 +590,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 +607,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"( @@ -621,7 +621,7 @@ TEST_CASE_FIXTURE(ClassFixture, "keyof_type_function_common_subset_if_union_of_d TEST_CASE_FIXTURE(ClassFixture, "binary_type_function_works_with_default_argument") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -636,7 +636,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 +659,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 +690,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 +730,7 @@ _(setmetatable(_,{[...]=_,})) TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_concat_function_at_work") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -743,7 +743,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 +776,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 +810,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 +843,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 +873,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -896,7 +896,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works") TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_array") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -911,7 +911,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 +933,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 +949,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 +966,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 +984,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 +1003,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 +1021,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"( @@ -1035,7 +1035,7 @@ TEST_CASE_FIXTURE(ClassFixture, "index_type_function_works_on_classes") TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works_w_index_metatables") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1061,7 +1061,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 +1083,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 +1097,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 +1113,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 +1129,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 +1146,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 +1166,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/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index 5b689939..bb5a2cdd 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/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/tests/TypeInfer.annotations.test.cpp b/tests/TypeInfer.annotations.test.cpp index 02f34054..4029924a 100644 --- a/tests/TypeInfer.annotations.test.cpp +++ b/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/tests/TypeInfer.anyerror.test.cpp b/tests/TypeInfer.anyerror.test.cpp index dc1c6199..445ce072 100644 --- a/tests/TypeInfer.anyerror.test.cpp +++ b/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/tests/TypeInfer.builtins.test.cpp b/tests/TypeInfer.builtins.test.cpp index d52e6390..16813e86 100644 --- a/tests/TypeInfer.builtins.test.cpp +++ b/tests/TypeInfer.builtins.test.cpp @@ -8,7 +8,8 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); +LUAU_FASTFLAG(LuauDCRMagicFunctionTypeChecker); TEST_SUITE_BEGIN("BuiltinTests"); @@ -132,7 +133,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 +415,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 +433,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 +446,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 +456,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 +513,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 +552,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 +600,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 +704,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 +722,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 +737,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 +771,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"( @@ -793,7 +794,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail_and_strin TEST_CASE_FIXTURE(Fixture, "string_format_as_method") { // CLI-115690 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check("local _ = ('%s'):format(5)"); @@ -820,7 +821,7 @@ TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument") TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument2") { // CLI-115690 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -882,7 +883,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "debug_info_is_crazy") TEST_CASE_FIXTURE(BuiltinsFixture, "aliased_string_format") { // CLI-115690 - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -945,9 +946,10 @@ 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) + if (FFlag::LuauSolverV2) return; + ScopedFastFlag sff{FFlag::LuauDCRMagicFunctionTypeChecker, true}; CheckResult result = check(R"( ("%s%d%s"):format(1, "hello", true) string.format("%s%d%s", 1, "hello", true) @@ -986,7 +988,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 +1011,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 +1044,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 +1072,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 +1080,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 +1095,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 +1130,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 +1140,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 +1149,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/tests/TypeInfer.classes.test.cpp b/tests/TypeInfer.classes.test.cpp index 17b14082..16751559 100644 --- a/tests/TypeInfer.classes.test.cpp +++ b/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/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index cd7076d7..c76745c7 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/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) @@ -604,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 @@ -883,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 @@ -1009,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)); @@ -1018,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 @@ -1042,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 @@ -1066,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 @@ -1108,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); @@ -1148,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)); @@ -1157,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)); @@ -1177,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)); @@ -1224,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"))); @@ -1235,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"( @@ -1354,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 @@ -1385,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 @@ -1452,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 } @@ -1486,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); @@ -1495,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 @@ -1518,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 @@ -1542,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) @@ -1565,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 @@ -1589,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) @@ -1670,7 +1670,7 @@ t.f = function(x) end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ( @@ -1720,7 +1720,7 @@ TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_th { // 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}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local foo @@ -1748,7 +1748,7 @@ t.f = function(x) end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK_EQ( @@ -1789,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} = {} @@ -1834,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, ...) @@ -1862,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, ...) @@ -1903,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"( @@ -1937,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"))); @@ -1985,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"); @@ -2016,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 @@ -2031,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 @@ -2153,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"( @@ -2199,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"( @@ -2229,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 { @@ -2248,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); @@ -2312,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); @@ -2342,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 |}"); @@ -2373,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) @@ -2409,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])); @@ -2434,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])); @@ -2483,7 +2483,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( function even(n) @@ -2505,7 +2505,7 @@ 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 @@ -2565,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) @@ -2583,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) @@ -2602,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 @@ -2655,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); @@ -2727,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"))); @@ -2891,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/tests/TypeInfer.generics.test.cpp b/tests/TypeInfer.generics.test.cpp index 97696f23..155e7fd4 100644 --- a/tests/TypeInfer.generics.test.cpp +++ b/tests/TypeInfer.generics.test.cpp @@ -10,7 +10,7 @@ #include "doctest.h" LUAU_FASTFLAG(LuauInstantiateInSubtyping); -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauSolverV2); using namespace Luau; @@ -70,7 +70,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,7 +142,7 @@ TEST_CASE_FIXTURE(Fixture, "properties_can_be_polytypes") TEST_CASE_FIXTURE(Fixture, "properties_can_be_instantiated_polytypes") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local t: { m: (number)->number } = { m = function(x:number) return x+1 end } @@ -198,7 +198,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"( @@ -220,7 +220,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"( @@ -260,7 +260,7 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_errors") TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_old_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type T = { id: (a) -> a } @@ -273,7 +273,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_old_solver") TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_new_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + ScopedFastFlag sff{FFlag::LuauSolverV2, true}; CheckResult result = check(R"( type T = { read id: (a) -> a } @@ -286,7 +286,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types_new_solver") TEST_CASE_FIXTURE(Fixture, "generic_factories") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type T = { id: (a) -> a } @@ -309,7 +309,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_factories") TEST_CASE_FIXTURE(Fixture, "factories_of_generics") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type T = { id: (a) -> a } @@ -466,7 +466,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_generic_types") local b: boolean = f(true) )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); } @@ -489,7 +489,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_leak_inferred_generic_types") local y: number = id(37) end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); } @@ -648,7 +648,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); @@ -669,7 +669,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`. @@ -774,7 +774,7 @@ return exports TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names_old_solver") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( local function f(a: T, ...: U...) end @@ -793,7 +793,7 @@ TEST_CASE_FIXTURE(Fixture, "instantiated_function_argument_names_old_solver") TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_types") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type C = () -> () @@ -810,7 +810,7 @@ local d: D = c TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_generic_pack") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( type C = () -> () @@ -866,7 +866,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))" @@ -886,7 +886,7 @@ Type 'number' could not be converted into 'string' in an invariant context)"; TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification1") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -906,7 +906,7 @@ local TheDispatcher: Dispatcher = { TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification2") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -926,7 +926,7 @@ local TheDispatcher: Dispatcher = { TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification3") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -946,7 +946,7 @@ local TheDispatcher: Dispatcher = { TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_few") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function test(a: number) @@ -965,7 +965,7 @@ wrapper(test) TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_many") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function test2(a: number, b: string) @@ -1163,7 +1163,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"); @@ -1182,7 +1182,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)); @@ -1202,7 +1202,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) @@ -1259,7 +1259,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"))); @@ -1301,7 +1301,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 @@ -1421,7 +1421,7 @@ TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics3") TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_generic") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( function foo(f, x: X) @@ -1434,7 +1434,7 @@ TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_gen TEST_CASE_FIXTURE(Fixture, "do_not_always_instantiate_generic_intersection_types") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; CheckResult result = check(R"( --!strict @@ -1470,7 +1470,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "higher_rank_polymorphism_should_not_accept_instantiated_arguments") { ScopedFastFlag sffs[] = { - {FFlag::DebugLuauDeferredConstraintResolution, false}, + {FFlag::LuauSolverV2, false}, {FFlag::LuauInstantiateInSubtyping, true}, }; @@ -1537,9 +1537,9 @@ TEST_CASE_FIXTURE(Fixture, "missing_generic_type_parameter") TEST_CASE_FIXTURE(BuiltinsFixture, "generic_type_functions_work_in_subtyping") { - ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + ScopedFastFlag sff{FFlag::LuauSolverV2, false}; - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( diff --git a/tests/TypeInfer.intersectionTypes.test.cpp b/tests/TypeInfer.intersectionTypes.test.cpp index b2de0128..50e28505 100644 --- a/tests/TypeInfer.intersectionTypes.test.cpp +++ b/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,7 +550,7 @@ 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 @@ -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)) @@ -610,7 +610,7 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables") LUAU_REQUIRE_ERROR_COUNT(1, result); const std::string expected = - (FFlag::DebugLuauDeferredConstraintResolution) + (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))" : @@ -630,7 +630,7 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_top_properties") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK_EQ( @@ -678,7 +678,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_returning_intersections") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ( @@ -726,7 +726,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic") end end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(0, result); } @@ -753,7 +753,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generics") )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_NO_ERRORS(result); } @@ -778,7 +778,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic_packs") end end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ( @@ -812,7 +812,7 @@ could not be converted into 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() @@ -834,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() @@ -864,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( @@ -907,7 +907,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_arguments") end )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(2, result); const std::string expected1 = R"(Type @@ -940,7 +940,7 @@ could not be converted into 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)) @@ -968,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); } @@ -993,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); } @@ -1018,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); } @@ -1045,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 @@ -1071,10 +1071,10 @@ could not be converted into TEST_CASE_FIXTURE(BuiltinsFixture, "intersect_metatables") { // CLI-117121 - Intersection of types are not compatible with the equivalent alias - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CheckResult result = check(R"( function f(a: string?, b: string?) @@ -1164,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 }) @@ -1230,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"( @@ -1247,7 +1247,7 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_intersection_types") TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_intersection_types_2") { - if (!FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1293,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 = { @@ -1316,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/tests/TypeInfer.loops.test.cpp b/tests/TypeInfer.loops.test.cpp index 98063d0f..de79654b 100644 --- a/tests/TypeInfer.loops.test.cpp +++ b/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/tests/TypeInfer.modules.test.cpp b/tests/TypeInfer.modules.test.cpp index 6719f158..42f1229f 100644 --- a/tests/TypeInfer.modules.test.cpp +++ b/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/tests/TypeInfer.negations.test.cpp b/tests/TypeInfer.negations.test.cpp index 53f32147..a21751ec 100644 --- a/tests/TypeInfer.negations.test.cpp +++ b/tests/TypeInfer.negations.test.cpp @@ -51,7 +51,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( function f(e) diff --git a/tests/TypeInfer.oop.test.cpp b/tests/TypeInfer.oop.test.cpp index 29935a08..3ccc04c1 100644 --- a/tests/TypeInfer.oop.test.cpp +++ b/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/tests/TypeInfer.operators.test.cpp b/tests/TypeInfer.operators.test.cpp index e5514db0..d8bf9152 100644 --- a/tests/TypeInfer.operators.test.cpp +++ b/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/tests/TypeInfer.primitives.test.cpp b/tests/TypeInfer.primitives.test.cpp index 0f6d6132..c3cce9df 100644 --- a/tests/TypeInfer.primitives.test.cpp +++ b/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/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index 9107c6f4..a443c187 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/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,7 +1307,7 @@ 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); diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 214bcdfd..9c26f165 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/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}))); @@ -1501,7 +1501,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( --!nonstrict @@ -1517,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}))); @@ -1569,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 @@ -1592,7 +1592,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( local function f(x: Part | Folder | string) @@ -1641,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}))); @@ -1779,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}))); @@ -1801,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); } @@ -1814,7 +1814,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( type Id = T @@ -1849,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}))); @@ -1865,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); @@ -1908,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}))); @@ -1998,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}))); @@ -2009,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 @@ -2049,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); @@ -2075,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"( @@ -2097,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?) @@ -2118,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/tests/TypeInfer.singletons.test.cpp b/tests/TypeInfer.singletons.test.cpp index bec9d953..43b1305e 100644 --- a/tests/TypeInfer.singletons.test.cpp +++ b/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/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index 04417fe1..9d76e7bd 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -15,7 +15,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering) LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections) @@ -26,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 = {} @@ -136,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])); @@ -163,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 |}"); @@ -318,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 = {} @@ -371,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 = {} @@ -396,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 = {} @@ -480,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])); @@ -513,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 { @@ -536,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) @@ -567,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 @@ -586,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 @@ -691,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 { @@ -763,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)); @@ -802,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)); @@ -811,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 } = {} @@ -851,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 |}"); @@ -893,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 @@ -913,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( @@ -930,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} @@ -993,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 @@ -1030,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"))); @@ -1145,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 } @@ -1165,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 = {} @@ -1523,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 = {} @@ -1566,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} @@ -1610,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} @@ -1665,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 } @@ -1698,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)); @@ -1726,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)); @@ -1761,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)); @@ -1796,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 }';" @@ -1825,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); @@ -1864,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)); @@ -1902,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)); @@ -1927,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 } @@ -1949,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})); @@ -1969,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]); @@ -2013,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"))); @@ -2037,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])); @@ -2096,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 = { @@ -2218,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} @@ -2265,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); @@ -2320,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 { @@ -2347,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 { @@ -2396,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: @@ -2461,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])); } @@ -2487,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])); } @@ -2504,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 @@ -2536,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]) @@ -2554,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 @@ -2572,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 @@ -2704,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); @@ -2730,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"))); @@ -2751,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"))); @@ -2827,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); @@ -2933,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"( @@ -2973,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); @@ -3059,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()); @@ -3119,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); @@ -3224,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"( @@ -3239,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])); @@ -3261,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])); @@ -3300,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 }, { @@ -3334,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)); @@ -3386,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"))); @@ -3406,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])); @@ -3485,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"))); @@ -3505,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( @@ -3572,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])); @@ -3593,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) @@ -3620,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. @@ -3682,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 @@ -3704,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); @@ -3737,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) @@ -3775,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); @@ -3813,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]); @@ -3833,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"( @@ -3971,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)); @@ -3999,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); @@ -4152,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]); @@ -4200,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})); @@ -4208,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 = { @@ -4236,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"( @@ -4276,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) @@ -4308,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); @@ -4322,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}) @@ -4344,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}) @@ -4359,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}) @@ -4374,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} @@ -4398,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} @@ -4415,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) @@ -4474,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"( @@ -4493,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"( @@ -4512,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} @@ -4632,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) @@ -4658,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])); @@ -4678,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})); @@ -4808,7 +4808,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_branching_table") LUAU_REQUIRE_NO_ERRORS(result); // unfortunate type duplication in the union - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("number | string | string" == toString(requireType("test2"))); else CHECK("number | string" == toString(requireType("test2"))); @@ -4826,7 +4826,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_branching_table2") LUAU_REQUIRE_NO_ERRORS(result); // unfortunate type duplication in the union - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) CHECK("unknown | unknown" == toString(requireType("test2"))); else CHECK("any" == toString(requireType("test2"))); diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index 1140976f..09c6c05b 100644 --- a/tests/TypeInfer.test.cpp +++ b/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/tests/TypeInfer.tryUnify.test.cpp b/tests/TypeInfer.tryUnify.test.cpp index 7db4512b..10ddd097 100644 --- a/tests/TypeInfer.tryUnify.test.cpp +++ b/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/tests/TypeInfer.typePacks.test.cpp b/tests/TypeInfer.typePacks.test.cpp index f23dfb47..8b489c44 100644 --- a/tests/TypeInfer.typePacks.test.cpp +++ b/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/tests/TypeInfer.typestates.test.cpp b/tests/TypeInfer.typestates.test.cpp index 22a30865..0bce7546 100644 --- a/tests/TypeInfer.typestates.test.cpp +++ b/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"( @@ -461,7 +461,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typestates_preserve_error_suppression_proper // early return if the flag isn't set since this is blocking gated commits // unconditional return // CLI-117098 Type states with error suppressing properties doesn't infer the correct type for properties. - if (!FFlag::DebugLuauDeferredConstraintResolution || FFlag::DebugLuauDeferredConstraintResolution) + if (!FFlag::LuauSolverV2 || FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -477,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"( @@ -495,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 @@ -512,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/tests/TypeInfer.unionTypes.test.cpp b/tests/TypeInfer.unionTypes.test.cpp index fedf155a..6cdec4af 100644 --- a/tests/TypeInfer.unionTypes.test.cpp +++ b/tests/TypeInfer.unionTypes.test.cpp @@ -8,7 +8,7 @@ using namespace Luau; -LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAcceptIndexingTableUnionsIntersections) TEST_SUITE_BEGIN("UnionTypes"); @@ -36,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 @@ -122,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?} @@ -237,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"))); @@ -411,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); @@ -473,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 } @@ -534,7 +534,7 @@ end LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CHECK_EQ( toString(result.errors[0]), @@ -566,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)"); @@ -581,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 { @@ -647,7 +647,7 @@ TEST_CASE_FIXTURE(Fixture, "indexing_into_a_cyclic_union_doesnt_crash") 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 } @@ -723,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() @@ -743,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() @@ -764,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?) @@ -783,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)) @@ -802,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?)) @@ -829,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) -> ()' @@ -848,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)) @@ -867,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"( @@ -884,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"( @@ -916,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))) @@ -940,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} @@ -974,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/tests/TypeInfer.unknownnever.test.cpp b/tests/TypeInfer.unknownnever.test.cpp index 23375680..0c62d0b6 100644 --- a/tests/TypeInfer.unknownnever.test.cpp +++ b/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"))); @@ -199,7 +199,7 @@ TEST_CASE_FIXTURE(Fixture, "assign_to_local_which_is_never") t = 3 )"); - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); } @@ -266,7 +266,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( type Disjoint = {foo: never, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"} @@ -286,7 +286,7 @@ 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::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) return; CheckResult result = check(R"( type Disjoint = {foo: string, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"} @@ -335,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"))); @@ -349,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])); @@ -379,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 @@ -403,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/tests/TypePath.test.cpp b/tests/TypePath.test.cpp index 54ec14b8..2481f27a 100644 --- a/tests/TypePath.test.cpp +++ b/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/tests/TypeVar.test.cpp b/tests/TypeVar.test.cpp index 17c0c34b..9e21b1e0 100644 --- a/tests/TypeVar.test.cpp +++ b/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/tests/Unifier2.test.cpp b/tests/Unifier2.test.cpp index 8efb2870..65734103 100644 --- a/tests/Unifier2.test.cpp +++ b/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/tests/VisitType.test.cpp b/tests/VisitType.test.cpp index c5e66829..186afaa5 100644 --- a/tests/VisitType.test.cpp +++ b/tests/VisitType.test.cpp @@ -9,13 +9,13 @@ 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") { - if (FFlag::DebugLuauDeferredConstraintResolution) + if (FFlag::LuauSolverV2) { CheckResult result = check(R"( local t : {a: {b: {c: {d: {e: boolean}}}}} @@ -61,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/tools/lldb_formatters.py b/tools/lldb_formatters.py index 9be6cade..7e957c75 100644 --- a/tools/lldb_formatters.py +++ b/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/tools/test_dcr.py b/tools/test_dcr.py index 30940c63..3de92b37 100644 --- a/tools/test_dcr.py +++ b/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)]