From 713ee2ff8b378441cde556dcea6f85054ffcf995 Mon Sep 17 00:00:00 2001 From: Andy Friesen Date: Fri, 13 Jun 2025 09:36:35 -0700 Subject: [PATCH] Sync to upstream/release/678 (#1878) # What's Changed? We've been hard at work fixing bugs in the new type solver and getting it ready to go! ## Native Codegen * Specialized Luau Codegen instruction for fetching an import. As a reminder, an import is an expression like `global.thing` and covers stuff like libraries without fastcalls `coroutine.resume`) and atomic extern libraries. ## New Type Solver * Fix an issue that prevented eager generalization from working properly with OO styled code. * Avoid copying uninitialized memory in Luau attribute parsing * Improve type inference of unsealed tables. * This fixes https://github.com/luau-lang/luau/issues/1838 * and https://github.com/luau-lang/luau/issues/1859 * Infer potential singleton string keys in autocomplete when the expected index type is a union type. * Avoid creating cyclic types when reducing types of the form `t1 where t1 = refine` * The type cloner now does the same thing for the new and old solvers. * Properly infer polarity (aka variance) for divergent table properties. (ie tables whose read type and write type are not the same) * Crash fixes. --------- Co-authored-by: Hunter Goldstein Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood Co-authored-by: Menarul Alam Co-authored-by: Aviral Goel Co-authored-by: Vighnesh Co-authored-by: Vyacheslav Egorov Co-authored-by: Ariel Weiss --- Analysis/include/Luau/ConstraintSolver.h | 3 - Analysis/include/Luau/ExpectedTypeVisitor.h | 4 + Analysis/include/Luau/Type.h | 2 - Analysis/include/Luau/TypeChecker2.h | 4 +- Analysis/include/Luau/TypeUtils.h | 4 +- Analysis/src/AstQuery.cpp | 2 +- Analysis/src/Autocomplete.cpp | 2 +- Analysis/src/AutocompleteCore.cpp | 22 +- Analysis/src/BuiltinDefinitions.cpp | 56 +- Analysis/src/Clone.cpp | 12 +- Analysis/src/Constraint.cpp | 27 +- Analysis/src/ConstraintGenerator.cpp | 154 ++++-- Analysis/src/ConstraintSolver.cpp | 119 +++-- Analysis/src/DcrLogger.cpp | 14 +- Analysis/src/Error.cpp | 16 +- Analysis/src/ExpectedTypeVisitor.cpp | 104 ++++ Analysis/src/FragmentAutocomplete.cpp | 10 +- Analysis/src/Frontend.cpp | 36 +- Analysis/src/Generalization.cpp | 18 +- Analysis/src/InferPolarity.cpp | 60 ++- Analysis/src/Linter.cpp | 11 +- Analysis/src/Module.cpp | 10 +- Analysis/src/NonStrictTypeChecker.cpp | 8 +- Analysis/src/Normalize.cpp | 6 +- Analysis/src/OverloadResolution.cpp | 8 +- Analysis/src/Substitution.cpp | 5 +- Analysis/src/Subtyping.cpp | 26 +- Analysis/src/TableLiteralInference.cpp | 21 +- Analysis/src/ToString.cpp | 4 +- Analysis/src/Transpiler.cpp | 2 +- Analysis/src/Type.cpp | 22 +- Analysis/src/TypeChecker2.cpp | 66 ++- Analysis/src/TypeFunction.cpp | 205 +++++++- Analysis/src/TypeFunctionReductionGuesser.cpp | 4 +- Analysis/src/TypeFunctionRuntime.cpp | 10 +- Analysis/src/TypeFunctionRuntimeBuilder.cpp | 12 +- Analysis/src/TypeInfer.cpp | 76 +-- Analysis/src/TypeUtils.cpp | 8 +- Analysis/src/Unifier.cpp | 48 +- Analysis/src/Unifier2.cpp | 10 +- Ast/include/Luau/Parser.h | 3 +- Ast/src/Parser.cpp | 64 ++- CLI/src/Analyze.cpp | 2 +- CLI/src/Ast.cpp | 2 +- CLI/src/Reduce.cpp | 6 +- CodeGen/include/Luau/IrBuilder.h | 1 + CodeGen/include/Luau/IrData.h | 17 + CodeGen/include/Luau/IrDump.h | 2 +- CodeGen/include/Luau/IrVisitUseDef.h | 3 + CodeGen/src/CodeGenUtils.cpp | 8 + CodeGen/src/CodeGenUtils.h | 1 + CodeGen/src/IrBuilder.cpp | 8 + CodeGen/src/IrDump.cpp | 36 +- CodeGen/src/IrLoweringA64.cpp | 46 ++ CodeGen/src/IrLoweringA64.h | 1 + CodeGen/src/IrLoweringX64.cpp | 35 ++ CodeGen/src/IrLoweringX64.h | 1 + CodeGen/src/IrTranslation.cpp | 40 +- CodeGen/src/IrUtils.cpp | 1 + CodeGen/src/IrValueLocationTracking.cpp | 1 + CodeGen/src/NativeState.cpp | 1 + CodeGen/src/NativeState.h | 1 + CodeGen/src/OptimizeConstProp.cpp | 7 + CodeGen/src/OptimizeDeadStore.cpp | 1 + fuzz/luau.proto | 64 ++- fuzz/protoprint.cpp | 103 +++- tests/Autocomplete.test.cpp | 137 +++-- tests/BuiltinDefinitions.test.cpp | 4 +- tests/ClassFixture.cpp | 22 +- tests/ClassFixture.h | 2 + tests/ConstraintGeneratorFixture.cpp | 12 +- tests/ConstraintGeneratorFixture.h | 2 +- tests/EqSatSimplification.test.cpp | 238 ++++----- tests/Error.test.cpp | 6 +- tests/Fixture.cpp | 178 ++++--- tests/Fixture.h | 16 +- tests/FragmentAutocomplete.test.cpp | 50 +- tests/Frontend.test.cpp | 355 +++++++------ tests/Generalization.test.cpp | 14 +- tests/InferPolarity.test.cpp | 43 +- tests/Instantiation2.test.cpp | 6 +- tests/IrLowering.test.cpp | 91 ++-- tests/Linter.test.cpp | 56 +- tests/Module.test.cpp | 100 ++-- tests/NonStrictTypeChecker.test.cpp | 18 +- tests/NonstrictMode.test.cpp | 10 +- tests/Normalize.test.cpp | 88 ++-- tests/Simplify.test.cpp | 56 +- tests/Subtyping.test.cpp | 490 +++++++++--------- tests/ToDot.test.cpp | 44 +- tests/ToString.test.cpp | 47 +- tests/TypeFunction.test.cpp | 60 +-- tests/TypeFunction.user.test.cpp | 95 ++-- tests/TypeInfer.aliases.test.cpp | 36 +- tests/TypeInfer.annotations.test.cpp | 32 +- tests/TypeInfer.anyerror.test.cpp | 4 +- tests/TypeInfer.builtins.test.cpp | 54 +- tests/TypeInfer.classes.test.cpp | 12 +- tests/TypeInfer.definitions.test.cpp | 94 ++-- tests/TypeInfer.functions.test.cpp | 60 +-- tests/TypeInfer.generics.test.cpp | 24 +- tests/TypeInfer.intersectionTypes.test.cpp | 4 +- tests/TypeInfer.loops.test.cpp | 30 +- tests/TypeInfer.modules.test.cpp | 68 +-- tests/TypeInfer.negations.test.cpp | 2 +- tests/TypeInfer.oop.test.cpp | 89 +++- tests/TypeInfer.operators.test.cpp | 50 +- tests/TypeInfer.primitives.test.cpp | 6 +- tests/TypeInfer.provisional.test.cpp | 56 +- tests/TypeInfer.refinements.test.cpp | 52 +- tests/TypeInfer.singletons.test.cpp | 8 +- tests/TypeInfer.tables.test.cpp | 186 ++++--- tests/TypeInfer.test.cpp | 59 ++- tests/TypeInfer.tryUnify.test.cpp | 56 +- tests/TypeInfer.typePacks.test.cpp | 44 +- tests/TypeInfer.typestates.test.cpp | 8 +- tests/TypeInfer.unionTypes.test.cpp | 18 +- tests/TypePath.test.cpp | 123 ++--- tests/TypeVar.test.cpp | 82 +-- tests/VisitType.test.cpp | 6 +- 120 files changed, 3114 insertions(+), 2075 deletions(-) diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h index f3675638..7765aa1e 100644 --- a/Analysis/include/Luau/ConstraintSolver.h +++ b/Analysis/include/Luau/ConstraintSolver.h @@ -439,9 +439,6 @@ public: TypeId simplifyIntersection(NotNull scope, Location location, std::set parts); TypeId simplifyUnion(NotNull scope, Location location, TypeId left, TypeId right); - TypeId errorRecoveryType() const; - TypePackId errorRecoveryTypePack() const; - TypePackId anyifyModuleReturnTypePackGenerics(TypePackId tp); void throwTimeLimitError() const; diff --git a/Analysis/include/Luau/ExpectedTypeVisitor.h b/Analysis/include/Luau/ExpectedTypeVisitor.h index 294befaf..4b195359 100644 --- a/Analysis/include/Luau/ExpectedTypeVisitor.h +++ b/Analysis/include/Luau/ExpectedTypeVisitor.h @@ -52,6 +52,10 @@ struct ExpectedTypeVisitor : public AstVisitor // parameters. bool visit(AstExprCall* expr) override; + // If we have an expression like `A[B]`, then the expected type of B is + // clearly the properties and indexer of `A`. + bool visit(AstExprIndexExpr* expr) override; + // If we have an expression of type: // // return X :: Y diff --git a/Analysis/include/Luau/Type.h b/Analysis/include/Luau/Type.h index 1d532d80..f3de41a8 100644 --- a/Analysis/include/Luau/Type.h +++ b/Analysis/include/Luau/Type.h @@ -970,8 +970,6 @@ struct BuiltinTypes TypeId errorRecoveryType(TypeId guess) const; TypePackId errorRecoveryTypePack(TypePackId guess) const; - TypeId errorRecoveryType() const; - TypePackId errorRecoveryTypePack() const; friend TypeId makeStringMetatable(NotNull builtinTypes); friend struct GlobalTypes; diff --git a/Analysis/include/Luau/TypeChecker2.h b/Analysis/include/Luau/TypeChecker2.h index 4b7901ab..7e0cb9ea 100644 --- a/Analysis/include/Luau/TypeChecker2.h +++ b/Analysis/include/Luau/TypeChecker2.h @@ -194,7 +194,9 @@ private: void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result); void explainError(TypePackId subTy, TypePackId superTy, Location location, const SubtypingResult& result); - bool testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedTy); + bool testLiteralOrAstTypeIsSubtype(AstExpr* expr, TypeId expectedType); + + bool testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedType); bool testIsSubtype(TypeId subTy, TypeId superTy, Location location); bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location); diff --git a/Analysis/include/Luau/TypeUtils.h b/Analysis/include/Luau/TypeUtils.h index f4549f0d..8326d618 100644 --- a/Analysis/include/Luau/TypeUtils.h +++ b/Analysis/include/Luau/TypeUtils.h @@ -40,11 +40,11 @@ struct InConditionalContext TypeContext* typeContext; TypeContext oldValue; - explicit InConditionalContext(TypeContext* c) + explicit InConditionalContext(TypeContext* c, TypeContext newValue = TypeContext::Condition) : typeContext(c) , oldValue(*c) { - *typeContext = TypeContext::Condition; + *typeContext = newValue; } ~InConditionalContext() diff --git a/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index 342d621c..ec674d93 100644 --- a/Analysis/src/AstQuery.cpp +++ b/Analysis/src/AstQuery.cpp @@ -483,7 +483,7 @@ static std::optional checkOverloadedDocumentationSymbol( const Module& module, const TypeId ty, const AstExpr* parentExpr, - const std::optional documentationSymbol + std::optional documentationSymbol ) { if (!documentationSymbol) diff --git a/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index bdfa04bf..8e0444da 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/Analysis/src/Autocomplete.cpp @@ -46,7 +46,7 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName std::vector ancestry = findAncestryAtPositionForAutocomplete(*sourceModule, position); LUAU_ASSERT(!ancestry.empty()); ScopePtr startScope = findScopeAtPosition(*module, position); - return autocomplete_(module, builtinTypes, &typeArena, ancestry, globalScope, startScope, position, frontend.fileResolver, callback); + return autocomplete_(module, builtinTypes, &typeArena, ancestry, globalScope, startScope, position, frontend.fileResolver, std::move(callback)); } } // namespace Luau diff --git a/Analysis/src/AutocompleteCore.cpp b/Analysis/src/AutocompleteCore.cpp index 0fe8b676..78a80448 100644 --- a/Analysis/src/AutocompleteCore.cpp +++ b/Analysis/src/AutocompleteCore.cpp @@ -760,7 +760,7 @@ static bool tryAddTypeCorrectSuggestion(AutocompleteEntryMap& result, ScopePtr s if (!ty) return false; - if (auto name = tryGetTypeNameInScope(scope, *ty)) + if (auto name = tryGetTypeNameInScope(std::move(scope), *ty)) { if (auto it = result.find(*name); it != result.end()) it->second.typeCorrect = TypeCorrectKind::Correct; @@ -966,7 +966,7 @@ AutocompleteEntryMap autocompleteTypeNames( } if (inferredType) - tryAddTypeCorrectSuggestion(result, startScope, topType, inferredType, position); + tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, inferredType, position); break; } @@ -1066,7 +1066,7 @@ AutocompleteEntryMap autocompleteTypeNames( if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node)) { if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) - tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position); + tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position); } } } @@ -1079,7 +1079,7 @@ AutocompleteEntryMap autocompleteTypeNames( if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node)) { if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) - tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position); + tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position); } } } @@ -1115,7 +1115,7 @@ AutocompleteEntryMap autocompleteTypeNames( if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node)) { if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) - tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position); + tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position); } } } @@ -1485,7 +1485,7 @@ static AutocompleteResult autocompleteExpression( { AutocompleteEntryMap result; AutocompleteContext context = autocompleteExpression(module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result); - return {result, ancestry, context}; + return {std::move(result), ancestry, context}; } static std::optional getMethodContainingExternType(const ModulePtr& module, AstExpr* funcExpr) @@ -2025,7 +2025,7 @@ AutocompleteResult autocomplete_( if (!key) autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result); - return {result, ancestry, AutocompleteContext::Property}; + return {std::move(result), ancestry, AutocompleteContext::Property}; } break; @@ -2061,12 +2061,12 @@ AutocompleteResult autocomplete_( // Also offer general expression suggestions autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result); - return {result, ancestry, AutocompleteContext::Property}; + return {std::move(result), ancestry, AutocompleteContext::Property}; } else if (isIdentifier(node) && (parent->is() || parent->is())) return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement}; - if (std::optional ret = autocompleteStringParams(module, ancestry, position, fileResolver, callback)) + if (std::optional ret = autocompleteStringParams(module, ancestry, position, fileResolver, std::move(callback))) { return {*ret, ancestry, AutocompleteContext::String}; } @@ -2094,14 +2094,14 @@ AutocompleteResult autocomplete_( } } - return {result, ancestry, AutocompleteContext::String}; + return {std::move(result), ancestry, AutocompleteContext::String}; } else if (stringPartOfInterpString(node, position)) { // We're not a simple interpolated string, we're something like `a{"b"}@1`, and we // can't know what to format to AutocompleteEntryMap map; - return {map, ancestry, AutocompleteContext::String}; + return {std::move(map), ancestry, AutocompleteContext::String}; } if (node->is()) diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index adf727e4..ff9f1bac 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -30,7 +30,7 @@ */ LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3) LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements) LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2) @@ -238,7 +238,7 @@ void addGlobalBinding(GlobalTypes& globals, const std::string& name, TypeId ty, void addGlobalBinding(GlobalTypes& globals, const std::string& name, Binding binding) { - addGlobalBinding(globals, globals.globalScope, name, binding); + addGlobalBinding(globals, globals.globalScope, name, std::move(binding)); } void addGlobalBinding(GlobalTypes& globals, const ScopePtr& scope, const std::string& name, TypeId ty, const std::string& packageName) @@ -312,8 +312,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC TypeArena& arena = globals.globalTypes; NotNull builtinTypes = globals.builtinTypes; - Scope* globalScope = nullptr; // NotNull when removing FFlag::LuauEagerGeneralization2 - if (FFlag::LuauEagerGeneralization3) + Scope* globalScope = nullptr; // NotNull when removing FFlag::LuauEagerGeneralization4 + if (FFlag::LuauEagerGeneralization4) globalScope = globals.globalScope.get(); if (FFlag::LuauSolverV2) @@ -376,7 +376,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC TypeId genericMT = arena.addType(GenericType{globalScope, "MT"}); TableType tab{TableState::Generic, globals.globalScope->level}; - TypeId tabTy = arena.addType(tab); + TypeId tabTy = arena.addType(std::move(tab)); TypeId tableMetaMT = arena.addType(MetatableType{tabTy, genericMT}); @@ -509,7 +509,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC globals.globalTypeFunctionScope->builtinTypeNames = globals.globalScope->builtinTypeNames; // Type function runtime also removes a few standard libraries and globals, so we will take only the ones that are defined - static const char* typeFunctionRuntimeBindings[] = { + static constexpr const char* typeFunctionRuntimeBindings[] = { // Libraries "math", "table", @@ -598,7 +598,7 @@ std::optional> MagicFormat::handleOldSolver( WithPredicate withPredicate ) { - auto [paramPack, _predicates] = withPredicate; + auto [paramPack, _predicates] = std::move(withPredicate); TypeArena& arena = typechecker.currentModule->internalTypes; @@ -959,7 +959,7 @@ std::optional> MagicGmatch::handleOldSolver( WithPredicate withPredicate ) { - auto [paramPack, _predicates] = withPredicate; + auto [paramPack, _predicates] = std::move(withPredicate); const auto& [params, tail] = flatten(paramPack); if (params.size() != 2) @@ -983,7 +983,7 @@ std::optional> MagicGmatch::handleOldSolver( typechecker.unify(params[0], typechecker.stringType, scope, expr.args.data[0]->location); const TypePackId emptyPack = arena.addTypePack({}); - const TypePackId returnList = arena.addTypePack(returnTypes); + const TypePackId returnList = arena.addTypePack(std::move(returnTypes)); const TypeId iteratorType = arena.addType(FunctionType{emptyPack, returnList}); return WithPredicate{arena.addTypePack({iteratorType})}; } @@ -1013,7 +1013,7 @@ bool MagicGmatch::infer(const MagicFunctionCallContext& context) context.solver->unify(context.constraint, params[0], context.solver->builtinTypes->stringType); const TypePackId emptyPack = arena->addTypePack({}); - const TypePackId returnList = arena->addTypePack(returnTypes); + const TypePackId returnList = arena->addTypePack(std::move(returnTypes)); const TypeId iteratorType = arena->addType(FunctionType{emptyPack, returnList}); const TypePackId resTypePack = arena->addTypePack({iteratorType}); asMutable(context.result)->ty.emplace(resTypePack); @@ -1028,7 +1028,7 @@ std::optional> MagicMatch::handleOldSolver( WithPredicate withPredicate ) { - auto [paramPack, _predicates] = withPredicate; + auto [paramPack, _predicates] = std::move(withPredicate); const auto& [params, tail] = flatten(paramPack); if (params.size() < 2 || params.size() > 3) @@ -1057,7 +1057,7 @@ std::optional> MagicMatch::handleOldSolver( if (params.size() == 3 && expr.args.size > initIndex) typechecker.unify(params[2], optionalNumber, scope, expr.args.data[initIndex]->location); - const TypePackId returnList = arena.addTypePack(returnTypes); + const TypePackId returnList = arena.addTypePack(std::move(returnTypes)); return WithPredicate{returnList}; } @@ -1091,7 +1091,7 @@ bool MagicMatch::infer(const MagicFunctionCallContext& context) if (params.size() == 3 && context.callSite->args.size > initIndex) context.solver->unify(context.constraint, params[2], optionalNumber); - const TypePackId returnList = arena->addTypePack(returnTypes); + const TypePackId returnList = arena->addTypePack(std::move(returnTypes)); asMutable(context.result)->ty.emplace(returnList); return true; @@ -1104,7 +1104,7 @@ std::optional> MagicFind::handleOldSolver( WithPredicate withPredicate ) { - auto [paramPack, _predicates] = withPredicate; + auto [paramPack, _predicates] = std::move(withPredicate); const auto& [params, tail] = flatten(paramPack); if (params.size() < 2 || params.size() > 4) @@ -1151,7 +1151,7 @@ std::optional> MagicFind::handleOldSolver( returnTypes.insert(returnTypes.begin(), {optionalNumber, optionalNumber}); - const TypePackId returnList = arena.addTypePack(returnTypes); + const TypePackId returnList = arena.addTypePack(std::move(returnTypes)); return WithPredicate{returnList}; } @@ -1204,7 +1204,7 @@ bool MagicFind::infer(const MagicFunctionCallContext& context) returnTypes.insert(returnTypes.begin(), {optionalNumber, optionalNumber}); - const TypePackId returnList = arena->addTypePack(returnTypes); + const TypePackId returnList = arena->addTypePack(std::move(returnTypes)); asMutable(context.result)->ty.emplace(returnList); return true; } @@ -1233,7 +1233,7 @@ TypeId makeStringMetatable(NotNull builtinTypes) FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack}; formatFTV.isCheckedFunction = true; - const TypeId formatFn = arena->addType(formatFTV); + const TypeId formatFn = arena->addType(std::move(formatFTV)); attachMagicFunction(formatFn, std::make_shared()); @@ -1254,7 +1254,7 @@ TypeId makeStringMetatable(NotNull builtinTypes) arena->addTypePack({stringType, stringType, optionalNumber}), arena->addTypePack(TypePackVar{VariadicTypePack{stringType}}) }; matchFuncTy.isCheckedFunction = true; - const TypeId matchFunc = arena->addType(matchFuncTy); + const TypeId matchFunc = arena->addType(std::move(matchFuncTy)); attachMagicFunction(matchFunc, std::make_shared()); FunctionType findFuncTy{ @@ -1262,7 +1262,7 @@ TypeId makeStringMetatable(NotNull builtinTypes) arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList}) }; findFuncTy.isCheckedFunction = true; - const TypeId findFunc = arena->addType(findFuncTy); + const TypeId findFunc = arena->addType(std::move(findFuncTy)); attachMagicFunction(findFunc, std::make_shared()); // string.byte : string -> number? -> number? -> ...number @@ -1281,8 +1281,8 @@ TypeId makeStringMetatable(NotNull builtinTypes) stringDotUnpack.isCheckedFunction = true; TableType::Props stringLib = { - {"byte", {arena->addType(stringDotByte)}}, - {"char", {arena->addType(stringDotChar)}}, + {"byte", {arena->addType(std::move(stringDotByte))}}, + {"char", {arena->addType(std::move(stringDotChar))}}, {"find", {findFunc}}, {"format", {formatFn}}, // FIXME {"gmatch", {gmatchFunc}}, @@ -1311,7 +1311,7 @@ TypeId makeStringMetatable(NotNull builtinTypes) oneStringPack, })}}, {"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}}, - {"unpack", {arena->addType(stringDotUnpack)}}, + {"unpack", {arena->addType(std::move(stringDotUnpack))}}, }; assignPropDocumentationSymbols(stringLib, "@luau/global/string"); @@ -1331,7 +1331,7 @@ std::optional> MagicSelect::handleOldSolver( WithPredicate withPredicate ) { - auto [paramPack, _predicates] = withPredicate; + auto [paramPack, _predicates] = std::move(withPredicate); (void)scope; @@ -1421,7 +1421,7 @@ std::optional> MagicSetMetatable::handleOldSolver( WithPredicate withPredicate ) { - auto [paramPack, _predicates] = withPredicate; + auto [paramPack, _predicates] = std::move(withPredicate); if (size(paramPack) < 2 && finite(paramPack)) return std::nullopt; @@ -1455,7 +1455,7 @@ std::optional> MagicSetMetatable::handleOldSolver( mtv.syntheticName = tableName; } - TypeId mtTy = arena.addType(mtv); + TypeId mtTy = arena.addType(std::move(mtv)); if (expr.args.size < 1) return std::nullopt; @@ -1508,7 +1508,7 @@ std::optional> MagicAssert::handleOldSolver( WithPredicate withPredicate ) { - auto [paramPack, predicates] = withPredicate; + auto [paramPack, predicates] = std::move(withPredicate); TypeArena& arena = typechecker.currentModule->internalTypes; @@ -1547,7 +1547,7 @@ std::optional> MagicPack::handleOldSolver( WithPredicate withPredicate ) { - auto [paramPack, _predicates] = withPredicate; + auto [paramPack, _predicates] = std::move(withPredicate); TypeArena& arena = typechecker.currentModule->internalTypes; @@ -1632,7 +1632,7 @@ std::optional> MagicClone::handleOldSolver( { LUAU_ASSERT(FFlag::LuauTableCloneClonesType3); - auto [paramPack, _predicates] = withPredicate; + auto [paramPack, _predicates] = std::move(withPredicate); TypeArena& arena = typechecker.currentModule->internalTypes; diff --git a/Analysis/src/Clone.cpp b/Analysis/src/Clone.cpp index 4a1f6a4d..69785d30 100644 --- a/Analysis/src/Clone.cpp +++ b/Analysis/src/Clone.cpp @@ -12,6 +12,8 @@ LUAU_FASTFLAG(LuauSolverV2) // For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit. LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000) +LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticClone) + namespace Luau { @@ -73,12 +75,12 @@ public: if (hasExceededIterationLimit()) { - TypeId error = builtinTypes->errorRecoveryType(); + TypeId error = builtinTypes->errorType; (*types)[ty] = error; return error; } - return find(ty).value_or(builtinTypes->errorRecoveryType()); + return find(ty).value_or(builtinTypes->errorType); } TypePackId clone(TypePackId tp) @@ -88,12 +90,12 @@ public: if (hasExceededIterationLimit()) { - TypePackId error = builtinTypes->errorRecoveryTypePack(); + TypePackId error = builtinTypes->errorTypePack; (*packs)[tp] = error; return error; } - return find(tp).value_or(builtinTypes->errorRecoveryTypePack()); + return find(tp).value_or(builtinTypes->errorTypePack); } private: @@ -208,7 +210,7 @@ public: private: Property shallowClone(const Property& p) { - if (FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone) { std::optional cloneReadTy; if (auto ty = p.readTy) diff --git a/Analysis/src/Constraint.cpp b/Analysis/src/Constraint.cpp index 5d02967e..8fc31512 100644 --- a/Analysis/src/Constraint.cpp +++ b/Analysis/src/Constraint.cpp @@ -3,7 +3,7 @@ #include "Luau/Constraint.h" #include "Luau/VisitType.h" -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) namespace Luau { @@ -43,6 +43,17 @@ struct ReferenceCountInitializer : TypeOnceVisitor return false; } + bool visit(TypeId ty, const TableType& tt) override + { + if (FFlag::LuauEagerGeneralization4) + { + if (tt.state == TableState::Unsealed || tt.state == TableState::Free) + result->insert(ty); + } + + return true; + } + bool visit(TypeId ty, const ExternType&) override { // ExternTypes never contain free types. @@ -51,12 +62,18 @@ struct ReferenceCountInitializer : TypeOnceVisitor bool visit(TypeId, const TypeFunctionInstanceType&) override { - return FFlag::LuauEagerGeneralization3 && traverseIntoTypeFunctions; + return FFlag::LuauEagerGeneralization4 && traverseIntoTypeFunctions; } }; bool isReferenceCountedType(const TypeId typ) { + if (FFlag::LuauEagerGeneralization4) + { + if (auto tt = get(typ)) + return tt->state == TableState::Free || tt->state == TableState::Unsealed; + } + // n.b. this should match whatever `ReferenceCountInitializer` includes. return get(typ) || get(typ) || get(typ); } @@ -104,7 +121,7 @@ DenseHashSet Constraint::getMaybeMutatedFreeTypes() const { rci.traverse(fchc->argsPack); } - else if (auto fcc = get(*this); fcc && FFlag::LuauEagerGeneralization3) + else if (auto fcc = get(*this); fcc && FFlag::LuauEagerGeneralization4) { rci.traverseIntoTypeFunctions = false; rci.traverse(fcc->fn); @@ -118,12 +135,12 @@ DenseHashSet Constraint::getMaybeMutatedFreeTypes() const else if (auto hpc = get(*this)) { rci.traverse(hpc->resultType); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) rci.traverse(hpc->subjectType); } else if (auto hic = get(*this)) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) rci.traverse(hic->subjectType); rci.traverse(hic->resultType); // `HasIndexerConstraint` should not mutate `indexType`. diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 25ef8816..1d634a14 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -34,8 +34,8 @@ LUAU_FASTINT(LuauCheckRecursionLimit) LUAU_FASTFLAG(DebugLuauLogSolverToJson) LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAG(LuauEagerGeneralization3) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation) LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties) @@ -43,12 +43,14 @@ LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation) LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables) LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500) LUAU_FASTFLAGVARIABLE(LuauUserTypeFunctionAliases) LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment) +LUAU_FASTFLAGVARIABLE(LuauFollowTypeAlias) +LUAU_FASTFLAGVARIABLE(LuauFollowExistingTypeFunction) namespace Luau { @@ -256,7 +258,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) rootScope->location = block->location; module->astScopes[block] = NotNull{scope.get()}; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.emplace_back(); @@ -292,7 +294,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) } ); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -311,7 +313,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block) } ); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -349,13 +351,13 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat // We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block); // Pre - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.emplace_back(); visitBlockWithoutChildScope(resumeScope, block); // Post - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -385,12 +387,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity); interiorFreeTypes.back().types.push_back(ft); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) freeTypes.insert(ft); return ft; @@ -407,7 +409,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po { FreeTypePack f{scope.get(), polarity}; TypePackId result = arena->addTypePack(TypePackVar{std::move(f)}); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.back().typePacks.push_back(result); return result; } @@ -708,7 +710,7 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat const TypeFunction& func = kind == RefinementsOpKind::Intersect ? builtinTypeFunctions().intersectFunc : builtinTypeFunctions().refineFunc; LUAU_ASSERT(!func.name.empty()); args.insert(args.end(), discriminants.begin(), discriminants.end()); - TypeId resultType = createTypeFunctionInstance(func, args, {}, scope, location); + TypeId resultType = createTypeFunctionInstance(func, std::move(args), {}, scope, location); discriminants.clear(); return resultType; }; @@ -945,7 +947,8 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc auto addToEnvironment = [this, &globalNameCollector](UserDefinedFunctionData& userFuncData, ScopePtr scope, const Name& name, TypeFun tf, size_t level) { - if (auto ty = get(tf.type); ty && ty->userFuncData.definition) + if (auto ty = get(FFlag::LuauFollowTypeAlias ? follow(tf.type) : tf.type); + ty && ty->userFuncData.definition) { if (userFuncData.environmentFunction.find(name)) return; @@ -958,7 +961,8 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc scope->bindings[ty->userFuncData.definition->name] = Binding{existing->typeId, ty->userFuncData.definition->location}; } } - else if (FFlag::LuauUserTypeFunctionAliases && !get(tf.type)) + else if (FFlag::LuauUserTypeFunctionAliases && + !get(FFlag::LuauFollowTypeAlias ? follow(tf.type) : tf.type)) { if (userFuncData.environmentAlias.find(name)) return; @@ -1417,7 +1421,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location); sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->name->location}; - bool sigFullyDefined = FFlag::LuauEagerGeneralization3 ? false : !hasFreeType(sig.signature); + bool sigFullyDefined = FFlag::LuauEagerGeneralization4 ? false : !hasFreeType(sig.signature); if (sigFullyDefined) emplaceType(asMutable(functionType), sig.signature); @@ -1477,7 +1481,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f Checkpoint start = checkpoint(this); FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location); - bool sigFullyDefined = FFlag::LuauEagerGeneralization3 ? false : !hasFreeType(sig.signature); + bool sigFullyDefined = FFlag::LuauEagerGeneralization4 ? false : !hasFreeType(sig.signature); DefId def = dfg->getDef(function->name); @@ -1564,7 +1568,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f } else if (AstExprError* err = function->name->as()) { - generalizedType = builtinTypes->errorRecoveryType(); + generalizedType = builtinTypes->errorType; } if (generalizedType == nullptr) @@ -1794,7 +1798,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio // Place this function as a child of the non-type function scope scope->children.push_back(NotNull{sig.signatureScope.get()}); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.push_back(std::vector{}); @@ -1812,7 +1816,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio } ); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -1821,7 +1825,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back()); getMutable(generalizedTy)->setOwner(gc); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.pop_back(); else DEPRECATED_interiorTypes.pop_back(); @@ -1850,8 +1854,18 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio if (!existingFunctionTy) ice->ice("checkAliases did not populate type function name", function->nameLocation); - if (auto bt = get(*existingFunctionTy); bt && nullptr == bt->getOwner()) - emplaceType(asMutable(*existingFunctionTy), generalizedTy); + if (FFlag::LuauFollowExistingTypeFunction) + { + TypeId unpackedTy = follow(*existingFunctionTy); + + if (auto bt = get(unpackedTy); bt && nullptr == bt->getOwner()) + emplaceType(asMutable(unpackedTy), generalizedTy); + } + else + { + if (auto bt = get(*existingFunctionTy); bt && nullptr == bt->getOwner()) + emplaceType(asMutable(*existingFunctionTy), generalizedTy); + } return ControlFlow::None; } @@ -1896,7 +1910,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte if (!lookupType) { - reportError(declaredExternType->location, UnknownSymbol{superName, UnknownSymbol::Type}); + reportError(declaredExternType->location, UnknownSymbol{std::move(superName), UnknownSymbol::Type}); return ControlFlow::None; } @@ -1918,7 +1932,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte Name className(declaredExternType->name.value); - TypeId externTy = arena->addType(ExternType(className, {}, superTy, std::nullopt, {}, {}, module->name, declaredExternType->location)); + TypeId externTy = arena->addType(ExternType(std::move(className), {}, superTy, std::nullopt, {}, {}, module->name, declaredExternType->location)); ExternType* etv = getMutable(externTy); TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()}); @@ -2120,7 +2134,7 @@ InferencePack ConstraintGenerator::checkPack( if (recursionCount >= FInt::LuauCheckRecursionLimit) { reportCodeTooComplex(expr->location); - return InferencePack{builtinTypes->errorRecoveryTypePack()}; + return InferencePack{builtinTypes->errorTypePack}; } InferencePack result; @@ -2132,7 +2146,7 @@ InferencePack ConstraintGenerator::checkPack( if (scope->varargPack) result = InferencePack{*scope->varargPack}; else - result = InferencePack{builtinTypes->errorRecoveryTypePack()}; + result = InferencePack{builtinTypes->errorTypePack}; } else { @@ -2189,7 +2203,14 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall* Checkpoint funcBeginCheckpoint = checkpoint(this); - TypeId fnType = check(scope, call->func).ty; + TypeId fnType = nullptr; + if (FFlag::LuauEagerGeneralization4) + { + InConditionalContext icc2{&typeContext, TypeContext::Default}; + fnType = check(scope, call->func).ty; + } + else + fnType = check(scope, call->func).ty; Checkpoint funcEndCheckpoint = checkpoint(this); @@ -2273,7 +2294,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall* mt = arena->addType(BlockedType{}); unpackedTypes.emplace_back(mt); - auto c = addConstraint(scope, call->location, UnpackConstraint{unpackedTypes, *argTail}); + auto c = addConstraint(scope, call->location, UnpackConstraint{std::move(unpackedTypes), *argTail}); getMutable(mt)->setOwner(c); if (auto b = getMutable(target); b && b->getOwner() == nullptr) b->setOwner(c); @@ -2402,7 +2423,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std:: if (recursionCount >= FInt::LuauCheckRecursionLimit) { reportCodeTooComplex(expr->location); - return Inference{builtinTypes->errorRecoveryType()}; + return Inference{builtinTypes->errorType}; } // We may recurse a given expression more than once when checking compound @@ -2459,7 +2480,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std:: for (AstExpr* subExpr : err->expressions) check(scope, subExpr); - result = Inference{builtinTypes->errorRecoveryType()}; + result = Inference{builtinTypes->errorType}; } else { @@ -2491,7 +2512,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin return Inference{builtinTypes->stringType}; TypeId freeTy = nullptr; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { freeTy = freshType(scope, Polarity::Positive); FreeType* ft = getMutable(freeTy); @@ -2532,7 +2553,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool* return Inference{builtinTypes->booleanType}; TypeId freeTy = nullptr; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { freeTy = freshType(scope, Polarity::Positive); FreeType* ft = getMutable(freeTy); @@ -2591,7 +2612,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprGlobal* globa return Inference{*ty, refinementArena.proposition(key, builtinTypes->truthyType)}; } else - return Inference{builtinTypes->errorRecoveryType()}; + return Inference{builtinTypes->errorType}; } Inference ConstraintGenerator::checkIndexName( @@ -2688,10 +2709,14 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIndexExpr* in Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* func, std::optional expectedType, bool generalize) { + std::optional inContext; + if (FFlag::LuauEagerGeneralization4) + inContext.emplace(&typeContext, TypeContext::Default); + Checkpoint startCheckpoint = checkpoint(this); FunctionSignature sig = checkFunctionSignature(scope, func, expectedType); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.emplace_back(); else DEPRECATED_interiorTypes.push_back(std::vector{}); @@ -2709,7 +2734,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun } ); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); @@ -2755,6 +2780,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprUnary* unary) { + std::optional inContext; + if (FFlag::LuauEagerGeneralization4 && unary->op != AstExprUnary::Op::Not) + inContext.emplace(&typeContext, TypeContext::Default); + auto [operandType, refinement] = check(scope, unary->expr); switch (unary->op) @@ -2916,6 +2945,10 @@ Inference ConstraintGenerator::checkAstExprBinary( Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional expectedType) { + std::optional inContext; + if (FFlag::LuauEagerGeneralization4) + inContext.emplace(&typeContext, TypeContext::Default); + RefinementId refinement = [&]() { InConditionalContext flipper{&typeContext}; @@ -2942,6 +2975,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTypeAssertion Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprInterpString* interpString) { + std::optional inContext; + if (FFlag::LuauEagerGeneralization4) + inContext.emplace(&typeContext, TypeContext::Default); + for (AstExpr* expr : interpString->expressions) check(scope, expr); @@ -2956,6 +2993,13 @@ std::tuple ConstraintGenerator::checkBinary( std::optional expectedType ) { + std::optional inContext; + if (FFlag::LuauEagerGeneralization4) + { + if (op != AstExprBinary::And && op != AstExprBinary::Or && op != AstExprBinary::CompareEq && op != AstExprBinary::CompareNe) + inContext.emplace(&typeContext, TypeContext::Default); + } + if (op == AstExprBinary::And) { std::optional relaxedExpectedLhs; @@ -3200,6 +3244,10 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprIndexExpr* e Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, std::optional expectedType) { + std::optional inContext; + if (FFlag::LuauEagerGeneralization4) + inContext.emplace(&typeContext, TypeContext::Default); + TypeId ty = arena->addType(TableType{}); TableType* ttv = getMutable(ty); LUAU_ASSERT(ttv); @@ -3213,7 +3261,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit)) largeTableDepth++; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) interiorFreeTypes.back().types.push_back(ty); else DEPRECATED_interiorTypes.back().push_back(ty); @@ -3306,7 +3354,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, } } - if (expectedType && !FFlag::LuauTableLiteralSubtypeSpecificCheck) + if (expectedType && !FFlag::LuauTableLiteralSubtypeSpecificCheck2) { addConstraint( scope, @@ -3474,7 +3522,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu LUAU_ASSERT(nullptr != varargPack); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // Some of the types in argTypes will eventually be generics, and some // will not. The ones that are not generic will be pruned when @@ -3517,7 +3565,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu // TODO: Preserve argument names in the function's type. - FunctionType actualFunction{TypeLevel{}, arena->addTypePack(argTypes, varargPack), returnType}; + FunctionType actualFunction{TypeLevel{}, arena->addTypePack(std::move(argTypes), varargPack), returnType}; actualFunction.generics = std::move(genericTypes); actualFunction.genericPacks = std::move(genericTypePacks); actualFunction.argNames = std::move(argNames); @@ -3539,13 +3587,13 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu if (expectedType && get(*expectedType)) bindFreeType(*expectedType, actualFunctionType); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) scopeToFunction[signatureScope.get()] = actualFunctionType; return { /* signature */ actualFunctionType, - /* signatureScope */ signatureScope, - /* bodyScope */ bodyScope, + /* signatureScope */ std::move(signatureScope), + /* bodyScope */ std::move(bodyScope), }; } @@ -3576,8 +3624,8 @@ TypeId ConstraintGenerator::resolveReferenceType( if (ref->parameters.size != 1 || !ref->parameters.data[0].type) { reportError(ty->location, GenericError{"_luau_print requires one generic parameter"}); - module->astResolvedTypes[ty] = builtinTypes->errorRecoveryType(); - return builtinTypes->errorRecoveryType(); + module->astResolvedTypes[ty] = builtinTypes->errorType; + return builtinTypes->errorType; } else return resolveType_(scope, ref->parameters.data[0].type, inTypeArguments); @@ -3633,7 +3681,7 @@ TypeId ConstraintGenerator::resolveReferenceType( } } - result = arena->addType(PendingExpansionType{ref->prefix, ref->name, parameters, packParameters}); + result = arena->addType(PendingExpansionType{ref->prefix, ref->name, std::move(parameters), std::move(packParameters)}); // If we're not in a type argument context, we need to create a constraint that expands this. // The dispatching of the above constraint will queue up additional constraints for nested @@ -3644,7 +3692,7 @@ TypeId ConstraintGenerator::resolveReferenceType( } else { - result = builtinTypes->errorRecoveryType(); + result = builtinTypes->errorType; if (replaceErrorWithFresh) result = freshType(scope, Polarity::Mixed); } @@ -3840,7 +3888,7 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo parts.push_back(resolveType_(scope, part, inTypeArguments)); } - result = arena->addType(UnionType{parts}); + result = arena->addType(UnionType{std::move(parts)}); } } else if (auto intersectionAnnotation = ty->as()) @@ -3855,7 +3903,7 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo parts.push_back(resolveType_(scope, part, inTypeArguments)); } - result = arena->addType(IntersectionType{parts}); + result = arena->addType(IntersectionType{std::move(parts)}); } } else if (auto typeGroupAnnotation = ty->as()) @@ -3875,14 +3923,14 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo } else if (ty->is()) { - result = builtinTypes->errorRecoveryType(); + result = builtinTypes->errorType; if (replaceErrorWithFresh) result = freshType(scope); } else { LUAU_ASSERT(0); - result = builtinTypes->errorRecoveryType(); + result = builtinTypes->errorType; } module->astResolvedTypes[ty] = result; @@ -3917,13 +3965,13 @@ TypePackId ConstraintGenerator::resolveTypePack_(const ScopePtr& scope, AstTypeP else { reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type}); - result = builtinTypes->errorRecoveryTypePack(); + result = builtinTypes->errorTypePack; } } else { LUAU_ASSERT(0); - result = builtinTypes->errorRecoveryTypePack(); + result = builtinTypes->errorTypePack; } module->astResolvedTypePacks[tp] = result; @@ -4391,7 +4439,7 @@ TypeId ConstraintGenerator::createTypeFunctionInstance( Location location ) { - TypeId result = arena->addTypeFunction(function, typeArguments, packArguments); + TypeId result = arena->addTypeFunction(function, std::move(typeArguments), std::move(packArguments)); addConstraint(scope, location, ReduceConstraint{result}); return result; } diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 0aba6a22..9548ca5f 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -32,13 +32,11 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies) LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings) LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauEagerGeneralization3) -LUAU_FASTFLAG(LuauDeprecatedAttribute) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2) LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult) LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck) LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint) @@ -187,7 +185,7 @@ std::pair, std::vector> saturateArguments( if (!defaultTy) break; - TypeId instantiatedDefault = atf.substitute(defaultTy).value_or(builtinTypes->errorRecoveryType()); + TypeId instantiatedDefault = atf.substitute(defaultTy).value_or(builtinTypes->errorType); atf.typeArguments[fn.typeParams[i].ty] = instantiatedDefault; saturatedTypeArguments.push_back(instantiatedDefault); } @@ -205,7 +203,7 @@ std::pair, std::vector> saturateArguments( if (!defaultTp) break; - TypePackId instantiatedDefault = atf.substitute(defaultTp).value_or(builtinTypes->errorRecoveryTypePack()); + TypePackId instantiatedDefault = atf.substitute(defaultTp).value_or(builtinTypes->errorTypePack); atf.typePackArguments[fn.typePackParams[i].tp] = instantiatedDefault; saturatedPackArguments.push_back(instantiatedDefault); } @@ -223,12 +221,12 @@ std::pair, std::vector> saturateArguments( // even if they're missing, so we use the error type as a filler. for (size_t i = saturatedTypeArguments.size(); i < typesRequired; ++i) { - saturatedTypeArguments.push_back(builtinTypes->errorRecoveryType()); + saturatedTypeArguments.push_back(builtinTypes->errorType); } for (size_t i = saturatedPackArguments.size(); i < packsRequired; ++i) { - saturatedPackArguments.push_back(builtinTypes->errorRecoveryTypePack()); + saturatedPackArguments.push_back(builtinTypes->errorTypePack); } for (TypeId& arg : saturatedTypeArguments) @@ -416,7 +414,7 @@ void ConstraintSolver::run() } // Free types that have no constraints at all can be generalized right away. - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { for (TypeId ty : constraintSet.freeTypes) { @@ -477,7 +475,7 @@ void ConstraintSolver::run() // expansion types, etc, so we need to follow it. ty = follow(ty); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { if (seen.contains(ty)) continue; @@ -496,7 +494,7 @@ void ConstraintSolver::run() if (refCount <= 1) unblock(ty, Location{}); - if (FFlag::LuauEagerGeneralization3 && refCount == 0) + if (FFlag::LuauEagerGeneralization4 && refCount == 0) generalizeOneType(ty); } } @@ -674,7 +672,7 @@ void ConstraintSolver::initFreeTypeTracking() auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0); refCount += 1; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr); it->second.insert(c.get()); @@ -730,7 +728,7 @@ void ConstraintSolver::bind(NotNull constraint, TypeId ty, Typ constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed ); // FIXME? Is this the right polarity? - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) trackInteriorFreeType(constraint->scope, ty); return; @@ -882,7 +880,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNulllocation); - bind(constraint, c.generalizedType, builtinTypes->errorRecoveryType()); + bind(constraint, c.generalizedType, builtinTypes->errorType); } // We check if this member is initialized and then access it, but @@ -891,7 +889,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNullscope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { ty = follow(ty); if (auto freeTy = get(ty)) @@ -912,7 +910,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNullscope->interiorFreeTypePacks) { @@ -932,7 +930,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNullerrorRecoveryType(), ty); + unify(constraint, builtinTypes->errorType, ty); return true; } @@ -1152,7 +1150,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2 && occursCheck(cTarget, result)) { reportError(OccursCheckFailed{}, constraint->location); - bind(constraint, cTarget, builtinTypes->errorRecoveryType()); + bind(constraint, cTarget, builtinTypes->errorType); } else { @@ -1167,7 +1165,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul if (!tf.has_value()) { reportError(UnknownSymbol{petv->name.value, UnknownSymbol::Context::Type}, constraint->location); - bindResult(errorRecoveryType()); + bindResult(builtinTypes->errorType); return true; } @@ -1183,7 +1181,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul if (occursCheck(lhs, rhs)) { reportError(OccursCheckFailed{}, constraint->location); - bindResult(errorRecoveryType()); + bindResult(builtinTypes->errorType); return true; } @@ -1254,7 +1252,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul if (itf.foundInfiniteType) { // TODO (CLI-56761): Report an error. - bindResult(errorRecoveryType()); + bindResult(builtinTypes->errorType); reportError(GenericError{"Recursive type being used with different parameters"}, constraint->location); return true; } @@ -1278,7 +1276,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul if (!maybeInstantiated.has_value()) { // TODO (CLI-56761): Report an error. - bindResult(errorRecoveryType()); + bindResult(builtinTypes->errorType); return true; } @@ -1377,7 +1375,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull(fn)) { - bind(constraint, c.result, builtinTypes->errorRecoveryTypePack()); + bind(constraint, c.result, builtinTypes->errorTypePack); fillInDiscriminantTypes(constraint, c.discriminantTypes); return true; } @@ -1517,7 +1515,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNullscope, freeTy); @@ -2084,7 +2082,7 @@ bool ConstraintSolver::tryDispatchHasIndexer( TypeId upperBound = arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed}); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { TypeId sr = follow(simplifyIntersection(constraint->scope, constraint->location, ft->upperBound, upperBound)); @@ -2115,7 +2113,7 @@ bool ConstraintSolver::tryDispatchHasIndexer( FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed}; emplace(constraint, resultType, freeResult); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) trackInteriorFreeType(constraint->scope, resultType); tt->indexer = TableIndexer{indexType, resultType}; @@ -2304,7 +2302,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNullupperBound); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue); if (!blocked.empty()) @@ -2424,6 +2422,10 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNullstate == TableState::Unsealed || lhsTable->state == TableState::Free) { + // eg if inserting a free type 'a into a table {| |}, anything that + // might affect {| |} is now known to potentially affect 'a + shiftReferences(lhsType, rhsType); + bind(constraint, c.propType, rhsType); Property& newProp = lhsTable->props[propName]; newProp.readTy = rhsType; @@ -2907,7 +2909,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl { std::vector expectedVariables{iteratorTable->indexer->indexType, iteratorTable->indexer->indexResultType}; while (c.variables.size() >= expectedVariables.size()) - expectedVariables.push_back(builtinTypes->errorRecoveryType()); + expectedVariables.push_back(builtinTypes->errorType); for (size_t i = 0; i < c.variables.size(); ++i) { @@ -3216,7 +3218,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( { const TypeId upperBound = follow(ft->upperBound); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { if (get(upperBound) || get(upperBound)) { @@ -3273,7 +3275,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( } if (!blocked.empty()) - return {blocked, std::nullopt}; + return {std::move(blocked), std::nullopt}; if (options.empty()) return {{}, std::nullopt}; @@ -3310,7 +3312,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( } if (!blocked.empty()) - return {blocked, std::nullopt}; + return {std::move(blocked), std::nullopt}; if (options.empty()) return {{}, std::nullopt}; @@ -3613,7 +3615,7 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l if (info.name.empty()) { reportError(UnknownRequire{}, location); - return errorRecoveryType(); + return builtinTypes->errorType; } for (const auto& [location, path] : requireCycles) @@ -3628,24 +3630,24 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l if (!moduleResolver->moduleExists(info.name) && !info.optional) reportError(UnknownRequire{moduleResolver->getHumanReadableModuleName(info.name)}, location); - return errorRecoveryType(); + return builtinTypes->errorType; } if (module->type != SourceCode::Type::Module) { reportError(IllegalRequire{module->humanReadableName, "Module is not a ModuleScript. It cannot be required."}, location); - return errorRecoveryType(); + return builtinTypes->errorType; } TypePackId modulePack = module->returnType; if (get(modulePack)) - return errorRecoveryType(); + return builtinTypes->errorType; std::optional moduleType = first(modulePack); if (!moduleType) { reportError(IllegalRequire{module->humanReadableName, "Module does not return exactly 1 value. It cannot be required."}, location); - return errorRecoveryType(); + return builtinTypes->errorType; } return *moduleType; @@ -3673,28 +3675,33 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target) return; auto sourceRefs = unresolvedConstraints.find(source); - if (!sourceRefs) - return; + if (sourceRefs) + { + // we read out the count before proceeding to avoid hash invalidation issues. + size_t count = *sourceRefs; - // we read out the count before proceeding to avoid hash invalidation issues. - size_t count = *sourceRefs; - - auto [targetRefs, _] = unresolvedConstraints.try_insert(target, 0); - targetRefs += count; + auto [targetRefs, _] = unresolvedConstraints.try_insert(target, 0); + targetRefs += count; + } // Any constraint that might have mutated source may now mutate target - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { auto it = mutatedFreeTypeToConstraint.find(source); if (it != mutatedFreeTypeToConstraint.end()) { - auto [it2, fresh] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet{nullptr}); - for (const Constraint* constraint : it->second) - { - it2->second.insert(constraint); + const DenseHashSet& constraintsAffectedBySource = it->second; - auto [it3, fresh2] = maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, DenseHashSet{nullptr}); + auto [it2, fresh2] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet{nullptr}); + DenseHashSet& constraintsAffectedByTarget = it2->second; + + // auto [it2, fresh] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet{nullptr}); + for (const Constraint* constraint : constraintsAffectedBySource) + { + constraintsAffectedByTarget.insert(constraint); + + auto [it3, fresh3] = maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, DenseHashSet{nullptr}); it3->second.insert(target); } } @@ -3784,16 +3791,6 @@ TypeId ConstraintSolver::simplifyUnion(NotNull scope, Location location, return ::Luau::simplifyUnion(builtinTypes, arena, left, right).result; } -TypeId ConstraintSolver::errorRecoveryType() const -{ - return builtinTypes->errorRecoveryType(); -} - -TypePackId ConstraintSolver::errorRecoveryTypePack() const -{ - return builtinTypes->errorRecoveryTypePack(); -} - TypePackId ConstraintSolver::anyifyModuleReturnTypePackGenerics(TypePackId tp) { tp = follow(tp); @@ -3821,7 +3818,7 @@ TypePackId ConstraintSolver::anyifyModuleReturnTypePackGenerics(TypePackId tp) if (std::optional tail = it.tail()) resultTail = anyifyModuleReturnTypePackGenerics(*tail); - return arena->addTypePack(resultTypes, resultTail); + return arena->addTypePack(std::move(resultTypes), resultTail); } LUAU_NOINLINE void ConstraintSolver::throwTimeLimitError() const diff --git a/Analysis/src/DcrLogger.cpp b/Analysis/src/DcrLogger.cpp index f013b985..3d706fe1 100644 --- a/Analysis/src/DcrLogger.cpp +++ b/Analysis/src/DcrLogger.cpp @@ -253,10 +253,10 @@ static ScopeSnapshot snapshotScope(const Scope* scope, ToStringOptions& opts) } return ScopeSnapshot{ - bindings, - typeBindings, - typePackBindings, - children, + std::move(bindings), + std::move(typeBindings), + std::move(typePackBindings), + std::move(children), }; } @@ -307,7 +307,7 @@ void DcrLogger::captureGenerationError(const TypeError& error) { std::string stringifiedError = toString(error); generationLog.errors.push_back(ErrorSnapshot{ - /* message */ stringifiedError, + /* message */ std::move(stringifiedError), /* location */ error.location, }); } @@ -424,7 +424,7 @@ StepSnapshot DcrLogger::prepareStepSnapshot( current, force, std::move(constraints), - scopeSnapshot, + std::move(scopeSnapshot), std::move(typeStrings), }; } @@ -443,7 +443,7 @@ void DcrLogger::captureTypeCheckError(const TypeError& error) { std::string stringifiedError = toString(error); checkLog.errors.push_back(ErrorSnapshot{ - /* message */ stringifiedError, + /* message */ std::move(stringifiedError), /* location */ error.location, }); } diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index fa2df2da..acc91b19 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -18,7 +18,7 @@ #include LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive) @@ -159,7 +159,7 @@ struct ErrorConverter } if (result.empty()) - result = constructErrorMessage(givenTypeName, wantedTypeName, std::nullopt, std::nullopt); + result = constructErrorMessage(std::move(givenTypeName), std::move(wantedTypeName), std::nullopt, std::nullopt); if (tm.error) @@ -663,7 +663,7 @@ struct ErrorConverter } // binary operators - const auto binaryOps = FFlag::LuauEagerGeneralization3 ? kBinaryOps : DEPRECATED_kBinaryOps; + const auto binaryOps = FFlag::LuauEagerGeneralization4 ? kBinaryOps : DEPRECATED_kBinaryOps; if (auto binaryString = binaryOps.find(tfit->function->name); binaryString != binaryOps.end()) { std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types "; @@ -718,7 +718,7 @@ struct ErrorConverter "'"; } - if ((FFlag::LuauEagerGeneralization3 ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name)) + if ((FFlag::LuauEagerGeneralization4 ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name)) { return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" + "This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues"; @@ -906,14 +906,14 @@ TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType) TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason) : wantedType(wantedType) , givenType(givenType) - , reason(reason) + , reason(std::move(reason)) { } TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional error) : wantedType(wantedType) , givenType(givenType) - , reason(reason) + , reason(std::move(reason)) , error(error ? std::make_shared(std::move(*error)) : nullptr) { } @@ -929,7 +929,7 @@ TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reas : wantedType(wantedType) , givenType(givenType) , context(context) - , reason(reason) + , reason(std::move(reason)) { } @@ -937,7 +937,7 @@ TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reas : wantedType(wantedType) , givenType(givenType) , context(context) - , reason(reason) + , reason(std::move(reason)) , error(error ? std::make_shared(std::move(*error)) : nullptr) { } diff --git a/Analysis/src/ExpectedTypeVisitor.cpp b/Analysis/src/ExpectedTypeVisitor.cpp index b2a9fa9d..4862a8cb 100644 --- a/Analysis/src/ExpectedTypeVisitor.cpp +++ b/Analysis/src/ExpectedTypeVisitor.cpp @@ -3,8 +3,12 @@ #include "Luau/Scope.h" #include "Luau/TypeArena.h" +#include "Luau/TypeIds.h" #include "Luau/TypePack.h" #include "Luau/TypeUtils.h" +#include "Luau/VisitType.h" + +LUAU_FASTFLAGVARIABLE(LuauImplicitTableIndexerKeys) namespace Luau { @@ -70,6 +74,106 @@ bool ExpectedTypeVisitor::visit(AstStatReturn* stat) return true; } +namespace +{ + +struct IndexerIndexCollector : public TypeOnceVisitor +{ + NotNull indexes; + + explicit IndexerIndexCollector(NotNull indexes) : TypeOnceVisitor(/* skipBoundTypes */ true), indexes(indexes) + { + } + + bool visit(TypeId ty) override + { + indexes->insert(ty); + return false; + } + + bool visit(TypeId, const UnionType&) override + { + return true; + } + + bool visit(TypeId, const IntersectionType&) override + { + return true; + } + +}; + +struct IndexCollector : public TypeOnceVisitor +{ + NotNull arena; + TypeIds indexes; + + explicit IndexCollector(NotNull arena) : TypeOnceVisitor(/* skipBoundTypes */ true), arena(arena) + { + } + + bool visit(TypeId ty) override + { + return false; + } + + bool visit(TypeId, const UnionType&) override + { + return true; + } + + bool visit(TypeId, const IntersectionType&) override + { + return true; + } + + bool visit(TypeId, const TableType& ttv) override + { + for (const auto& [name, _] : ttv.props) + indexes.insert(arena->addType(SingletonType{StringSingleton{name}})); + + if (ttv.indexer) + { + IndexerIndexCollector iic{NotNull{&indexes}}; + iic.traverse(ttv.indexer->indexType); + } + + return false; + } + +}; + +} + +bool ExpectedTypeVisitor::visit(AstExprIndexExpr* expr) +{ + if (!FFlag::LuauImplicitTableIndexerKeys) + return true; + + if (auto ty = astTypes->find(expr->expr)) + { + IndexCollector ic{arena}; + ic.traverse(*ty); + if (ic.indexes.size() > 1) + { + applyExpectedType( + arena->addType(UnionType{ic.indexes.take()}), + expr->index + ); + } + else if (ic.indexes.size() == 1) + { + applyExpectedType( + *ic.indexes.begin(), + expr->index + ); + } + } + + return true; +} + + bool ExpectedTypeVisitor::visit(AstExprCall* expr) { auto ty = astTypes->find(expr->func); diff --git a/Analysis/src/FragmentAutocomplete.cpp b/Analysis/src/FragmentAutocomplete.cpp index b20830f5..7d056bb8 100644 --- a/Analysis/src/FragmentAutocomplete.cpp +++ b/Analysis/src/FragmentAutocomplete.cpp @@ -479,7 +479,7 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* st } } - return {localMap, localStack, ancestry, region.nearestStatement, region.parentBlock, region.fragmentLocation}; + return {std::move(localMap), std::move(localStack), std::move(ancestry), region.nearestStatement, region.parentBlock, region.fragmentLocation}; } std::optional parseFragment( @@ -512,7 +512,7 @@ std::optional parseFragment( opts.allowDeclarationSyntax = false; opts.captureComments = true; opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack), startPos}; - ParseResult p = Luau::Parser::parse(srcStart, parseLength, *names, *fragmentResult.alloc, opts); + ParseResult p = Luau::Parser::parse(srcStart, parseLength, *names, *fragmentResult.alloc, std::move(opts)); // This means we threw a ParseError and we should decline to offer autocomplete here. if (p.root == nullptr) return std::nullopt; @@ -1037,7 +1037,7 @@ std::optional parseFragment_DEPRECATED( opts.allowDeclarationSyntax = false; opts.captureComments = true; opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack), startPos}; - ParseResult p = Luau::Parser::parse(srcStart, parseLength, *names, *fragmentResult.alloc, opts); + ParseResult p = Luau::Parser::parse(srcStart, parseLength, *names, *fragmentResult.alloc, std::move(opts)); // This means we threw a ParseError and we should decline to offer autocomplete here. if (p.root == nullptr) return std::nullopt; @@ -1192,7 +1192,7 @@ FragmentTypeCheckResult typecheckFragment_( {}, nullptr, NotNull{&dfg}, - limits + std::move(limits) }; try @@ -1351,7 +1351,7 @@ FragmentAutocompleteResult fragmentAutocomplete( tcResult.freshScope, cursorPosition, frontend.fileResolver, - callback + std::move(callback) ); if (FFlag::LuauFragmentAcMemoryLeak) freeze(tcResult.incrementalModule->internalTypes); diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 0e8c48be..6b3af463 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -40,7 +40,7 @@ LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(LuauInferInNoCheckMode) LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile) LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes) @@ -207,16 +207,16 @@ LoadDefinitionFileResult Frontend::loadDefinitionFile( Luau::ParseResult parseResult = parseSourceForModule(source, sourceModule, captureComments); if (parseResult.errors.size() > 0) - return LoadDefinitionFileResult{false, parseResult, sourceModule, nullptr}; + return LoadDefinitionFileResult{false, std::move(parseResult), std::move(sourceModule), nullptr}; ModulePtr checkedModule = check(sourceModule, Mode::Definition, {}, std::nullopt, /*forAutocomplete*/ false, /*recordJsonLog*/ false, {}); if (checkedModule->errors.size() > 0) - return LoadDefinitionFileResult{false, parseResult, sourceModule, checkedModule}; + return LoadDefinitionFileResult{false, std::move(parseResult), std::move(sourceModule), std::move(checkedModule)}; - persistCheckedTypes(checkedModule, globals, targetScope, packageName); + persistCheckedTypes(checkedModule, globals, std::move(targetScope), packageName); - return LoadDefinitionFileResult{true, parseResult, sourceModule, checkedModule}; + return LoadDefinitionFileResult{true, std::move(parseResult), std::move(sourceModule), std::move(checkedModule)}; } namespace @@ -970,7 +970,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) environmentScope, /*forAutocomplete*/ true, /*recordJsonLog*/ false, - typeCheckLimits + std::move(typeCheckLimits) ); double duration = getTimestamp() - timestamp; @@ -990,7 +990,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) return; } - ModulePtr module = check(sourceModule, mode, requireCycles, environmentScope, /*forAutocomplete*/ false, item.recordJsonLog, typeCheckLimits); + ModulePtr module = check(sourceModule, mode, requireCycles, environmentScope, /*forAutocomplete*/ false, item.recordJsonLog, std::move(typeCheckLimits)); double duration = getTimestamp() - timestamp; @@ -1172,7 +1172,7 @@ void Frontend::sendQueueCycleItemTask(std::shared_ptr state if (!item.processing) { - sendQueueItemTask(state, i); + sendQueueItemTask(std::move(state), i); break; } } @@ -1314,10 +1314,10 @@ ModulePtr check( parentScope, typeFunctionScope, std::move(prepareModuleScope), - options, - limits, + std::move(options), + std::move(limits), recordJsonLog, - writeJsonLog + std::move(writeJsonLog) ); } @@ -1438,13 +1438,13 @@ ModulePtr check( requireCycles }; - // FIXME: Delete this flag when clipping FFlag::LuauEagerGeneralization2. + // FIXME: Delete this flag when clipping FFlag::LuauEagerGeneralization4. // // This optional<> only exists so that we can run one constructor when the flag // is set, and another when it is unset. std::optional cs; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { ConstraintSet constraintSet = cg.run(sourceModule.root); result->errors = std::move(constraintSet.errors); @@ -1522,13 +1522,13 @@ ModulePtr check( // If solver was interrupted, skip typechecking and replace all module results with error-supressing types to avoid leaking blocked/pending // types ScopePtr moduleScope = result->getModuleScope(); - moduleScope->returnType = builtinTypes->errorRecoveryTypePack(); + moduleScope->returnType = builtinTypes->errorTypePack; for (auto& [name, ty] : result->declaredGlobals) - ty = builtinTypes->errorRecoveryType(); + ty = builtinTypes->errorType; for (auto& [name, tf] : result->exportedTypeBindings) - tf.type = builtinTypes->errorRecoveryType(); + tf.type = builtinTypes->errorType; } else if (FFlag::LuauNewSolverTypecheckCatchTimeouts) { @@ -1705,7 +1705,7 @@ ModulePtr Frontend::check( globals.globalTypeFunctionScope, prepareModuleScopeWrap, options, - typeCheckLimits, + std::move(typeCheckLimits), recordJsonLog, writeJsonLog ); @@ -1740,7 +1740,7 @@ ModulePtr Frontend::check( typeChecker.unifierIterationLimit = typeCheckLimits.unifierIterationLimit; typeChecker.cancellationToken = typeCheckLimits.cancellationToken; - return typeChecker.check(sourceModule, mode, environmentScope); + return typeChecker.check(sourceModule, mode, std::move(environmentScope)); } } diff --git a/Analysis/src/Generalization.cpp b/Analysis/src/Generalization.cpp index 5e38ba43..785abb75 100644 --- a/Analysis/src/Generalization.cpp +++ b/Analysis/src/Generalization.cpp @@ -16,7 +16,7 @@ LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) -LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization3) +LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(LuauGeneralizationCannotMutateAcrossModules) namespace Luau @@ -470,7 +470,7 @@ struct FreeTypeSearcher : TypeVisitor bool visit(TypeId ty, const FreeType& ft) override { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { if (!subsumes(scope, ft.scope)) return true; @@ -521,7 +521,7 @@ struct FreeTypeSearcher : TypeVisitor if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope)) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) unsealedTables.insert(ty); else { @@ -594,7 +594,7 @@ struct FreeTypeSearcher : TypeVisitor if (tt.indexer) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // {[K]: V} is equivalent to three functions: get, set, and iterate // @@ -652,7 +652,7 @@ struct FreeTypeSearcher : TypeVisitor if (!subsumes(scope, ftp.scope)) return true; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { GeneralizationParams& params = typePacks[tp]; ++params.useCount; @@ -1223,7 +1223,7 @@ GeneralizationResult generalizeType( if (!hasLowerBound && !hasUpperBound) { - if (!isWithinFunction || (!FFlag::LuauEagerGeneralization3 && (params.polarity != Polarity::Mixed && params.useCount == 1))) + if (!isWithinFunction || (!FFlag::LuauEagerGeneralization4 && (params.polarity != Polarity::Mixed && params.useCount == 1))) emplaceType(asMutable(freeTy), builtinTypes->unknownType); else { @@ -1247,7 +1247,7 @@ GeneralizationResult generalizeType( if (follow(lb) != freeTy) emplaceType(asMutable(freeTy), lb); - else if (!isWithinFunction || (!FFlag::LuauEagerGeneralization3 && params.useCount == 1)) + else if (!isWithinFunction || (!FFlag::LuauEagerGeneralization4 && params.useCount == 1)) emplaceType(asMutable(freeTy), builtinTypes->unknownType); else { @@ -1353,7 +1353,7 @@ std::optional generalize( FreeTypeSearcher fts{scope, cachedTypes}; fts.traverse(ty); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { FunctionType* functionTy = getMutable(ty); auto pushGeneric = [&](TypeId t) @@ -1597,7 +1597,7 @@ void pruneUnnecessaryGenerics( TypeId ty ) { - if (!FFlag::LuauEagerGeneralization3) + if (!FFlag::LuauEagerGeneralization4) return; ty = follow(ty); diff --git a/Analysis/src/InferPolarity.cpp b/Analysis/src/InferPolarity.cpp index 73518c0d..db845c59 100644 --- a/Analysis/src/InferPolarity.cpp +++ b/Analysis/src/InferPolarity.cpp @@ -5,7 +5,8 @@ #include "Luau/Scope.h" #include "Luau/VisitType.h" -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAGVARIABLE(LuauInferPolarityOfReadWriteProperties) namespace Luau { @@ -48,25 +49,52 @@ struct InferPolarity : TypeVisitor return false; const Polarity p = polarity; - for (const auto& [name, prop] : tt.props) + if (FFlag::LuauInferPolarityOfReadWriteProperties) { - if (prop.isShared()) + for (const auto& [name, prop] : tt.props) { - polarity = Polarity::Mixed; - traverse(prop.type()); + if (prop.isShared()) + { + polarity = Polarity::Mixed; + traverse(*prop.readTy); + continue; + } + + if (prop.readTy) + { + polarity = p; + traverse(*prop.readTy); + } + + if (prop.writeTy) + { + polarity = invert(p); + traverse(*prop.writeTy); + } } - else if (prop.isReadOnly()) + } + else + { + for (const auto& [name, prop] : tt.props) { - polarity = p; - traverse(*prop.readTy); + if (prop.isShared()) + { + polarity = Polarity::Mixed; + traverse(prop.type()); + } + else if (prop.isReadOnly()) + { + polarity = p; + traverse(*prop.readTy); + } + else if (prop.isWriteOnly()) + { + polarity = invert(p); + traverse(*prop.writeTy); + } + else + LUAU_ASSERT(!"Unreachable"); } - else if (prop.isWriteOnly()) - { - polarity = invert(p); - traverse(*prop.writeTy); - } - else - LUAU_ASSERT(!"Unreachable"); } if (tt.indexer) @@ -133,7 +161,7 @@ struct InferPolarity : TypeVisitor template static void inferGenericPolarities_(NotNull arena, NotNull scope, TID ty) { - if (!FFlag::LuauEagerGeneralization3) + if (!FFlag::LuauEagerGeneralization4) return; InferPolarity infer{arena, scope}; diff --git a/Analysis/src/Linter.cpp b/Analysis/src/Linter.cpp index aaeb2283..2bdceebe 100644 --- a/Analysis/src/Linter.cpp +++ b/Analysis/src/Linter.cpp @@ -16,9 +16,6 @@ LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauAttribute) -LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute) - LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) namespace Luau @@ -107,7 +104,7 @@ static void emitWarning(LintContext& context, LintWarning::Code code, const Loca std::string message = vformat(format, args); va_end(args); - LintWarning warning = {code, location, message}; + LintWarning warning = {code, location, std::move(message)}; context.result.push_back(warning); } @@ -3392,8 +3389,6 @@ static void lintComments(LintContext& context, const std::vector& ho static bool hasNativeCommentDirective(const std::vector& hotcomments) { - LUAU_ASSERT(FFlag::LintRedundantNativeAttribute); - for (const HotComment& hc : hotcomments) { if (hc.content.empty() || hc.content[0] == ' ' || hc.content[0] == '\t') @@ -3417,8 +3412,6 @@ struct LintRedundantNativeAttribute : AstVisitor public: LUAU_NOINLINE static void process(LintContext& context) { - LUAU_ASSERT(FFlag::LintRedundantNativeAttribute); - LintRedundantNativeAttribute pass; pass.context = &context; context.root->visit(&pass); @@ -3540,7 +3533,7 @@ std::vector lint( if (context.warningEnabled(LintWarning::Code_ComparisonPrecedence)) LintComparisonPrecedence::process(context); - if (FFlag::LintRedundantNativeAttribute && context.warningEnabled(LintWarning::Code_RedundantNativeAttribute)) + if (context.warningEnabled(LintWarning::Code_RedundantNativeAttribute)) { if (hasNativeCommentDirective(hotcomments)) LintRedundantNativeAttribute::process(context); diff --git a/Analysis/src/Module.cpp b/Analysis/src/Module.cpp index 053e19cf..2758edff 100644 --- a/Analysis/src/Module.cpp +++ b/Analysis/src/Module.cpp @@ -172,7 +172,7 @@ struct ClonePublicInterface : Substitution InternalError{"Free type is escaping its module; please report this bug at " "https://github.com/luau-lang/luau/issues"} ); - result = builtinTypes->errorRecoveryType(); + result = builtinTypes->errorType; } else if (auto genericty = getMutable(result)) { @@ -196,7 +196,7 @@ struct ClonePublicInterface : Substitution InternalError{"Free type pack is escaping its module; please report this bug at " "https://github.com/luau-lang/luau/issues"} ); - clonedTp = builtinTypes->errorRecoveryTypePack(); + clonedTp = builtinTypes->errorTypePack; } else if (auto gtp = getMutable(clonedTp)) gtp->scope = nullptr; @@ -218,7 +218,7 @@ struct ClonePublicInterface : Substitution else { module->errors.push_back(TypeError{module->scopes[0].first, UnificationTooComplex{}}); - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } } @@ -232,7 +232,7 @@ struct ClonePublicInterface : Substitution else { module->errors.push_back(TypeError{module->scopes[0].first, UnificationTooComplex{}}); - return builtinTypes->errorRecoveryTypePack(); + return builtinTypes->errorTypePack; } } @@ -265,7 +265,7 @@ struct ClonePublicInterface : Substitution TypeId type = cloneType(tf.type); - return TypeFun{typeParams, typePackParams, type, tf.definitionLocation}; + return TypeFun{std::move(typeParams), std::move(typePackParams), type, tf.definitionLocation}; } }; diff --git a/Analysis/src/NonStrictTypeChecker.cpp b/Analysis/src/NonStrictTypeChecker.cpp index a650877d..b8316e2b 100644 --- a/Analysis/src/NonStrictTypeChecker.cpp +++ b/Analysis/src/NonStrictTypeChecker.cpp @@ -226,7 +226,7 @@ struct NonStrictTypeChecker return result; } else if (get(pack)) - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; else if (finite(pack) && size(pack) == 0) return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` else @@ -682,7 +682,7 @@ struct NonStrictTypeChecker if (arguments.size() > argTypes.size()) { // We are passing more arguments than we expect, so we should error - reportError(CheckedFunctionIncorrectArgs{functionName, argTypes.size(), arguments.size()}, call->location); + reportError(CheckedFunctionIncorrectArgs{std::move(functionName), argTypes.size(), arguments.size()}, call->location); return fresh; } @@ -729,7 +729,7 @@ struct NonStrictTypeChecker if (!remainingArgsOptional) { - reportError(CheckedFunctionIncorrectArgs{functionName, argTypes.size(), arguments.size()}, call->location); + reportError(CheckedFunctionIncorrectArgs{std::move(functionName), argTypes.size(), arguments.size()}, call->location); return fresh; } } @@ -1009,7 +1009,7 @@ struct NonStrictTypeChecker } symbol += ty->name.value; - reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location); + reportError(UnknownSymbol{std::move(symbol), UnknownSymbol::Context::Type}, ty->location); } } } diff --git a/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index 3a75b43d..8d89a22d 100644 --- a/Analysis/src/Normalize.cpp +++ b/Analysis/src/Normalize.cpp @@ -1363,7 +1363,7 @@ std::optional Normalizer::unionOfTypePacks(TypePackId here, TypePack else if (thereSubHere) return here; if (!head.empty()) - return arena->addTypePack(TypePack{head, tail}); + return arena->addTypePack(TypePack{std::move(head), tail}); else if (tail) return *tail; else @@ -1822,7 +1822,7 @@ std::optional Normalizer::negateNormal(const NormalizedType& her } if (!rootNegations.empty()) - result.externTypes.pushPair(builtinTypes->externType, rootNegations); + result.externTypes.pushPair(builtinTypes->externType, std::move(rootNegations)); } result.nils = get(here.nils) ? builtinTypes->nilType : builtinTypes->neverType; @@ -2375,7 +2375,7 @@ std::optional Normalizer::intersectionOfTypePacks(TypePackId here, T else if (thereSubHere) return there; if (!head.empty()) - return arena->addTypePack(TypePack{head, tail}); + return arena->addTypePack(TypePack{std::move(head), tail}); else if (tail) return *tail; else diff --git a/Analysis/src/OverloadResolution.cpp b/Analysis/src/OverloadResolution.cpp index 00d417b2..cc925a62 100644 --- a/Analysis/src/OverloadResolution.cpp +++ b/Analysis/src/OverloadResolution.cpp @@ -256,7 +256,7 @@ std::pair OverloadResolver::checkOverload_ TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; - return {Analysis::ArityMismatch, {error}}; + return {Analysis::ArityMismatch, {std::move(error)}}; } // If any of the unsatisfied arguments are not supertypes of @@ -274,7 +274,7 @@ std::pair OverloadResolver::checkOverload_ TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; - return {Analysis::ArityMismatch, {error}}; + return {Analysis::ArityMismatch, {std::move(error)}}; } } else @@ -284,7 +284,7 @@ std::pair OverloadResolver::checkOverload_ auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes); TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; - return {Analysis::ArityMismatch, {error}}; + return {Analysis::ArityMismatch, {std::move(error)}}; } } } @@ -305,7 +305,7 @@ std::pair OverloadResolver::checkOverload_ auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes); TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg}}; - return {Analysis::ArityMismatch, {error}}; + return {Analysis::ArityMismatch, {std::move(error)}}; } } } diff --git a/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index 2b9cbe53..b99e2238 100644 --- a/Analysis/src/Substitution.cpp +++ b/Analysis/src/Substitution.cpp @@ -10,6 +10,7 @@ LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256) +LUAU_FASTFLAG(LuauSolverAgnosticClone) namespace Luau { @@ -189,7 +190,7 @@ void Tarjan::visitChildren(TypeId ty, int index) LUAU_ASSERT(!ttv->boundTo); for (const auto& [name, prop] : ttv->props) { - if (FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone) { visitChild(prop.readTy); visitChild(prop.writeTy); @@ -773,7 +774,7 @@ void Substitution::replaceChildren(TypeId ty) LUAU_ASSERT(!ttv->boundTo); for (auto& [name, prop] : ttv->props) { - if (FFlag::LuauSolverV2) + if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone) { if (prop.readTy) prop.readTy = replace(prop.readTy); diff --git a/Analysis/src/Subtyping.cpp b/Analysis/src/Subtyping.cpp index 5fa81dda..3f61fea9 100644 --- a/Analysis/src/Subtyping.cpp +++ b/Analysis/src/Subtyping.cpp @@ -20,7 +20,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity) LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) namespace Luau { @@ -165,13 +165,13 @@ SubtypingResult& SubtypingResult::orElse(const SubtypingResult& other) SubtypingResult& SubtypingResult::withBothComponent(TypePath::Component component) { - return withSubComponent(component).withSuperComponent(component); + return withSubComponent(component).withSuperComponent(std::move(component)); } SubtypingResult& SubtypingResult::withSubComponent(TypePath::Component component) { if (reasoning.empty()) - reasoning.insert(SubtypingReasoning{Path(component), TypePath::kEmpty}); + reasoning.insert(SubtypingReasoning{Path(std::move(component)), TypePath::kEmpty}); else { for (auto& r : reasoning) @@ -184,7 +184,7 @@ SubtypingResult& SubtypingResult::withSubComponent(TypePath::Component component SubtypingResult& SubtypingResult::withSuperComponent(TypePath::Component component) { if (reasoning.empty()) - reasoning.insert(SubtypingReasoning{TypePath::kEmpty, Path(component)}); + reasoning.insert(SubtypingReasoning{TypePath::kEmpty, Path(std::move(component))}); else { for (auto& r : reasoning) @@ -196,13 +196,13 @@ SubtypingResult& SubtypingResult::withSuperComponent(TypePath::Component compone SubtypingResult& SubtypingResult::withBothPath(TypePath::Path path) { - return withSubPath(path).withSuperPath(path); + return withSubPath(path).withSuperPath(std::move(path)); } SubtypingResult& SubtypingResult::withSubPath(TypePath::Path path) { if (reasoning.empty()) - reasoning.insert(SubtypingReasoning{path, TypePath::kEmpty}); + reasoning.insert(SubtypingReasoning{std::move(path), TypePath::kEmpty}); else { for (auto& r : reasoning) @@ -215,7 +215,7 @@ SubtypingResult& SubtypingResult::withSubPath(TypePath::Path path) SubtypingResult& SubtypingResult::withSuperPath(TypePath::Path path) { if (reasoning.empty()) - reasoning.insert(SubtypingReasoning{TypePath::kEmpty, path}); + reasoning.insert(SubtypingReasoning{TypePath::kEmpty, std::move(path)}); else { for (auto& r : reasoning) @@ -680,14 +680,14 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub result.isSubtype = ok; result.isCacheable = false; } - else if (auto pair = get2(subTy, superTy); FFlag::LuauEagerGeneralization3 && pair) + else if (auto pair = get2(subTy, superTy); FFlag::LuauEagerGeneralization4 && pair) { // Any two free types are potentially subtypes of one another because // both of them could be narrowed to never. result = {true}; result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy}); } - else if (auto superFree = get(superTy); superFree && FFlag::LuauEagerGeneralization3) + else if (auto superFree = get(superTy); superFree && FFlag::LuauEagerGeneralization4) { // Given SubTy <: (LB <: SuperTy <: UB) // @@ -702,7 +702,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub if (result.isSubtype) result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy}); } - else if (auto subFree = get(subTy); subFree && FFlag::LuauEagerGeneralization3) + else if (auto subFree = get(subTy); subFree && FFlag::LuauEagerGeneralization4) { // Given (LB <: SubTy <: UB) <: SuperTy // @@ -792,7 +792,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub assertReasoningValid(subTy, superTy, result, builtinTypes); - return cache(env, result, subTy, superTy); + return cache(env, std::move(result), subTy, superTy); } SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp, NotNull scope) @@ -1437,7 +1437,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl { SubtypingResult result{true}; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { if (subTable->props.empty() && !subTable->indexer && subTable->state == TableState::Sealed && superTable->indexer) { @@ -1493,7 +1493,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl { if (subTable->indexer) result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope)); - else if (FFlag::LuauEagerGeneralization3 && subTable->state != TableState::Sealed) + else if (FFlag::LuauEagerGeneralization4 && subTable->state != TableState::Sealed) { // As above, we assume that {| |} <: {T} because the unsealed table // on the left will eventually gain the necessary indexer. diff --git a/Analysis/src/TableLiteralInference.cpp b/Analysis/src/TableLiteralInference.cpp index 8ed7558c..6c26449b 100644 --- a/Analysis/src/TableLiteralInference.cpp +++ b/Analysis/src/TableLiteralInference.cpp @@ -13,7 +13,7 @@ #include "Luau/TypeUtils.h" #include "Luau/Unifier2.h" -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAGVARIABLE(LuauWriteOnlyPropertyMangling) namespace Luau { @@ -195,11 +195,20 @@ TypeId matchLiteralType( Property& prop = it->second; - // If we encounter a duplcate property, we may have already - // set it to be read-only. If that's the case, the only thing - // that will definitely crash is trying to access a write - // only property. - LUAU_ASSERT(!prop.isWriteOnly()); + if (FFlag::LuauWriteOnlyPropertyMangling) + { + // If the property is write-only, do nothing. + if (prop.isWriteOnly()) + continue; + } + else + { + // If we encounter a duplcate property, we may have already + // set it to be read-only. If that's the case, the only thing + // that will definitely crash is trying to access a write + // only property. + LUAU_ASSERT(!prop.isWriteOnly()); + } TypeId propTy = *prop.readTy; auto it2 = expectedTableTy->props.find(keyStr); diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index 6a0b4c58..2f121de4 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -148,7 +148,7 @@ void findCyclicTypes(std::set& cycles, std::set& cycleTPs, T static std::pair> canUseTypeNameInScope(ScopePtr scope, const std::string& name) { - for (ScopePtr curr = scope; curr; curr = curr->parent) + for (ScopePtr curr = std::move(scope); curr; curr = curr->parent) { for (const auto& [importName, nameTable] : curr->importedTypeBindings) { @@ -1957,7 +1957,7 @@ std::string toString(const Constraint& constraint, ToStringOptions& opts) return tos(c.resultType) + " ~ hasIndexer " + tos(c.subjectType) + " " + tos(c.indexType); } else if constexpr (std::is_same_v) - return "assignProp " + tos(c.lhsType) + " " + c.propName + " " + tos(c.rhsType); + return tos(c.propType) + " ~ assignProp " + tos(c.lhsType) + " " + c.propName + " " + tos(c.rhsType); else if constexpr (std::is_same_v) return "assignIndex " + tos(c.lhsType) + " " + tos(c.indexType) + " " + tos(c.rhsType); else if constexpr (std::is_same_v) diff --git a/Analysis/src/Transpiler.cpp b/Analysis/src/Transpiler.cpp index 64b2fb6a..36b2247f 100644 --- a/Analysis/src/Transpiler.cpp +++ b/Analysis/src/Transpiler.cpp @@ -2960,7 +2960,7 @@ TranspileResult transpile(std::string_view source, ParseOptions options, bool wi auto allocator = Allocator{}; auto names = AstNameTable{allocator}; - ParseResult parseResult = Parser::parse(source.data(), source.size(), names, allocator, options); + ParseResult parseResult = Parser::parse(source.data(), source.size(), names, allocator, std::move(options)); if (!parseResult.errors.empty()) { diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index 0f2619f1..d49eee09 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -585,8 +585,8 @@ PendingExpansionType::PendingExpansionType( ) : prefix(prefix) , name(name) - , typeArguments(typeArguments) - , packArguments(packArguments) + , typeArguments(std::move(typeArguments)) + , packArguments(std::move(packArguments)) , index(++nextIndex) { } @@ -619,8 +619,8 @@ FunctionType::FunctionType( bool hasSelf ) : definition(std::move(defn)) - , generics(generics) - , genericPacks(genericPacks) + , generics(std::move(generics)) + , genericPacks(std::move(genericPacks)) , argTypes(argTypes) , retTypes(retTypes) , hasSelf(hasSelf) @@ -637,8 +637,8 @@ FunctionType::FunctionType( bool hasSelf ) : definition(std::move(defn)) - , generics(generics) - , genericPacks(genericPacks) + , generics(std::move(generics)) + , genericPacks(std::move(genericPacks)) , level(level) , argTypes(argTypes) , retTypes(retTypes) @@ -1046,16 +1046,6 @@ BuiltinTypes::~BuiltinTypes() FFlag::DebugLuauFreezeArena.value = prevFlag; } -TypeId BuiltinTypes::errorRecoveryType() const -{ - return errorType; -} - -TypePackId BuiltinTypes::errorRecoveryTypePack() const -{ - return errorTypePack; -} - TypeId BuiltinTypes::errorRecoveryType(TypeId guess) const { return guess; diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 046684cb..732d3525 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -36,7 +36,7 @@ LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks) LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors) LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases) -LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) namespace Luau @@ -598,7 +598,7 @@ TypePackId TypeChecker2::reconstructPack(AstArray exprs, TypeArena& ar } TypePackId tail = lookupPack(exprs.data[exprs.size - 1]); - return arena.addTypePack(TypePack{head, tail}); + return arena.addTypePack(TypePack{std::move(head), tail}); } Scope* TypeChecker2::findInnermostScope(Location location) const @@ -713,7 +713,7 @@ void TypeChecker2::visit(AstStatReturn* ret) { Scope* scope = findInnermostScope(ret->location); TypePackId expectedRetType = scope->returnType; - if (FFlag::LuauTableLiteralSubtypeSpecificCheck) + if (FFlag::LuauTableLiteralSubtypeSpecificCheck2) { if (ret->list.size == 0) { @@ -811,7 +811,7 @@ void TypeChecker2::visit(AstStatLocal* local) TypeId valueType = value ? lookupType(value) : nullptr; if (valueType) { - if (FFlag::LuauTableLiteralSubtypeSpecificCheck) + if (FFlag::LuauTableLiteralSubtypeSpecificCheck2) testPotentialLiteralIsSubtype(value, annotationType); else testIsSubtype(valueType, annotationType, value->location); @@ -960,7 +960,7 @@ void TypeChecker2::visit(AstStatForIn* forInStatement) } // and now we can put everything together to get the actual typepack of the iterators. - TypePackId iteratorPack = arena.addTypePack(valueTypes, iteratorTail); + TypePackId iteratorPack = arena.addTypePack(std::move(valueTypes), iteratorTail); // ... and then expand it out to 3 values (if possible) TypePack iteratorTypes = extendTypePack(arena, builtinTypes, iteratorPack, 3); @@ -1120,7 +1120,7 @@ void TypeChecker2::visit(AstStatForIn* forInStatement) if (const FunctionType* nextFtv = get(*instantiatedNextFn)) { - checkFunction(nextFtv, instantiatedIteratorTypes, true); + checkFunction(nextFtv, std::move(instantiatedIteratorTypes), true); } else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *instantiatedNextFn)) { @@ -1224,13 +1224,20 @@ void TypeChecker2::visit(AstStatAssign* assign) continue; } - if (FFlag::LuauTableLiteralSubtypeSpecificCheck) + if (FFlag::LuauTableLiteralSubtypeSpecificCheck2) { - // If rhsType bindingType = getBindingType(lhs)) - testPotentialLiteralIsSubtype(rhs, *bindingType); + testLiteralOrAstTypeIsSubtype(rhs, *bindingType); } } else @@ -2137,7 +2144,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) auto name = getIdentifierOfBaseVar(expr->left); reportError( CannotInferBinaryOperation{ - expr->op, name, isComparison ? CannotInferBinaryOperation::OpKind::Comparison : CannotInferBinaryOperation::OpKind::Operation + expr->op, std::move(name), isComparison ? CannotInferBinaryOperation::OpKind::Comparison : CannotInferBinaryOperation::OpKind::Operation }, expr->location ); @@ -2197,7 +2204,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) expr->location ); - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } std::optional mm; @@ -2280,7 +2287,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) reportError(GenericError{format("Metamethod '%s' must return a value", it->second)}, expr->location); } - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } } else @@ -2288,7 +2295,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) reportError(CannotCallNonFunction{*mm}, expr->location); } - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } // If this is a string comparison, or a concatenation of strings, we // want to fall through to primitive behavior. @@ -2323,7 +2330,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) ); } - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } else if (!leftMt && !rightMt && (get(leftType) || get(rightType))) { @@ -2352,7 +2359,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) ); } - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } } } @@ -2410,7 +2417,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) )}, expr->location ); - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } case AstExprBinary::Op::And: @@ -2423,7 +2430,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey) default: // Unhandled AstExprBinary::Op possibility. LUAU_ASSERT(false); - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } } @@ -2509,7 +2516,7 @@ TypeId TypeChecker2::flattenPack(TypePackId pack) return result; } else if (get(pack)) - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; else if (finite(pack) && size(pack) == 0) return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` else @@ -2721,7 +2728,7 @@ void TypeChecker2::visit(AstTypeReference* ty) } symbol += ty->name.value; - reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location); + reportError(UnknownSymbol{std::move(symbol), UnknownSymbol::Context::Type}, ty->location); } } } @@ -2967,6 +2974,17 @@ void TypeChecker2::explainError(TypePackId subTy, TypePackId superTy, Location l reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location); } +bool TypeChecker2::testLiteralOrAstTypeIsSubtype(AstExpr* expr, TypeId expectedType) +{ + NotNull scope{findInnermostScope(expr->location)}; + auto exprTy = lookupType(expr); + SubtypingResult r = subtyping->isSubtype(exprTy, expectedType, scope); + if (r.isSubtype) + return true; + + return testPotentialLiteralIsSubtype(expr, expectedType); +} + bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedType) { auto exprType = follow(lookupType(expr)); @@ -3298,7 +3316,7 @@ PropertyTypes TypeChecker2::lookupProp( } } - return {typesOfProp, typesMissingTheProp}; + return {std::move(typesOfProp), std::move(typesMissingTheProp)}; } @@ -3445,9 +3463,9 @@ PropertyType TypeChecker2::hasIndexTypeFromType( TypeId propTy; if (context == ValueContext::LValue) - propTy = module->internalTypes.addType(IntersectionType{parts}); + propTy = module->internalTypes.addType(IntersectionType{std::move(parts)}); else - propTy = module->internalTypes.addType(UnionType{parts}); + propTy = module->internalTypes.addType(UnionType{std::move(parts)}); return {NormalizationResult::True, propTy}; } @@ -3501,7 +3519,7 @@ void TypeChecker2::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& } if (!candidates.empty()) - data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, candidates}); + data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, std::move(candidates)}); } bool TypeChecker2::isErrorSuppressing(Location loc, TypeId ty) diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index bbfeed9d..b7b5f52f 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -48,14 +48,15 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0 LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1); LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauEagerGeneralization3) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies) LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers) LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults) LUAU_FASTFLAG(LuauUserTypeFunctionAliases) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) +LUAU_FASTFLAGVARIABLE(LuauOccursCheckForRefinement) namespace Luau { @@ -264,7 +265,7 @@ struct TypeFunctionReducer Okay, }; - SkipTestResult testForSkippability(TypeId ty) + SkipTestResult DEPRECATED_testForSkippability(TypeId ty) { ty = follow(ty); @@ -283,7 +284,7 @@ struct TypeFunctionReducer } else if (is(ty)) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) return SkipTestResult::Generic; else return SkipTestResult::Irreducible; @@ -292,6 +293,51 @@ struct TypeFunctionReducer return SkipTestResult::Okay; } + SkipTestResult testForSkippability(TypeId ty) + { + if (!FFlag::LuauEagerGeneralization4) + return DEPRECATED_testForSkippability(ty); + + VecDeque queue; + DenseHashSet seen{nullptr}; + + queue.push_back(follow(ty)); + + while (!queue.empty()) + { + TypeId t = queue.front(); + queue.pop_front(); + + if (seen.contains(t)) + continue; + + if (is(t)) + { + for (auto cyclicTy : cyclicTypeFunctions) + { + if (t == cyclicTy) + return SkipTestResult::CyclicTypeFunction; + } + + if (!irreducible.contains(t)) + return SkipTestResult::Defer; + + return SkipTestResult::Irreducible; + } + else if (is(t)) + return SkipTestResult::Generic; + else if (auto it = get(t)) + { + for (TypeId part : it->parts) + queue.push_back(follow(part)); + } + + seen.insert(t); + } + + return SkipTestResult::Okay; + } + SkipTestResult testForSkippability(TypePackId ty) const { ty = follow(ty); @@ -305,7 +351,7 @@ struct TypeFunctionReducer } else if (is(ty)) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) return SkipTestResult::Generic; else return SkipTestResult::Irreducible; @@ -517,7 +563,7 @@ struct TypeFunctionReducer ctx.userFuncName = tfit->userFuncName; TypeFunctionReductionResult result = tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); - handleTypeFunctionReduction(subject, result); + handleTypeFunctionReduction(subject, std::move(result)); } } @@ -542,7 +588,7 @@ struct TypeFunctionReducer TypeFunctionReductionResult result = tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); - handleTypeFunctionReduction(subject, result); + handleTypeFunctionReduction(subject, std::move(result)); } } @@ -752,7 +798,7 @@ static std::optional> tryDistributeTypeFunct } if (reductionStatus != Reduction::MaybeOk || !blockedTypes.empty()) - return {{std::nullopt, reductionStatus, blockedTypes, {}}}; + return {{std::nullopt, reductionStatus, std::move(blockedTypes), {}}}; if (!results.empty()) { @@ -924,7 +970,7 @@ TypeFunctionReductionResult userDefinedTypeFunction( // If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones if (!ctx->typeFunctionRuntime->allowEvaluation || typeFunction->userFuncData.definition->hasErrors) - return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; + return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}}; FindUserTypeFunctionBlockers check{ctx}; @@ -949,7 +995,7 @@ TypeFunctionReductionResult userDefinedTypeFunction( { // Cannot evaluate if a potential dependency couldn't be parsed if (definition.first->hasErrors) - return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; + return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}}; if (std::optional error = ctx->typeFunctionRuntime->registerFunction(definition.first)) { @@ -1090,7 +1136,7 @@ TypeFunctionReductionResult userDefinedTypeFunction( ctx->typeFunctionRuntime->messages.clear(); if (auto error = checkResultForError(L, name.value, lua_pcall(L, int(typeParams.size()), 1, 0))) - return {std::nullopt, Reduction::Erroneous, {}, {}, error, ctx->typeFunctionRuntime->messages}; + return {std::nullopt, Reduction::Erroneous, {}, {}, std::move(error), ctx->typeFunctionRuntime->messages}; // If the return value is not a type userdata, return with error message if (!isTypeUserData(L, 1)) @@ -1222,7 +1268,7 @@ TypeFunctionReductionResult lenTypeFunction( const FunctionType* instantiatedMmFtv = get(*instantiatedMmType); if (!instantiatedMmFtv) - return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; + return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}}; TypePackId inferredArgPack = ctx->arena->addTypePack({operandTy}); Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice}; @@ -1259,7 +1305,7 @@ TypeFunctionReductionResult unmTypeFunction( if (isPending(operandTy, ctx->solver)) return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}}; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) operandTy = follow(operandTy); std::shared_ptr normTy = ctx->normalizer->normalize(operandTy); @@ -1305,7 +1351,7 @@ TypeFunctionReductionResult unmTypeFunction( const FunctionType* instantiatedMmFtv = get(*instantiatedMmType); if (!instantiatedMmFtv) - return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; + return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}}; TypePackId inferredArgPack = ctx->arena->addTypePack({operandTy}); Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice}; @@ -1772,7 +1818,7 @@ TypeFunctionReductionResult concatTypeFunction( const FunctionType* instantiatedMmFtv = get(*instantiatedMmType); if (!instantiatedMmFtv) - return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; + return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}}; std::vector inferredArgs; if (!reversed) @@ -1856,7 +1902,7 @@ TypeFunctionReductionResult orTypeFunction( return {rhsTy, Reduction::MaybeOk, {}, {}}; // check to see if both operand types are resolved enough, and wait to reduce if not - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { if (is(lhsTy)) return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; @@ -1903,7 +1949,7 @@ static TypeFunctionReductionResult comparisonTypeFunction( if (lhsTy == instance || rhsTy == instance) return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}}; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { if (is(lhsTy)) return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; @@ -1997,7 +2043,7 @@ static TypeFunctionReductionResult comparisonTypeFunction( const FunctionType* instantiatedMmFtv = get(*instantiatedMmType); if (!instantiatedMmFtv) - return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; + return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}}; TypePackId inferredArgPack = ctx->arena->addTypePack({lhsTy, rhsTy}); Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice}; @@ -2126,7 +2172,7 @@ TypeFunctionReductionResult eqTypeFunction( const FunctionType* instantiatedMmFtv = get(*instantiatedMmType); if (!instantiatedMmFtv) - return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; + return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}}; TypePackId inferredArgPack = ctx->arena->addTypePack({lhsTy, rhsTy}); Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice}; @@ -2247,6 +2293,98 @@ bool isSimpleDiscriminant(TypeId ty) return isApproximateTruthy(ty) || isApproximateFalsy(ty); } +struct RefineTypeScrubber : public Substitution +{ + NotNull ctx; + TypeId needle; + + explicit RefineTypeScrubber(NotNull ctx, TypeId needle) + : Substitution(ctx->arena) + , ctx{ctx} + , needle{needle} + { + } + + bool isDirty(TypePackId tp) override + { + return false; + } + + bool ignoreChildren(TypePackId tp) override + { + return false; + } + + TypePackId clean(TypePackId tp) override + { + return tp; + } + + bool isDirty(TypeId ty) override + { + if (auto ut = get(ty)) + { + for (auto option : ut) + { + if (option == needle) + return true; + } + } + else if (auto it = get(ty)) + { + for (auto part : it) + { + if (part == needle) + return true; + } + } + return false; + } + + bool ignoreChildren(TypeId ty) override + { + return !is(ty); + } + + TypeId clean(TypeId ty) override + { + // NOTE: this feels pretty similar to other places where we try to + // filter over a set type, may be worth combining those in the future. + if (auto ut = get(ty)) + { + TypeIds newOptions; + for (auto option : ut) + { + if (option != needle && !is(option)) + newOptions.insert(option); + } + if (newOptions.empty()) + return ctx->builtins->neverType; + else if (newOptions.size() == 1) + return *newOptions.begin(); + else + return ctx->arena->addType(UnionType{newOptions.take()}); + } + else if (auto it = get(ty)) + { + TypeIds newParts; + for (auto part : it) + { + if (part != needle && !is(part)) + newParts.insert(part); + } + if (newParts.empty()) + return ctx->builtins->unknownType; + else if (newParts.size() == 1) + return *newParts.begin(); + else + return ctx->arena->addType(IntersectionType{newParts.take()}); + } + return ty; + } + +}; + } // namespace TypeFunctionReductionResult refineTypeFunction( @@ -2263,11 +2401,30 @@ TypeFunctionReductionResult refineTypeFunction( } TypeId targetTy = follow(typeParams.at(0)); + + if (FFlag::LuauOccursCheckForRefinement) + { + // If we end up minting a refine type like: + // + // t1 where t1 = refine + // + // This can create a degenerate set type such as: + // + // t1 where t1 = (T | t1) & Y + // + // Instead, we can clip the recursive part: + // + // t1 where t1 = refine => refine + RefineTypeScrubber rts{ctx, instance}; + if (auto result = rts.substitute(targetTy)) + targetTy = *result; + } + std::vector discriminantTypes; for (size_t i = 1; i < typeParams.size(); i++) discriminantTypes.push_back(follow(typeParams.at(i))); - const bool targetIsPending = FFlag::LuauEagerGeneralization3 ? is(targetTy) + const bool targetIsPending = FFlag::LuauEagerGeneralization4 ? is(targetTy) : isPending(targetTy, ctx->solver); // check to see if both operand types are resolved enough, and wait to reduce if not @@ -2348,7 +2505,7 @@ TypeFunctionReductionResult refineTypeFunction( if (is(target) || isSimpleDiscriminant(discriminant)) { SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // Simplification considers free and generic types to be // 'blocking', but that's not suitable for refine<>. @@ -2844,7 +3001,7 @@ TypeFunctionReductionResult keyofFunctionImpl( if (singletons.size() == 1) return {singletons.front(), Reduction::MaybeOk, {}, {}}; - return {ctx->arena->addType(UnionType{singletons}), Reduction::MaybeOk, {}, {}}; + return {ctx->arena->addType(UnionType{std::move(singletons)}), Reduction::MaybeOk, {}, {}}; } TypeFunctionReductionResult keyofTypeFunction( @@ -3289,7 +3446,7 @@ TypeFunctionReductionResult setmetatableTypeFunction( blockedTypes.reserve(simplified.blockedTypes.size()); for (auto ty : simplified.blockedTypes) blockedTypes.push_back(ty); - return {std::nullopt, Reduction::MaybeOk, blockedTypes, {}}; + return {std::nullopt, Reduction::MaybeOk, std::move(blockedTypes), {}}; } result = simplified.result; @@ -3489,7 +3646,7 @@ BuiltinTypeFunctions::BuiltinTypeFunctions() , ltFunc{"lt", ltTypeFunction} , leFunc{"le", leTypeFunction} , eqFunc{"eq", eqTypeFunction} - , refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization3} + , refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization4} , singletonFunc{"singleton", singletonTypeFunction} , unionFunc{"union", unionTypeFunction} , intersectFunc{"intersect", intersectTypeFunction} diff --git a/Analysis/src/TypeFunctionReductionGuesser.cpp b/Analysis/src/TypeFunctionReductionGuesser.cpp index 59b94125..c00a8619 100644 --- a/Analysis/src/TypeFunctionReductionGuesser.cpp +++ b/Analysis/src/TypeFunctionReductionGuesser.cpp @@ -125,7 +125,7 @@ std::optional TypeFunctionReductionGuesser::guess(TypePackId tp) guessedHead.push_back(*guessedType); } - return arena->addTypePack(TypePack{guessedHead, tail}); + return arena->addTypePack(TypePack{std::move(guessedHead), tail}); } TypeFunctionReductionGuessResult TypeFunctionReductionGuesser::guessTypeFunctionReductionForFunctionExpr( @@ -182,7 +182,7 @@ TypeFunctionReductionGuessResult TypeFunctionReductionGuesser::guessTypeFunction functionReducesTo.clear(); substitutable.clear(); - return TypeFunctionReductionGuessResult{results, recommendedAnnotation}; + return TypeFunctionReductionGuessResult{std::move(results), recommendedAnnotation}; } std::optional TypeFunctionReductionGuesser::guessType(TypeId arg) diff --git a/Analysis/src/TypeFunctionRuntime.cpp b/Analysis/src/TypeFunctionRuntime.cpp index d5849aaa..254d5916 100644 --- a/Analysis/src/TypeFunctionRuntime.cpp +++ b/Analysis/src/TypeFunctionRuntime.cpp @@ -342,7 +342,7 @@ static int createOptional(lua_State* L) components.emplace_back(allocateTypeFunctionType(L, TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::NilType))); - allocTypeUserData(L, TypeFunctionUnionType{components}); + allocTypeUserData(L, TypeFunctionUnionType{std::move(components)}); return 1; } @@ -362,7 +362,7 @@ static int createUnion(lua_State* L) for (int i = 1; i <= argSize; i++) components.push_back(getTypeUserData(L, i)); - allocTypeUserData(L, TypeFunctionUnionType{components}); + allocTypeUserData(L, TypeFunctionUnionType{std::move(components)}); return 1; } @@ -382,7 +382,7 @@ static int createIntersection(lua_State* L) for (int i = 1; i <= argSize; i++) components.push_back(getTypeUserData(L, i)); - allocTypeUserData(L, TypeFunctionIntersectionType{components}); + allocTypeUserData(L, TypeFunctionIntersectionType{std::move(components)}); return 1; } @@ -542,7 +542,7 @@ static int createTable(lua_State* L) if (metatable && !get(*metatable)) luaL_error(L, "types.newtable: expected to be given a table type as a metatable, but got %s instead", getTag(L, *metatable).c_str()); - allocTypeUserData(L, TypeFunctionTableType{props, indexer, metatable}); + allocTypeUserData(L, TypeFunctionTableType{std::move(props), indexer, metatable}); return 1; } @@ -904,7 +904,7 @@ static TypeFunctionTypePackId getTypePack(lua_State* L, int headIdx, int tailIdx if (head.size() == 0 && tail.has_value()) result = *tail; else - result = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{head, tail}); + result = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{std::move(head), tail}); return result; } diff --git a/Analysis/src/TypeFunctionRuntimeBuilder.cpp b/Analysis/src/TypeFunctionRuntimeBuilder.cpp index a1edc3d9..9334e63b 100644 --- a/Analysis/src/TypeFunctionRuntimeBuilder.cpp +++ b/Analysis/src/TypeFunctionRuntimeBuilder.cpp @@ -218,7 +218,7 @@ private: if (!g->explicitName) name = format("g%d", g->index); - target = typeFunctionRuntime->typeArena.allocate(TypeFunctionGenericType{g->explicitName, false, name}); + target = typeFunctionRuntime->typeArena.allocate(TypeFunctionGenericType{g->explicitName, false, std::move(name)}); } else { @@ -251,7 +251,7 @@ private: if (!gPack->explicitName) name = format("g%d", gPack->index); - target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionGenericTypePack{gPack->explicitName, name}); + target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionGenericTypePack{gPack->explicitName, std::move(name)}); } else { @@ -527,12 +527,12 @@ public: if (hasExceededIterationLimit() || state->errors.size() != 0) { - TypeId error = state->ctx->builtins->errorRecoveryType(); + TypeId error = state->ctx->builtins->errorType; types[ty] = error; return error; } - return find(ty).value_or(state->ctx->builtins->errorRecoveryType()); + return find(ty).value_or(state->ctx->builtins->errorType); } TypePackId deserialize(TypeFunctionTypePackId tp) @@ -542,12 +542,12 @@ public: if (hasExceededIterationLimit() || state->errors.size() != 0) { - TypePackId error = state->ctx->builtins->errorRecoveryTypePack(); + TypePackId error = state->ctx->builtins->errorTypePack; packs[tp] = error; return error; } - return find(tp).value_or(state->ctx->builtins->errorRecoveryTypePack()); + return find(tp).value_or(state->ctx->builtins->errorTypePack); } private: diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 76990b6c..544a6df5 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -232,7 +232,7 @@ ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optiona { try { - return checkWithoutRecursionCheck(module, mode, environmentScope); + return checkWithoutRecursionCheck(module, mode, std::move(environmentScope)); } catch (const RecursionLimitException&) { @@ -688,7 +688,7 @@ static std::optional tryGetTypeGuardPredicate(const AstExprBinary& ex if (!lvalue) return std::nullopt; - Predicate predicate{TypeGuardPredicate{std::move(*lvalue), expr.location, ssval, isTypeof}}; + Predicate predicate{TypeGuardPredicate{std::move(*lvalue), expr.location, std::move(ssval), isTypeof}}; if (expr.op == AstExprBinary::Op::CompareNe) return NotPredicate{{std::move(predicate)}}; @@ -1372,7 +1372,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin) varTypes.front() = *fty; } - TypePackId varPack = addTypePack(TypePackVar{TypePack{varTypes, freshTypePack(scope)}}); + TypePackId varPack = addTypePack(TypePackVar{TypePack{std::move(varTypes), freshTypePack(scope)}}); unify(retPack, varPack, scope, forin.location); @@ -1681,7 +1681,7 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareExternTyp if (!lookupType) { - reportError(declaredExternType.location, UnknownSymbol{superName, UnknownSymbol::Type}); + reportError(declaredExternType.location, UnknownSymbol{std::move(superName), UnknownSymbol::Type}); incorrectExternTypeDefinitions.insert(&declaredExternType); return; } @@ -2166,7 +2166,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( if (goodOptions.empty()) reportError(location, UnknownProperty{type, name}); else - reportError(location, MissingUnionProperty{type, badOptions, name}); + reportError(location, MissingUnionProperty{type, std::move(badOptions), name}); } return std::nullopt; } @@ -2615,7 +2615,7 @@ TypeId TypeChecker::unionOfTypes(TypeId a, TypeId b, const ScopePtr& scope, cons if (types.size() == 1) return types[0]; - return addType(UnionType{types}); + return addType(UnionType{std::move(types)}); } static std::optional getIdentifierOfBaseVar(AstExpr* node) @@ -2923,7 +2923,7 @@ TypeId TypeChecker::checkRelationalOperation( if (get(follow(lhsType)) && !isEquality) { auto name = getIdentifierOfBaseVar(expr.left); - reportError(expr.location, CannotInferBinaryOperation{expr.op, name, CannotInferBinaryOperation::Comparison}); + reportError(expr.location, CannotInferBinaryOperation{expr.op, std::move(name), CannotInferBinaryOperation::Comparison}); return errorRecoveryType(booleanType); } @@ -3032,7 +3032,7 @@ TypeId TypeChecker::checkBinaryOperation( if (!isNonstrictMode() && get(lhsType)) { auto name = getIdentifierOfBaseVar(expr.left); - reportError(expr.location, CannotInferBinaryOperation{expr.op, name, CannotInferBinaryOperation::Operation}); + reportError(expr.location, CannotInferBinaryOperation{expr.op, std::move(name), CannotInferBinaryOperation::Operation}); // We will fall-through to the `return anyType` check below. } @@ -3102,7 +3102,7 @@ TypeId TypeChecker::checkBinaryOperation( std::string op = opToMetaTableEntry(expr.op); if (auto fnt = findMetatableEntry(lhsType, op, expr.location, /* addErrors= */ true)) return checkMetatableCall(*fnt, lhsType, rhsType); - if (auto fnt = findMetatableEntry(rhsType, op, expr.location, /* addErrors= */ true)) + if (auto fnt = findMetatableEntry(rhsType, std::move(op), expr.location, /* addErrors= */ true)) { // Note the intentionally reversed arguments here. return checkMetatableCall(*fnt, rhsType, lhsType); @@ -3382,7 +3382,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprGloba // If we're in strict mode, we want to report defining a global as an error, // but still add it to the bindings, so that autocomplete includes it in completions. if (!isNonstrictMode()) - reportError(TypeError{expr.location, UnknownSymbol{name, UnknownSymbol::Binding}}); + reportError(TypeError{expr.location, UnknownSymbol{std::move(name), UnknownSymbol::Binding}}); return result; } @@ -3426,7 +3426,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex if (!state.errors.empty()) { - reportError(expr.location, UnknownProperty{lhs, name}); + reportError(expr.location, UnknownProperty{lhs, std::move(name)}); retType = errorRecoveryType(retType); } else @@ -3436,7 +3436,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex } else if (lhsTable->state == TableState::Sealed) { - reportError(TypeError{expr.location, CannotExtendTable{lhs, CannotExtendTable::Property, name}}); + reportError(TypeError{expr.location, CannotExtendTable{lhs, CannotExtendTable::Property, std::move(name)}}); return errorRecoveryType(scope); } else @@ -3463,7 +3463,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex } } - reportError(TypeError{expr.location, UnknownProperty{lhs, name}}); + reportError(TypeError{expr.location, UnknownProperty{lhs, std::move(name)}}); return errorRecoveryType(scope); } else if (get(lhs)) @@ -3474,7 +3474,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex // If intersection has a table part, report that it cannot be extended just as a sealed table if (isTableIntersection(lhs)) { - reportError(TypeError{expr.location, CannotExtendTable{lhs, CannotExtendTable::Property, name}}); + reportError(TypeError{expr.location, CannotExtendTable{lhs, CannotExtendTable::Property, std::move(name)}}); return errorRecoveryType(scope); } } @@ -3530,7 +3530,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex // If intersection has a table part, report that it cannot be extended just as a sealed table if (isTableIntersection(exprType)) { - reportError(TypeError{expr.location, CannotExtendTable{exprType, CannotExtendTable::Property, name}}); + reportError(TypeError{expr.location, CannotExtendTable{exprType, CannotExtendTable::Property, std::move(name)}}); return errorRecoveryType(scope); } } @@ -3645,7 +3645,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex if (options.size() == 1) return options[0]; - return addType(UnionType{options}); + return addType(UnionType{std::move(options)}); } return addType(IntersectionType{{propTypes.begin(), propTypes.end()}}); @@ -3700,7 +3700,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex if (options.size() == 1) return options[0]; - return addType(UnionType{options}); + return addType(UnionType{std::move(options)}); } return addType(IntersectionType{{resultTypes.begin(), resultTypes.end()}}); @@ -3941,7 +3941,7 @@ std::pair TypeChecker::checkFunctionSignature( ++expectedArgsCurr; } - TypePackId argPack = addTypePack(TypePackVar(TypePack{argTypes, funScope->varargPack})); + TypePackId argPack = addTypePack(TypePackVar(TypePack{std::move(argTypes), funScope->varargPack})); FunctionDefinition defn; defn.definitionModuleName = currentModule->name; @@ -4129,7 +4129,7 @@ void TypeChecker::checkArgumentList( auto [minParams, optMaxParams] = getParameterExtents(&state.log, paramPack); state.reportError(TypeError{ location, - CountMismatch{minParams, optMaxParams, std::distance(begin(argPack), end(argPack)), CountMismatch::Context::Arg, false, namePath} + CountMismatch{minParams, optMaxParams, std::distance(begin(argPack), end(argPack)), CountMismatch::Context::Arg, false, std::move(namePath)} }); }; @@ -4219,7 +4219,7 @@ void TypeChecker::checkArgumentList( return; } - TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}}); + TypePackId varPack = addTypePack(TypePackVar{TypePack{std::move(rest), paramIter.tail()}}); state.tryUnify(tail, varPack); return; } @@ -4248,7 +4248,7 @@ void TypeChecker::checkArgumentList( namePath = *path; state.reportError(TypeError{ - funName.location, CountMismatch{minParams, optMaxParams, paramIndex, CountMismatch::Context::Arg, isVariadic, namePath} + funName.location, CountMismatch{minParams, optMaxParams, paramIndex, CountMismatch::Context::Arg, isVariadic, std::move(namePath)} }); return; } @@ -4325,7 +4325,7 @@ void TypeChecker::checkArgumentList( return; } - TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}}); + TypePackId varPack = addTypePack(TypePackVar{TypePack{std::move(rest), argIter.tail()}}); state.tryUnify(varPack, tail); return; @@ -4950,7 +4950,7 @@ WithPredicate TypeChecker::checkExprList( if (uninhabitable) return WithPredicate{uninhabitableTypePack}; - return {pack, predicates}; + return {pack, std::move(predicates)}; } std::optional TypeChecker::matchRequire(const AstExprCall& call) @@ -5314,7 +5314,7 @@ void TypeChecker::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& d } if (!candidates.empty()) - data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, candidates}); + data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, std::move(candidates)}); } LUAU_NOINLINE void TypeChecker::reportErrorCodeTooComplex(const Location& location) @@ -5404,7 +5404,7 @@ TypeId TypeChecker::singletonType(std::string value) TypeId TypeChecker::errorRecoveryType(const ScopePtr& scope) { - return builtinTypes->errorRecoveryType(); + return builtinTypes->errorType; } TypeId TypeChecker::errorRecoveryType(TypeId guess) @@ -5414,7 +5414,7 @@ TypeId TypeChecker::errorRecoveryType(TypeId guess) TypePackId TypeChecker::errorRecoveryTypePack(const ScopePtr& scope) { - return builtinTypes->errorRecoveryTypePack(); + return builtinTypes->errorTypePack; } TypePackId TypeChecker::errorRecoveryTypePack(TypePackId guess) @@ -5449,7 +5449,7 @@ TypeIdPredicate TypeChecker::mkTruthyPredicate(bool sense, TypeId emptySetTy) std::optional TypeChecker::filterMapImpl(TypeId type, TypeIdPredicate predicate) { - std::vector types = Luau::filterMap(type, predicate); + std::vector types = Luau::filterMap(type, std::move(predicate)); if (!types.empty()) return types.size() == 1 ? types[0] : addType(UnionType{std::move(types)}); return std::nullopt; @@ -5457,7 +5457,7 @@ std::optional TypeChecker::filterMapImpl(TypeId type, TypeIdPredicate pr std::pair, bool> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate) { - TypeId ty = filterMapImpl(type, predicate).value_or(neverType); + TypeId ty = filterMapImpl(type, std::move(predicate)).value_or(neverType); return {ty, !bool(get(ty))}; } @@ -5556,9 +5556,9 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno typeName += lit->name.value; if (scope->lookupPack(typeName)) - reportError(TypeError{annotation.location, SwappedGenericTypeParameter{typeName, SwappedGenericTypeParameter::Type}}); + reportError(TypeError{annotation.location, SwappedGenericTypeParameter{std::move(typeName), SwappedGenericTypeParameter::Type}}); else - reportError(TypeError{annotation.location, UnknownSymbol{typeName, UnknownSymbol::Type}}); + reportError(TypeError{annotation.location, UnknownSymbol{std::move(typeName), UnknownSymbol::Type}}); return errorRecoveryType(scope); } @@ -5844,7 +5844,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno for (AstType* ann : un->types) types.push_back(resolveType(scope, *ann)); - return addType(UnionType{types}); + return addType(UnionType{std::move(types)}); } else if (const auto& un = annotation.as()) { @@ -5854,7 +5854,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno for (AstType* ann : un->types) types.push_back(resolveType(scope, *ann)); - return addType(IntersectionType{types}); + return addType(IntersectionType{std::move(types)}); } else if (const auto& g = annotation.as()) { @@ -5890,7 +5890,7 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypeList head.push_back(resolveType(scope, *ann)); std::optional tail = types.tailType ? std::optional(resolveTypePack(scope, *types.tailType)) : std::nullopt; - return addTypePack(TypePack{head, tail}); + return addTypePack(TypePack{std::move(head), tail}); } return addTypePack(TypePack{}); @@ -5911,9 +5911,9 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypePack if (!genericTy) { if (scope->lookupType(genericName)) - reportError(TypeError{generic->location, SwappedGenericTypeParameter{genericName, SwappedGenericTypeParameter::Pack}}); + reportError(TypeError{generic->location, SwappedGenericTypeParameter{std::move(genericName), SwappedGenericTypeParameter::Pack}}); else - reportError(TypeError{generic->location, UnknownSymbol{genericName, UnknownSymbol::Type}}); + reportError(TypeError{generic->location, UnknownSymbol{std::move(genericName), UnknownSymbol::Type}}); result = errorRecoveryTypePack(scope); } @@ -6089,7 +6089,7 @@ GenericTypeDefinitions TypeChecker::createGenericTypes( scope->privateTypePackBindings[n] = cached; } - return {generics, genericPacks}; + return {std::move(generics), std::move(genericPacks)}; } void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const ScopePtr& scope, TypeIdPredicate predicate) @@ -6117,7 +6117,7 @@ void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const // If we do not have a key, it means we're not trying to discriminate anything, so it's a simple matter of just filtering for a subset. if (!key) { - auto [result, ok] = filterMap(*ty, predicate); + auto [result, ok] = filterMap(*ty, std::move(predicate)); addRefinement(refis, *target, *result); return; } @@ -6416,7 +6416,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r return std::nullopt; }; - refineLValue(lvalue, refis, scope, predicate); + refineLValue(lvalue, refis, scope, std::move(predicate)); }; // Note: "vector" never happens here at this point, so we don't have to write something for it. diff --git a/Analysis/src/TypeUtils.cpp b/Analysis/src/TypeUtils.cpp index be3a002f..1fcca83b 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/Analysis/src/TypeUtils.cpp @@ -12,7 +12,7 @@ #include LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs) namespace Luau @@ -306,7 +306,7 @@ TypePack extendTypePack( TypePack newPack; newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) trackInteriorFreeTypePack(ftp->scope, *newPack.tail); if (FFlag::LuauSolverV2) @@ -343,7 +343,7 @@ TypePack extendTypePack( else if (auto etp = getMutable(pack)) { while (result.head.size() < length) - result.head.push_back(builtinTypes->errorRecoveryType()); + result.head.push_back(builtinTypes->errorType); result.tail = pack; return result; @@ -588,7 +588,7 @@ void trackInteriorFreeType(Scope* scope, TypeId ty) void trackInteriorFreeTypePack(Scope* scope, TypePackId tp) { LUAU_ASSERT(tp); - if (!FFlag::LuauEagerGeneralization3) + if (!FFlag::LuauEagerGeneralization4) return; for (; scope; scope = scope->parent.get()) diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index d84c4ca8..36d5765c 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -1078,33 +1078,33 @@ void Unifier::tryUnifyNormalizedTypes( return; if (get(subNorm.tops)) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); if (get(subNorm.booleans)) { if (!get(superNorm.booleans)) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); } else if (const SingletonType* stv = get(subNorm.booleans)) { if (!get(superNorm.booleans) && stv != get(superNorm.booleans)) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); } if (get(subNorm.nils)) if (!get(superNorm.nils)) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); if (get(subNorm.numbers)) if (!get(superNorm.numbers)) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); if (!isSubtype(subNorm.strings, superNorm.strings)) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); if (get(subNorm.threads)) if (!get(superNorm.errors)) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); for (const auto& [subExternType, _] : subNorm.externTypes.externTypes) { @@ -1140,7 +1140,7 @@ void Unifier::tryUnifyNormalizedTypes( if (!found) { - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); } } @@ -1169,19 +1169,19 @@ void Unifier::tryUnifyNormalizedTypes( return reportError(*e); } if (!found) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); } if (!subNorm.functions.isNever()) { if (superNorm.functions.isNever()) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); for (TypeId superFun : superNorm.functions.parts) { std::unique_ptr innerState = makeChildUnifier(); const FunctionType* superFtv = get(superFun); if (!superFtv) - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); TypePackId tgt = innerState->tryApplyOverloadedFunction(subTy, subNorm.functions, superFtv->argTypes); innerState->tryUnify_(tgt, superFtv->retTypes); if (innerState->errors.empty()) @@ -1189,7 +1189,7 @@ void Unifier::tryUnifyNormalizedTypes( else if (auto e = hasUnificationTooComplex(innerState->errors)) return reportError(*e); else - return reportError(location, TypeMismatch{superTy, subTy, reason, error, mismatchContext()}); + return reportError(location, TypeMismatch{superTy, subTy, std::move(reason), std::move(error), mismatchContext()}); } } @@ -1210,7 +1210,7 @@ TypePackId Unifier::tryApplyOverloadedFunction(TypeId function, const Normalized if (overloads.isNever()) { reportError(location, CannotCallNonFunction{function}); - return builtinTypes->errorRecoveryTypePack(); + return builtinTypes->errorTypePack; } std::optional result; @@ -1266,7 +1266,7 @@ TypePackId Unifier::tryApplyOverloadedFunction(TypeId function, const Normalized else { reportError(location, CannotCallNonFunction{function}); - return builtinTypes->errorRecoveryTypePack(); + return builtinTypes->errorTypePack; } } @@ -1677,13 +1677,13 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal while (superIter.good()) { - tryUnify_(*superIter, builtinTypes->errorRecoveryType()); + tryUnify_(*superIter, builtinTypes->errorType); superIter.advance(); } while (subIter.good()) { - tryUnify_(*subIter, builtinTypes->errorRecoveryType()); + tryUnify_(*subIter, builtinTypes->errorType); subIter.advance(); } @@ -2256,9 +2256,9 @@ void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed) { std::string reason = "The former's metatable does not satisfy the requirements."; if (e) - reportError(location, TypeMismatch{osuperTy, osubTy, reason, *e, mismatchContext()}); + reportError(location, TypeMismatch{osuperTy, osubTy, std::move(reason), std::move(e), mismatchContext()}); else - reportError(location, TypeMismatch{osuperTy, osubTy, reason, mismatchContext()}); + reportError(location, TypeMismatch{osuperTy, osubTy, std::move(reason), mismatchContext()}); }; // Given t1 where t1 = { lower: (t1) -> (a, b...) } @@ -2369,7 +2369,7 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed) case TableState::Sealed: case TableState::Unsealed: case TableState::Generic: - reportError(mismatchError); + reportError(std::move(mismatchError)); } } else if (log.getMutable(subTy) || log.getMutable(subTy)) @@ -2377,7 +2377,7 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed) } else { - reportError(mismatchError); + reportError(std::move(mismatchError)); } } @@ -2462,7 +2462,7 @@ void Unifier::tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed { ok = false; std::string msg = "Extern type " + superExternType->name + " does not have an indexer"; - reportError(location, GenericError{msg}); + reportError(location, GenericError{std::move(msg)}); } if (!ok) @@ -2670,7 +2670,7 @@ void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp) { LUAU_ASSERT(get(anyTp)); - const TypeId anyTy = builtinTypes->errorRecoveryType(); + const TypeId anyTy = builtinTypes->errorType; std::vector queue; @@ -2726,7 +2726,7 @@ bool Unifier::occursCheck(TypeId needle, TypeId haystack, bool reversed) if (innerState->failure) { reportError(location, OccursCheckFailed{}); - log.replace(needle, BoundType{builtinTypes->errorRecoveryType()}); + log.replace(needle, BoundType{builtinTypes->errorType}); } } @@ -2787,7 +2787,7 @@ bool Unifier::occursCheck(TypePackId needle, TypePackId haystack, bool reversed) if (occurs) { reportError(location, OccursCheckFailed{}); - log.replace(needle, BoundTypePack{builtinTypes->errorRecoveryTypePack()}); + log.replace(needle, BoundTypePack{builtinTypes->errorTypePack}); } return occurs; diff --git a/Analysis/src/Unifier2.cpp b/Analysis/src/Unifier2.cpp index 5ed565fe..af1098a2 100644 --- a/Analysis/src/Unifier2.cpp +++ b/Analysis/src/Unifier2.cpp @@ -19,7 +19,7 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) namespace Luau { @@ -329,12 +329,12 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn) for (TypePackId genericPack : subFn->genericPacks) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) genericPack = follow(genericPack); - // TODO: Clip this follow() with LuauEagerGeneralization2 + // TODO: Clip this follow() with LuauEagerGeneralization4 const GenericTypePack* gen = get(follow(genericPack)); if (gen) genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity); @@ -465,7 +465,7 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable) { result &= unify(subTable->indexer->indexType, superTable->indexer->indexType); result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // FIXME: We can probably do something more efficient here. result &= unify(superTable->indexer->indexType, subTable->indexer->indexType); diff --git a/Ast/include/Luau/Parser.h b/Ast/include/Luau/Parser.h index 0d5af69b..cffb304a 100644 --- a/Ast/include/Luau/Parser.h +++ b/Ast/include/Luau/Parser.h @@ -130,7 +130,8 @@ private: // function funcname funcbody LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray& attributes = {nullptr, 0}); - std::pair validateAttribute(const char* attributeName, const TempVector& attributes); + std::pair validateAttribute_DEPRECATED(const char* attributeName, const TempVector& attributes); + std::optional validateAttribute(const char* attributeName, const TempVector& attributes); // attribute ::= '@' NAME void parseAttribute(TempVector& attribute); diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 80a5369f..fcc4d496 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -24,6 +24,7 @@ LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer) LUAU_FASTFLAGVARIABLE(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauStoreLocalAnnotationColonPositions) LUAU_FASTFLAGVARIABLE(LuauCSTForReturnTypeFunctionTail) +LUAU_FASTFLAGVARIABLE(LuauParseAttributeFixUninit) LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false) // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix @@ -808,8 +809,10 @@ AstStat* Parser::parseFunctionStat(const AstArray& attributes) } -std::pair Parser::validateAttribute(const char* attributeName, const TempVector& attributes) +std::pair Parser::validateAttribute_DEPRECATED(const char* attributeName, const TempVector& attributes) { + LUAU_ASSERT(!FFlag::LuauParseAttributeFixUninit); + AstAttr::Type type; // check if the attribute name is valid @@ -848,6 +851,42 @@ std::pair Parser::validateAttribute(const char* attributeNa return {found, type}; } +std::optional Parser::validateAttribute(const char* attributeName, const TempVector& attributes) +{ + LUAU_ASSERT(FFlag::LuauParseAttributeFixUninit); + + // check if the attribute name is valid + std::optional type; + + for (int i = 0; kAttributeEntries[i].name; ++i) + { + if (strcmp(attributeName, kAttributeEntries[i].name) == 0) + { + type = kAttributeEntries[i].type; + break; + } + } + + if (!type) + { + if (strlen(attributeName) == 1) + report(lexer.current().location, "Attribute name is missing"); + else + report(lexer.current().location, "Invalid attribute '%s'", attributeName); + } + else + { + // check that attribute is not duplicated + for (const AstAttr* attr : attributes) + { + if (attr->type == *type) + report(lexer.current().location, "Cannot duplicate attribute '%s'", attributeName); + } + } + + return type; +} + // attribute ::= '@' NAME void Parser::parseAttribute(TempVector& attributes) { @@ -855,13 +894,26 @@ void Parser::parseAttribute(TempVector& attributes) Location loc = lexer.current().location; - const char* name = lexer.current().name; - const auto [found, type] = validateAttribute(name, attributes); + if (FFlag::LuauParseAttributeFixUninit) + { + const char* name = lexer.current().name; + std::optional type = validateAttribute(name, attributes); - nextLexeme(); + nextLexeme(); - if (found) - attributes.push_back(allocator.alloc(loc, type)); + if (type) + attributes.push_back(allocator.alloc(loc, *type)); + } + else + { + const char* name = lexer.current().name; + const auto [found, type] = validateAttribute_DEPRECATED(name, attributes); + + nextLexeme(); + + if (found) + attributes.push_back(allocator.alloc(loc, type)); + } } // attributes ::= {attribute} diff --git a/CLI/src/Analyze.cpp b/CLI/src/Analyze.cpp index fdd8df33..1a2cafb4 100644 --- a/CLI/src/Analyze.cpp +++ b/CLI/src/Analyze.cpp @@ -178,7 +178,7 @@ struct CliFileResolver : Luau::FileResolver Luau::Require::ErrorHandler nullErrorHandler{}; Luau::Require::Navigator navigator(navigationContext, nullErrorHandler); - if (navigator.navigate(path) != Luau::Require::Navigator::Status::Success) + if (navigator.navigate(std::move(path)) != Luau::Require::Navigator::Status::Success) return std::nullopt; if (!navigationContext.isModulePresent()) diff --git a/CLI/src/Ast.cpp b/CLI/src/Ast.cpp index 5341d889..25d6a326 100644 --- a/CLI/src/Ast.cpp +++ b/CLI/src/Ast.cpp @@ -66,7 +66,7 @@ int main(int argc, char** argv) options.captureComments = true; options.allowDeclarationSyntax = true; - Luau::ParseResult parseResult = Luau::Parser::parse(source.data(), source.size(), names, allocator, options); + Luau::ParseResult parseResult = Luau::Parser::parse(source.data(), source.size(), names, allocator, std::move(options)); if (parseResult.errors.size() > 0) { diff --git a/CLI/src/Reduce.cpp b/CLI/src/Reduce.cpp index e66d80dc..ff6a65ca 100644 --- a/CLI/src/Reduce.cpp +++ b/CLI/src/Reduce.cpp @@ -490,8 +490,8 @@ int main(int argc, char** argv) help(args); } - const std::string scriptName = argv[1]; - const std::string appName = argv[2]; + std::string scriptName = argv[1]; + std::string appName = argv[2]; const std::string searchText = argv[3]; std::optional source = readFile(scriptName); @@ -503,5 +503,5 @@ int main(int argc, char** argv) } Reducer reducer; - reducer.run(scriptName, appName, *source, searchText); + reducer.run(std::move(scriptName), std::move(appName), *source, searchText); } diff --git a/CodeGen/include/Luau/IrBuilder.h b/CodeGen/include/Luau/IrBuilder.h index 2077cce0..3f48dc0c 100644 --- a/CodeGen/include/Luau/IrBuilder.h +++ b/CodeGen/include/Luau/IrBuilder.h @@ -41,6 +41,7 @@ struct IrBuilder IrOp constInt(int value); IrOp constUint(unsigned value); + IrOp constImport(unsigned value); IrOp constDouble(double value); IrOp constTag(uint8_t value); IrOp constAny(IrConst constant, uint64_t asCommonKey); diff --git a/CodeGen/include/Luau/IrData.h b/CodeGen/include/Luau/IrData.h index 52a394d8..0dd3ca21 100644 --- a/CodeGen/include/Luau/IrData.h +++ b/CodeGen/include/Luau/IrData.h @@ -392,11 +392,19 @@ enum class IrCmd : uint8_t // C: Rn or unsigned int (key) SET_TABLE, + // TODO: remove with FFlagLuauCodeGenSimplifyImport // Lookup a value in the environment // A: Rn (where to store the result) // B: unsigned int (import path) GET_IMPORT, + // Store an import from constant or the import path + // A: Rn (where to store the result) + // B: Kn + // C: unsigned int (import path) + // D: unsigned int (pcpos) + GET_CACHED_IMPORT, + // Concatenate multiple TValues into a string // A: Rn (value start) // B: unsigned int (number of registers to go over) @@ -763,6 +771,7 @@ enum class IrConstKind : uint8_t Uint, Double, Tag, + Import, }; struct IrConst @@ -1129,6 +1138,14 @@ struct IrFunction return value.valueUint; } + unsigned importOp(IrOp op) + { + IrConst& value = constOp(op); + + CODEGEN_ASSERT(value.kind == IrConstKind::Import); + return value.valueUint; + } + std::optional asUintOp(IrOp op) { if (op.kind != IrOpKind::Constant) diff --git a/CodeGen/include/Luau/IrDump.h b/CodeGen/include/Luau/IrDump.h index 27a9feb4..efdb95ec 100644 --- a/CodeGen/include/Luau/IrDump.h +++ b/CodeGen/include/Luau/IrDump.h @@ -32,7 +32,7 @@ void toString(IrToStringContext& ctx, const IrInst& inst, uint32_t index); void toString(IrToStringContext& ctx, const IrBlock& block, uint32_t index); // Block title void toString(IrToStringContext& ctx, IrOp op); -void toString(std::string& result, IrConst constant); +void toString(std::string& result, Proto* proto, IrConst constant); const char* getBytecodeTypeName(uint8_t type, const char* const* userdataTypes); diff --git a/CodeGen/include/Luau/IrVisitUseDef.h b/CodeGen/include/Luau/IrVisitUseDef.h index 39dd6cf8..97707ee2 100644 --- a/CodeGen/include/Luau/IrVisitUseDef.h +++ b/CodeGen/include/Luau/IrVisitUseDef.h @@ -68,6 +68,9 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i case IrCmd::GET_IMPORT: visitor.def(inst.a); break; + case IrCmd::GET_CACHED_IMPORT: + visitor.def(inst.a); + break; case IrCmd::CONCAT: visitor.useRange(vmRegOp(inst.a), function.uintOp(inst.b)); diff --git a/CodeGen/src/CodeGenUtils.cpp b/CodeGen/src/CodeGenUtils.cpp index 6fb8b5c8..05d98472 100644 --- a/CodeGen/src/CodeGenUtils.cpp +++ b/CodeGen/src/CodeGenUtils.cpp @@ -235,6 +235,14 @@ Udata* newUserdata(lua_State* L, size_t s, int tag) return u; } +void getImport(lua_State* L, StkId res, unsigned id, unsigned pc) +{ + Closure* cl = clvalue(L->ci->func); + L->ci->savedpc = cl->l.p->code + pc; + + luaV_getimport(L, cl->env, cl->l.p->k, res, id, /*propagatenil*/ false); +} + // Extracted as-is from lvmexecute.cpp with the exception of control flow (reentry) and removed interrupts/savedpc Closure* callFallback(lua_State* L, StkId ra, StkId argtop, int nresults) { diff --git a/CodeGen/src/CodeGenUtils.h b/CodeGen/src/CodeGenUtils.h index 1003a6f3..14c3595b 100644 --- a/CodeGen/src/CodeGenUtils.h +++ b/CodeGen/src/CodeGenUtils.h @@ -18,6 +18,7 @@ Closure* callProlog(lua_State* L, TValue* ra, StkId argtop, int nresults); void callEpilogC(lua_State* L, int nresults, int n); Udata* newUserdata(lua_State* L, size_t s, int tag); +void getImport(lua_State* L, StkId res, unsigned id, unsigned pc); #define CALL_FALLBACK_YIELD 1 diff --git a/CodeGen/src/IrBuilder.cpp b/CodeGen/src/IrBuilder.cpp index bf762dc4..3d673f92 100644 --- a/CodeGen/src/IrBuilder.cpp +++ b/CodeGen/src/IrBuilder.cpp @@ -676,6 +676,14 @@ IrOp IrBuilder::constUint(unsigned value) return constAny(constant, uint64_t(value)); } +IrOp IrBuilder::constImport(unsigned value) +{ + IrConst constant; + constant.kind = IrConstKind::Import; + constant.valueUint = value; + return constAny(constant, uint64_t(value)); +} + IrOp IrBuilder::constDouble(double value) { IrConst constant; diff --git a/CodeGen/src/IrDump.cpp b/CodeGen/src/IrDump.cpp index e4f0c27d..1427e30e 100644 --- a/CodeGen/src/IrDump.cpp +++ b/CodeGen/src/IrDump.cpp @@ -251,6 +251,8 @@ const char* getCmdName(IrCmd cmd) return "SET_TABLE"; case IrCmd::GET_IMPORT: return "GET_IMPORT"; + case IrCmd::GET_CACHED_IMPORT: + return "GET_CACHED_IMPORT"; case IrCmd::CONCAT: return "CONCAT"; case IrCmd::GET_UPVALUE: @@ -501,7 +503,7 @@ void toString(IrToStringContext& ctx, IrOp op) append(ctx.result, "undef"); break; case IrOpKind::Constant: - toString(ctx.result, ctx.constants[op.index]); + toString(ctx.result, ctx.proto, ctx.constants[op.index]); break; case IrOpKind::Condition: CODEGEN_ASSERT(op.index < uint32_t(IrCondition::Count)); @@ -539,7 +541,7 @@ void toString(IrToStringContext& ctx, IrOp op) } } -void toString(std::string& result, IrConst constant) +void toString(std::string& result, Proto* proto, IrConst constant) { switch (constant.kind) { @@ -558,6 +560,36 @@ void toString(std::string& result, IrConst constant) case IrConstKind::Tag: result.append(getTagName(constant.valueTag)); break; + case IrConstKind::Import: + append(result, "%uu", constant.valueUint); + + if (proto) + { + append(result, " ("); + + int count = constant.valueUint >> 30; + int id0 = count > 0 ? int(constant.valueUint >> 20) & 1023 : -1; + int id1 = count > 1 ? int(constant.valueUint >> 10) & 1023 : -1; + int id2 = count > 2 ? int(constant.valueUint) & 1023 : -1; + + if (id0 != -1) + appendVmConstant(result, proto, id0); + + if (id1 != -1) + { + append(result, "."); + appendVmConstant(result, proto, id1); + } + + if (id2 != -1) + { + append(result, "."); + appendVmConstant(result, proto, id2); + } + + append(result, ")"); + } + break; } } diff --git a/CodeGen/src/IrLoweringA64.cpp b/CodeGen/src/IrLoweringA64.cpp index ddfa2c1d..8957c01c 100644 --- a/CodeGen/src/IrLoweringA64.cpp +++ b/CodeGen/src/IrLoweringA64.cpp @@ -1382,6 +1382,47 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) emitUpdateBase(build); break; + case IrCmd::GET_CACHED_IMPORT: + { + Label skip, exit; + RegisterA64 tempTag = regs.allocTemp(KindA64::w); + + AddressA64 addrConstTag = tempAddr(inst.b, offsetof(TValue, tt)); + build.ldr(tempTag, addrConstTag); + + // If the constant for the import is set, we will use it directly, otherwise we have to call an import path lookup function + CODEGEN_ASSERT(LUA_TNIL == 0); + build.cbnz(tempTag, skip); + + { + size_t spills = regs.spill(build, index); + + build.mov(x0, rState); + build.add(x1, rBase, uint16_t(vmRegOp(inst.a) * sizeof(TValue))); + build.mov(w2, importOp(inst.c)); + build.mov(w3, uintOp(inst.d)); + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, getImport))); + build.blr(x4); + + regs.restore(build, spills); // Need to restore before skip so that registers are in a consistent state + + emitUpdateBase(build); + build.b(exit); + } + + RegisterA64 tempTv = regs.allocTemp(KindA64::q); + + build.setLabel(skip); + + AddressA64 addrConst = tempAddr(inst.b, 0); + build.ldr(tempTv, addrConst); + + AddressA64 addrReg = tempAddr(inst.a, 0); + build.str(tempTv, addrReg); + + build.setLabel(exit); + break; + } case IrCmd::CONCAT: regs.spill(build, index); build.mov(x0, rState); @@ -2720,6 +2761,11 @@ unsigned IrLoweringA64::uintOp(IrOp op) const return function.uintOp(op); } +unsigned IrLoweringA64::importOp(IrOp op) const +{ + return function.importOp(op); +} + double IrLoweringA64::doubleOp(IrOp op) const { return function.doubleOp(op); diff --git a/CodeGen/src/IrLoweringA64.h b/CodeGen/src/IrLoweringA64.h index 5f13f58e..717b5d6a 100644 --- a/CodeGen/src/IrLoweringA64.h +++ b/CodeGen/src/IrLoweringA64.h @@ -54,6 +54,7 @@ struct IrLoweringA64 uint8_t tagOp(IrOp op) const; int intOp(IrOp op) const; unsigned uintOp(IrOp op) const; + unsigned importOp(IrOp op) const; double doubleOp(IrOp op) const; IrBlock& blockOp(IrOp op) const; diff --git a/CodeGen/src/IrLoweringX64.cpp b/CodeGen/src/IrLoweringX64.cpp index 1ef9f774..068c2d4e 100644 --- a/CodeGen/src/IrLoweringX64.cpp +++ b/CodeGen/src/IrLoweringX64.cpp @@ -1219,6 +1219,36 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) emitUpdateBase(build); break; } + case IrCmd::GET_CACHED_IMPORT: + { + Label skip, exit; + + // If the constant for the import is set, we will use it directly, otherwise we have to call an import path lookup function + build.cmp(luauConstantTag(vmConstOp(inst.b)), LUA_TNIL); + build.jcc(ConditionX64::NotEqual, skip); + + { + ScopedSpills spillGuard(regs); + + IrCallWrapperX64 callWrap(regs, build, index); + callWrap.addArgument(SizeX64::qword, rState); + callWrap.addArgument(SizeX64::qword, luauRegAddress(vmRegOp(inst.a))); + callWrap.addArgument(SizeX64::dword, importOp(inst.c)); + callWrap.addArgument(SizeX64::dword, uintOp(inst.d)); + callWrap.call(qword[rNativeContext + offsetof(NativeContext, getImport)]); + } + + emitUpdateBase(build); + build.jmp(exit); + + ScopedRegX64 tmp1{regs, SizeX64::xmmword}; + + build.setLabel(skip); + build.vmovups(tmp1.reg, luauConstant(vmConstOp(inst.b))); + build.vmovups(luauReg(vmRegOp(inst.a)), tmp1.reg); + build.setLabel(exit); + break; + } case IrCmd::CONCAT: { IrCallWrapperX64 callWrap(regs, build, index); @@ -2350,6 +2380,11 @@ unsigned IrLoweringX64::uintOp(IrOp op) const return function.uintOp(op); } +unsigned IrLoweringX64::importOp(IrOp op) const +{ + return function.importOp(op); +} + double IrLoweringX64::doubleOp(IrOp op) const { return function.doubleOp(op); diff --git a/CodeGen/src/IrLoweringX64.h b/CodeGen/src/IrLoweringX64.h index 8fb311ea..b4b9918a 100644 --- a/CodeGen/src/IrLoweringX64.h +++ b/CodeGen/src/IrLoweringX64.h @@ -57,6 +57,7 @@ struct IrLoweringX64 uint8_t tagOp(IrOp op) const; int intOp(IrOp op) const; unsigned uintOp(IrOp op) const; + unsigned importOp(IrOp op) const; double doubleOp(IrOp op) const; IrBlock& blockOp(IrOp op) const; diff --git a/CodeGen/src/IrTranslation.cpp b/CodeGen/src/IrTranslation.cpp index 0c22bfc3..fe9a7eb6 100644 --- a/CodeGen/src/IrTranslation.cpp +++ b/CodeGen/src/IrTranslation.cpp @@ -12,6 +12,8 @@ #include "lstate.h" #include "ltm.h" +LUAU_FASTFLAGVARIABLE(LuauCodeGenSimplifyImport) + namespace Luau { namespace CodeGen @@ -1215,27 +1217,35 @@ void translateInstGetImport(IrBuilder& build, const Instruction* pc, int pcpos) int k = LUAU_INSN_D(*pc); uint32_t aux = pc[1]; - IrOp fastPath = build.block(IrBlockKind::Internal); - IrOp fallback = build.block(IrBlockKind::Fallback); + if (FFlag::LuauCodeGenSimplifyImport) + { + build.inst(IrCmd::CHECK_SAFE_ENV, build.vmExit(pcpos)); + build.inst(IrCmd::GET_CACHED_IMPORT, build.vmReg(ra), build.vmConst(k), build.constImport(aux), build.constUint(pcpos + 1)); + } + else + { + IrOp fastPath = build.block(IrBlockKind::Internal); + IrOp fallback = build.block(IrBlockKind::Fallback); - build.inst(IrCmd::CHECK_SAFE_ENV, build.vmExit(pcpos)); + build.inst(IrCmd::CHECK_SAFE_ENV, build.vmExit(pcpos)); - // note: if import failed, k[] is nil; we could check this during codegen, but we instead use runtime fallback - // this allows us to handle ahead-of-time codegen smoothly when an import fails to resolve at runtime - IrOp tk = build.inst(IrCmd::LOAD_TAG, build.vmConst(k)); - build.inst(IrCmd::JUMP_EQ_TAG, tk, build.constTag(LUA_TNIL), fallback, fastPath); + // note: if import failed, k[] is nil; we could check this during codegen, but we instead use runtime fallback + // this allows us to handle ahead-of-time codegen smoothly when an import fails to resolve at runtime + IrOp tk = build.inst(IrCmd::LOAD_TAG, build.vmConst(k)); + build.inst(IrCmd::JUMP_EQ_TAG, tk, build.constTag(LUA_TNIL), fallback, fastPath); - build.beginBlock(fastPath); + build.beginBlock(fastPath); - IrOp tvk = build.inst(IrCmd::LOAD_TVALUE, build.vmConst(k)); - build.inst(IrCmd::STORE_TVALUE, build.vmReg(ra), tvk); + IrOp tvk = build.inst(IrCmd::LOAD_TVALUE, build.vmConst(k)); + build.inst(IrCmd::STORE_TVALUE, build.vmReg(ra), tvk); - IrOp next = build.blockAtInst(pcpos + 2); - FallbackStreamScope scope(build, fallback, next); + IrOp next = build.blockAtInst(pcpos + 2); + FallbackStreamScope scope(build, fallback, next); - build.inst(IrCmd::SET_SAVEDPC, build.constUint(pcpos + 1)); - build.inst(IrCmd::GET_IMPORT, build.vmReg(ra), build.constUint(aux)); - build.inst(IrCmd::JUMP, next); + build.inst(IrCmd::SET_SAVEDPC, build.constUint(pcpos + 1)); + build.inst(IrCmd::GET_IMPORT, build.vmReg(ra), build.constUint(aux)); + build.inst(IrCmd::JUMP, next); + } } void translateInstGetTableKS(IrBuilder& build, const Instruction* pc, int pcpos) diff --git a/CodeGen/src/IrUtils.cpp b/CodeGen/src/IrUtils.cpp index bc04215f..f77ceb78 100644 --- a/CodeGen/src/IrUtils.cpp +++ b/CodeGen/src/IrUtils.cpp @@ -240,6 +240,7 @@ IrValueKind getCmdValueKind(IrCmd cmd) case IrCmd::GET_TABLE: case IrCmd::SET_TABLE: case IrCmd::GET_IMPORT: + case IrCmd::GET_CACHED_IMPORT: case IrCmd::CONCAT: case IrCmd::GET_UPVALUE: case IrCmd::SET_UPVALUE: diff --git a/CodeGen/src/IrValueLocationTracking.cpp b/CodeGen/src/IrValueLocationTracking.cpp index 050b951e..3f764801 100644 --- a/CodeGen/src/IrValueLocationTracking.cpp +++ b/CodeGen/src/IrValueLocationTracking.cpp @@ -55,6 +55,7 @@ void IrValueLocationTracking::beforeInstLowering(IrInst& inst) case IrCmd::DO_LEN: case IrCmd::GET_TABLE: case IrCmd::GET_IMPORT: + case IrCmd::GET_CACHED_IMPORT: invalidateRestoreOp(inst.a, /*skipValueInvalidation*/ false); break; case IrCmd::CONCAT: diff --git a/CodeGen/src/NativeState.cpp b/CodeGen/src/NativeState.cpp index 798ec314..2c70fca6 100644 --- a/CodeGen/src/NativeState.cpp +++ b/CodeGen/src/NativeState.cpp @@ -90,6 +90,7 @@ void initFunctions(NativeContext& context) context.callProlog = callProlog; context.callEpilogC = callEpilogC; context.newUserdata = newUserdata; + context.getImport = getImport; context.callFallback = callFallback; diff --git a/CodeGen/src/NativeState.h b/CodeGen/src/NativeState.h index b4f74132..6de775ed 100644 --- a/CodeGen/src/NativeState.h +++ b/CodeGen/src/NativeState.h @@ -94,6 +94,7 @@ struct NativeContext Closure* (*callProlog)(lua_State* L, TValue* ra, StkId argtop, int nresults) = nullptr; void (*callEpilogC)(lua_State* L, int nresults, int n) = nullptr; Udata* (*newUserdata)(lua_State* L, size_t s, int tag) = nullptr; + void (*getImport)(lua_State* L, StkId res, unsigned id, unsigned pc) = nullptr; Closure* (*callFallback)(lua_State* L, StkId ra, StkId argtop, int nresults) = nullptr; diff --git a/CodeGen/src/OptimizeConstProp.cpp b/CodeGen/src/OptimizeConstProp.cpp index 11909ec5..31d88fec 100644 --- a/CodeGen/src/OptimizeConstProp.cpp +++ b/CodeGen/src/OptimizeConstProp.cpp @@ -1539,6 +1539,13 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& state.invalidate(inst.a); state.invalidateUserCall(); break; + case IrCmd::GET_CACHED_IMPORT: + state.invalidate(inst.a); + + // Outside of safe environment, environment traversal for an import can execute custom code + if (!state.inSafeEnv) + state.invalidateUserCall(); + break; case IrCmd::CONCAT: state.invalidateRegisterRange(vmRegOp(inst.a), function.uintOp(inst.b)); state.invalidateUserCall(); // TODO: if only strings and numbers are concatenated, there will be no user calls diff --git a/CodeGen/src/OptimizeDeadStore.cpp b/CodeGen/src/OptimizeDeadStore.cpp index 1483e4a2..a0892919 100644 --- a/CodeGen/src/OptimizeDeadStore.cpp +++ b/CodeGen/src/OptimizeDeadStore.cpp @@ -767,6 +767,7 @@ static void markDeadStoresInInst(RemoveDeadStoreState& state, IrBuilder& build, case IrCmd::GET_TABLE: case IrCmd::SET_TABLE: case IrCmd::GET_IMPORT: + case IrCmd::GET_CACHED_IMPORT: case IrCmd::CONCAT: case IrCmd::INTERRUPT: case IrCmd::CHECK_GC: diff --git a/fuzz/luau.proto b/fuzz/luau.proto index 5fed9ddc..148eb1d6 100644 --- a/fuzz/luau.proto +++ b/fuzz/luau.proto @@ -39,10 +39,26 @@ message Local { required int32 name = 1; } -message Typename { +message RegularTypeName { required int32 index = 1; } +message GenericTypeName { + required int32 index = 1; +} + +message BuiltinTypeName { + required int32 index = 1; +} + +message TypeName { + oneof expr_oneof { + RegularTypeName regular = 1; + GenericTypeName generic = 2; + BuiltinTypeName builtin = 3; + } +} + message Name { oneof name_oneof { int32 builtin = 1; @@ -97,8 +113,8 @@ message ExprIndexExpr { } message ExprFunction { - repeated Typename generics = 1; - repeated Typename genericpacks = 2; + repeated GenericTypeName generics = 1; + repeated GenericTypeName genericpacks = 2; repeated Local args = 3; required bool vararg = 4; required StatBlock body = 5; @@ -195,6 +211,7 @@ message Stat { StatLocalFunction local_function = 15; StatTypeAlias type_alias = 16; StatRequireIntoLocalHelper require_into_local = 17; + StatTypeFunction type_function = 18; } } @@ -289,10 +306,16 @@ message StatLocalFunction { message StatTypeAlias { required bool export = 1; - required Typename name = 2; + required RegularTypeName name = 2; required Type type = 3; - repeated Typename generics = 4; - repeated Typename genericpacks = 5; + repeated GenericTypeName generics = 4; + repeated GenericTypeName genericpacks = 5; +} + +message StatTypeFunction { + required bool export = 1; + required RegularTypeName name = 2; + required ExprFunction func = 3; } message StatRequireIntoLocalHelper { @@ -309,7 +332,7 @@ message Type { TypeTypeof typeof = 5; TypeUnion union = 6; TypeIntersection intersection = 7; - TypeClass class = 8; + TypeExtern extern = 8; TypeRef ref = 9; TypeBoolean boolean = 10; TypeString string = 11; @@ -321,19 +344,26 @@ message TypePrimitive { } message TypeLiteral { - required Typename name = 1; + required TypeName name = 1; repeated Type generics = 2; - repeated Typename genericpacks = 3; + repeated GenericTypeName genericpacks = 3; +} + +enum TableFieldAccess { + Read = 1; + Write = 2; } message TypeTableItem { - required Name key = 1; - required Type type = 2; + optional TableFieldAccess access = 1; + required Name key = 2; + required Type type = 3; } message TypeTableIndexer { - required Type key = 1; - required Type value = 2; + optional TableFieldAccess access = 1; + required Type key = 2; + required Type value = 3; } message TypeTable { @@ -342,8 +372,8 @@ message TypeTable { } message TypeFunction { - repeated Typename generics = 1; - repeated Typename genericpacks = 2; + repeated GenericTypeName generics = 1; + repeated GenericTypeName genericpacks = 2; repeated Type args = 3; repeated Type rets = 4; // TODO: vararg? @@ -363,13 +393,13 @@ message TypeIntersection { required Type right = 2; } -message TypeClass { +message TypeExtern { required int32 kind = 1; } message TypeRef { required Local prefix = 1; - required Typename index = 2; + required TypeName index = 2; } message TypeBoolean { diff --git a/fuzz/protoprint.cpp b/fuzz/protoprint.cpp index dc3160eb..2aa5244e 100644 --- a/fuzz/protoprint.cpp +++ b/fuzz/protoprint.cpp @@ -22,7 +22,8 @@ static const std::string kNames[] = { "sub", "table", "tan", "tanh", "thread", "time", "tonumber", "tostring", "tostring", "traceback", "type", "typeof", "unpack", "upper", "userdata", "utf8", "vector", "wrap", "writef32", "writef64", "writei16", "writei32", "writei8", "writestring", "writeu16", "writeu32", "writeu8", - "xpcall", "yield", + "xpcall", "yield", "types", "unknown", "never", "any", "singleton", "optional", "generic", + "negationof", "unionof", "intersectionof", "newtable", "newfunction", }; static const std::string kTypes[] = { @@ -34,6 +35,8 @@ static const std::string kTypes[] = { "string", "thread", "vector", + "unknown", + "never", }; static const std::string kExternTypes[] = { @@ -42,6 +45,28 @@ static const std::string kExternTypes[] = { "Part", }; +static const std::string kBuiltinTypes[] = { + "len", + "unm", + "add", + "sub", + "mul", + "div", + "idiv", + "pow", + "mod", + "concat", + "lt", + "le", + "eq", + "keyof", + "rawkeyof", + "index", + "rawget", + "setmetatable", + "getmetatable", +}; + struct ProtoToLuau { struct Function @@ -79,12 +104,35 @@ struct ProtoToLuau } } - void ident(const luau::Typename& name) + void ident(const luau::RegularTypeName& name) { source += 't'; source += std::to_string(name.index() & 0xff); } + void ident(const luau::GenericTypeName& name) + { + source += char('A' + (name.index() % 26)); + } + + void ident(const luau::BuiltinTypeName& name) + { + size_t index = size_t(name.index()) % std::size(kBuiltinTypes); + source += kBuiltinTypes[index]; + } + + void ident(const luau::TypeName& name) + { + if (name.has_regular()) + ident(name.regular()); + else if (name.has_generic()) + ident(name.generic()); + else if (name.has_builtin()) + ident(name.builtin()); + else + source += "any"; + } + template void genericidents(const T& node) { @@ -480,6 +528,8 @@ struct ProtoToLuau print(stat.type_alias()); else if (stat.has_require_into_local()) print(stat.require_into_local()); + else if (stat.has_type_function()) + print(stat.type_function()); else source += "do end\n"; } @@ -768,6 +818,17 @@ struct ProtoToLuau source += " = require(module" + std::to_string(stat.modulenum() % 2) + ")\n"; } + void print(const luau::StatTypeFunction& stat) + { + if (stat.export_()) + source += "export "; + + source += "type function "; + ident(stat.name()); + function(stat.func()); + source += '\n'; + } + void print(const luau::Type& type) { if (type.has_primitive()) @@ -784,8 +845,8 @@ struct ProtoToLuau print(type.union_()); else if (type.has_intersection()) print(type.intersection()); - else if (type.has_class_()) - print(type.class_()); + else if (type.has_extern_()) + print(type.extern_()); else if (type.has_ref()) print(type.ref()); else if (type.has_boolean()) @@ -832,22 +893,46 @@ struct ProtoToLuau } } + void print(const luau::TableFieldAccess& expr) + { + if (expr == luau::TableFieldAccess::Read) + source += "read"; + else if (expr == luau::TableFieldAccess::Write) + source += "write"; + } + void print(const luau::TypeTable& type) { source += '{'; for (size_t i = 0; i < type.items_size(); ++i) { - ident(type.items(i).key()); + auto& item = type.items(i); + + if (item.has_access()) + { + print(item.access()); + source += ' '; + } + + ident(item.key()); source += ':'; - print(type.items(i).type()); + print(item.type()); source += ','; } if (type.has_indexer()) { + auto& indexer = type.indexer(); + + if (indexer.has_access()) + { + print(indexer.access()); + source += ' '; + } + source += '['; - print(type.indexer().key()); + print(indexer.key()); source += "]:"; - print(type.indexer().value()); + print(indexer.value()); } source += '}'; } @@ -900,7 +985,7 @@ struct ProtoToLuau source += ')'; } - void print(const luau::TypeClass& type) + void print(const luau::TypeExtern& type) { size_t index = size_t(type.kind()) % std::size(kExternTypes); source += kExternTypes[index]; diff --git a/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index a955b738..3a5e708f 100644 --- a/tests/Autocomplete.test.cpp +++ b/tests/Autocomplete.test.cpp @@ -19,8 +19,9 @@ LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2) LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) LUAU_FASTINT(LuauTypeInferRecursionLimit) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauExpectedTypeVisitor) +LUAU_FASTFLAG(LuauImplicitTableIndexerKeys) using namespace Luau; @@ -45,9 +46,9 @@ struct ACFixtureImpl : BaseType // NOTE: Autocomplete does *not* require strict checking, meaning we should // try to check all of these examples in `--!nocheck` mode. this->configResolver.defaultConfig.mode = Mode::NoCheck; - this->frontend.check("MainModule", opts); + this->getFrontend().check("MainModule", opts); - return Luau::autocomplete(this->frontend, "MainModule", Position{row, column}, nullCallback); + return Luau::autocomplete(this->getFrontend(), "MainModule", Position{row, column}, nullCallback); } AutocompleteResult autocomplete(char marker, StringCompletionCallback callback = nullCallback) @@ -58,9 +59,9 @@ struct ACFixtureImpl : BaseType // NOTE: Autocomplete does *not* require strict checking, meaning we should // try to check all of these examples in `--!nocheck` mode. this->configResolver.defaultConfig.mode = Mode::NoCheck; - this->frontend.check("MainModule", opts); + this->getFrontend().check("MainModule", opts); - return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), callback); + return Luau::autocomplete(this->getFrontend(), "MainModule", getPosition(marker), callback); } AutocompleteResult autocomplete(const ModuleName& name, Position pos, StringCompletionCallback callback = nullCallback) @@ -71,9 +72,9 @@ struct ACFixtureImpl : BaseType // NOTE: Autocomplete does *not* require strict checking, meaning we should // try to check all of these examples in `--!nocheck` mode. this->configResolver.defaultConfig.mode = Mode::NoCheck; - this->frontend.check(name, opts); + this->getFrontend().check(name, opts); - return Luau::autocomplete(this->frontend, name, pos, callback); + return Luau::autocomplete(this->getFrontend(), name, pos, callback); } CheckResult check(const std::string& source) @@ -120,18 +121,18 @@ struct ACFixtureImpl : BaseType LoadDefinitionFileResult loadDefinition(const std::string& source) { - GlobalTypes& globals = this->frontend.globalsForAutocomplete; + GlobalTypes& globals = this->getFrontend().globalsForAutocomplete; unfreeze(globals.globalTypes); - LoadDefinitionFileResult result = this->frontend.loadDefinitionFile( + LoadDefinitionFileResult result = this->getFrontend().loadDefinitionFile( globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typeCheckForAutocomplete */ true ); freeze(globals.globalTypes); if (FFlag::LuauSolverV2) { - GlobalTypes& globals = this->frontend.globals; + GlobalTypes& globals = this->getFrontend().globals; unfreeze(globals.globalTypes); - LoadDefinitionFileResult result = this->frontend.loadDefinitionFile( + LoadDefinitionFileResult result = this->getFrontend().loadDefinitionFile( globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typeCheckForAutocomplete */ true ); freeze(globals.globalTypes); @@ -156,10 +157,11 @@ struct ACFixture : ACFixtureImpl ACFixture() : ACFixtureImpl() { - addGlobalBinding(frontend.globals, "table", Binding{builtinTypes->anyType}); - addGlobalBinding(frontend.globals, "math", Binding{builtinTypes->anyType}); - addGlobalBinding(frontend.globalsForAutocomplete, "table", Binding{builtinTypes->anyType}); - addGlobalBinding(frontend.globalsForAutocomplete, "math", Binding{builtinTypes->anyType}); + // TODO - move this into its own consructor + addGlobalBinding(getFrontend().globals, "table", Binding{getBuiltins()->anyType}); + addGlobalBinding(getFrontend().globals, "math", Binding{getBuiltins()->anyType}); + addGlobalBinding(getFrontend().globalsForAutocomplete, "table", Binding{getBuiltins()->anyType}); + addGlobalBinding(getFrontend().globalsForAutocomplete, "math", Binding{getBuiltins()->anyType}); } }; @@ -1388,14 +1390,14 @@ export type B = { z: number, w: number } return {} )"; - LUAU_REQUIRE_NO_ERRORS(frontend.check("Module/A")); + LUAU_REQUIRE_NO_ERRORS(getFrontend().check("Module/A")); fileResolver.source["Module/B"] = R"( local aaa = require(script.Parent.A) local a: aa )"; - frontend.check("Module/B"); + getFrontend().check("Module/B"); auto ac = autocomplete("Module/B", Position{2, 11}); @@ -1411,14 +1413,14 @@ export type B = { z: number, w: number } return {} )"; - LUAU_REQUIRE_NO_ERRORS(frontend.check("Module/A")); + LUAU_REQUIRE_NO_ERRORS(getFrontend().check("Module/A")); fileResolver.source["Module/B"] = R"( local aaa = require(script.Parent.A) local a: aaa. )"; - frontend.check("Module/B"); + getFrontend().check("Module/B"); auto ac = autocomplete("Module/B", Position{2, 13}); @@ -2071,14 +2073,14 @@ local function b(a: ((done) -> number) -> number) return a(function(done) return return {a = a, b = b} )"; - LUAU_REQUIRE_NO_ERRORS(frontend.check("Module/A")); + LUAU_REQUIRE_NO_ERRORS(getFrontend().check("Module/A")); fileResolver.source["Module/B"] = R"( local ex = require(script.Parent.A) ex.a(function(x: )"; - frontend.check("Module/B"); + getFrontend().check("Module/B"); auto ac = autocomplete("Module/B", Position{2, 16}); @@ -2089,7 +2091,7 @@ local ex = require(script.Parent.A) ex.b(function(x: )"; - frontend.check("Module/C"); + getFrontend().check("Module/C"); ac = autocomplete("Module/C", Position{2, 16}); @@ -2105,14 +2107,14 @@ local function b(a: ((done) -> number) -> number) return a(function(done) return return {a = a, b = b} )"; - LUAU_REQUIRE_NO_ERRORS(frontend.check("Module/A")); + LUAU_REQUIRE_NO_ERRORS(getFrontend().check("Module/A")); fileResolver.source["Module/B"] = R"( local ex = require(script.Parent.A) ex.a(function(x: )"; - frontend.check("Module/B"); + getFrontend().check("Module/B"); auto ac = autocomplete("Module/B", Position{2, 16}); @@ -2125,7 +2127,7 @@ local ex = require(script.Parent.A) ex.b(function(x: )"; - frontend.check("Module/C"); + getFrontend().check("Module/C"); ac = autocomplete("Module/C", Position{2, 16}); @@ -2441,14 +2443,14 @@ export type other = { z: number, w: number } return {} )"; - LUAU_REQUIRE_NO_ERRORS(frontend.check("Module/A")); + LUAU_REQUIRE_NO_ERRORS(getFrontend().check("Module/A")); fileResolver.source["Module/B"] = R"( local aaa = require(script.Parent.A) local a: aaa.do )"; - frontend.check("Module/B"); + getFrontend().check("Module/B"); auto ac = autocomplete("Module/B", Position{2, 15}); @@ -3089,12 +3091,29 @@ TEST_CASE_FIXTURE(ACBuiltinsFixture, "autocomplete_on_string_singletons") CHECK(ac.entryMap.count("format")); } -TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons") +TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons_in_literal") { - if (FFlag::LuauSolverV2) // CLI-116814 Autocomplete needs to populate expected types for function arguments correctly - // (overloads and singletons) + if (FFlag::LuauSolverV2) return; + // CLI-116814: Under the new solver, we fail to properly apply the expected + // type to `tag` as we fail to recognize that we can "break apart" unions + // when trying to apply an expected type. + + check(R"( + type tagged = {tag:"cat", fieldx:number} | {tag:"dog", fieldy:number} + local x: tagged = {tag="@1"} + )"); + + auto ac = autocomplete('1'); + + CHECK(ac.entryMap.count("cat")); + CHECK(ac.entryMap.count("dog")); + CHECK_EQ(ac.context, AutocompleteContext::String); +} + +TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons") +{ check(R"( type tag = "cat" | "dog" local function f(a: tag) end @@ -3120,17 +3139,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons") CHECK(ac.entryMap.count("cat")); CHECK(ac.entryMap.count("dog")); CHECK_EQ(ac.context, AutocompleteContext::String); - - check(R"( - type tagged = {tag:"cat", fieldx:number} | {tag:"dog", fieldy:number} - local x: tagged = {tag="@4"} - )"); - - ac = autocomplete('4'); - - CHECK(ac.entryMap.count("cat")); - CHECK(ac.entryMap.count("dog")); - CHECK_EQ(ac.context, AutocompleteContext::String); } TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key_iso") @@ -3705,7 +3713,7 @@ local a = { x = 2, y = 4 } a.@1 )"); - frontend.clear(); + getFrontend().clear(); auto ac = autocomplete('1'); @@ -3714,21 +3722,21 @@ a.@1 CHECK(ac.entryMap.count("x")); CHECK(ac.entryMap.count("y")); - frontend.check("MainModule", {}); + getFrontend().check("MainModule", {}); ac = autocomplete('1'); CHECK(ac.entryMap.count("x")); CHECK(ac.entryMap.count("y")); - frontend.markDirty("MainModule", nullptr); + getFrontend().markDirty("MainModule", nullptr); ac = autocomplete('1'); CHECK(ac.entryMap.count("x")); CHECK(ac.entryMap.count("y")); - frontend.check("MainModule", {}); + getFrontend().check("MainModule", {}); ac = autocomplete('1'); @@ -3763,7 +3771,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback") declare function require(path: string): any )"); - GlobalTypes& globals = FFlag::LuauSolverV2 ? frontend.globals : frontend.globalsForAutocomplete; + GlobalTypes& globals = FFlag::LuauSolverV2 ? getFrontend().globals : getFrontend().globalsForAutocomplete; std::optional require = globals.globalScope->linearSearchForBinding("require"); REQUIRE(require); @@ -3944,7 +3952,7 @@ local a: T@1 CHECK_EQ(ac.context, AutocompleteContext::Type); } -TEST_CASE_FIXTURE(ACFixture, "frontend_use_correct_global_scope") +TEST_CASE_FIXTURE(ACFixture, "getFrontend().use_correct_global_scope") { loadDefinition(R"( declare class Instance @@ -3970,7 +3978,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_completion_outside_quotes") declare function require(path: string): any )"); - GlobalTypes& globals = FFlag::LuauSolverV2 ? frontend.globals : frontend.globalsForAutocomplete; + GlobalTypes& globals = FFlag::LuauSolverV2 ? getFrontend().globals : getFrontend().globalsForAutocomplete; std::optional require = globals.globalScope->linearSearchForBinding("require"); REQUIRE(require); @@ -4468,9 +4476,9 @@ TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union") auto ac = autocomplete('1'); - if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization3) + if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization4) { - // This `if` statement is because `LuauEagerGeneralization2` + // This `if` statement is because `LuauEagerGeneralization4` // sets some flags CHECK(ac.entryMap.count("BaseMethod") > 0); CHECK(ac.entryMap.count("Method") > 0); @@ -4501,8 +4509,8 @@ TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_private_scope") ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; // Global scope polution by the embedder has no effect - addGlobalBinding(frontend.globals, "thisAlsoShouldNotBeThere", Binding{builtinTypes->anyType}); - addGlobalBinding(frontend.globalsForAutocomplete, "thisAlsoShouldNotBeThere", Binding{builtinTypes->anyType}); + addGlobalBinding(getFrontend().globals, "thisAlsoShouldNotBeThere", Binding{getBuiltins()->anyType}); + addGlobalBinding(getFrontend().globalsForAutocomplete, "thisAlsoShouldNotBeThere", Binding{getBuiltins()->anyType}); check(R"( local function thisShouldNotBeThere() end @@ -4607,4 +4615,29 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_type_assertion") CHECK_EQ(ac2.entryMap.count("prop"), 1); } +TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr") +{ + ScopedFastFlag sffs[] = { + // Somewhat surprisingly, the old solver didn't cover this case. + {FFlag::LuauSolverV2, true}, + {FFlag::LuauExpectedTypeVisitor, true}, + {FFlag::LuauImplicitTableIndexerKeys, true}, + }; + + check(R"( + type Constraint = "A" | "B" | "C" + local foo : { [Constraint]: string } = { + A = "Value for A", + B = "Value for B", + C = "Value for C", + } + foo["@1"] + )"); + + auto ac = autocomplete('1'); + CHECK_EQ(ac.entryMap.count("A"), 1); + CHECK_EQ(ac.entryMap.count("B"), 1); + CHECK_EQ(ac.entryMap.count("C"), 1); +} + TEST_SUITE_END(); diff --git a/tests/BuiltinDefinitions.test.cpp b/tests/BuiltinDefinitions.test.cpp index 56355f56..47f548a4 100644 --- a/tests/BuiltinDefinitions.test.cpp +++ b/tests/BuiltinDefinitions.test.cpp @@ -12,9 +12,9 @@ TEST_SUITE_BEGIN("BuiltinDefinitionsTest"); TEST_CASE_FIXTURE(BuiltinsFixture, "lib_documentation_symbols") { - CHECK(!frontend.globals.globalScope->bindings.empty()); + CHECK(!getFrontend().globals.globalScope->bindings.empty()); - for (const auto& [name, binding] : frontend.globals.globalScope->bindings) + for (const auto& [name, binding] : getFrontend().globals.globalScope->bindings) { std::string nameString(name.c_str()); std::string expectedRootSymbol = "@luau/global/" + nameString; diff --git a/tests/ClassFixture.cpp b/tests/ClassFixture.cpp index a8689db8..43e225e9 100644 --- a/tests/ClassFixture.cpp +++ b/tests/ClassFixture.cpp @@ -12,10 +12,17 @@ namespace Luau ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete) : BuiltinsFixture(prepareAutocomplete) { - GlobalTypes& globals = frontend.globals; + +} + +Frontend& ExternTypeFixture::getFrontend() +{ + Frontend& f = BuiltinsFixture::getFrontend(); + + GlobalTypes& globals = f.globals; TypeArena& arena = globals.globalTypes; - TypeId numberType = builtinTypes->numberType; - TypeId stringType = builtinTypes->stringType; + TypeId numberType = getBuiltins()->numberType; + TypeId stringType = getBuiltins()->stringType; unfreeze(arena); @@ -30,8 +37,7 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete) }; getMutable(connectionType)->props = { - {"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}} - }; + {"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}}}; TypeId baseClassType = arena.addType(ExternType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}}); getMutable(baseClassType)->props = { @@ -107,9 +113,8 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete) {"__mul", {arena.addType(IntersectionType{{ makeFunction(arena, vector2InstanceType, {vector2InstanceType}, {vector2InstanceType}), - makeFunction(arena, vector2InstanceType, {builtinTypes->numberType}, {vector2InstanceType}), - }})}} - }; + makeFunction(arena, vector2InstanceType, {getBuiltins()->numberType}, {vector2InstanceType}), + }})}}}; globals.globalScope->exportedTypeBindings["Vector2"] = TypeFun{{}, vector2InstanceType}; addGlobalBinding(globals, "Vector2", vector2Type, "@test"); @@ -146,6 +151,7 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete) persist(tf.type); freeze(arena); + return *frontend; } } // namespace Luau diff --git a/tests/ClassFixture.h b/tests/ClassFixture.h index 6ea6e851..62fadde0 100644 --- a/tests/ClassFixture.h +++ b/tests/ClassFixture.h @@ -10,6 +10,8 @@ struct ExternTypeFixture : BuiltinsFixture { explicit ExternTypeFixture(bool prepareAutocomplete = false); + Frontend& getFrontend() override; + TypeId vector2Type; TypeId vector2InstanceType; }; diff --git a/tests/ConstraintGeneratorFixture.cpp b/tests/ConstraintGeneratorFixture.cpp index af7178d1..27a4b7de 100644 --- a/tests/ConstraintGeneratorFixture.cpp +++ b/tests/ConstraintGeneratorFixture.cpp @@ -10,9 +10,10 @@ namespace Luau ConstraintGeneratorFixture::ConstraintGeneratorFixture() : Fixture() , mainModule(new Module) - , simplifier(newSimplifier(NotNull{&arena}, builtinTypes)) + , simplifier(newSimplifier(NotNull{&arena}, getBuiltins())) , forceTheFlag{FFlag::LuauSolverV2, true} { + getFrontend(); // Force the frontend to exist in the constructor. mainModule->name = "MainModule"; mainModule->humanReadableName = "MainModule"; @@ -31,10 +32,10 @@ void ConstraintGeneratorFixture::generateConstraints(const std::string& code) NotNull{simplifier.get()}, NotNull{&typeFunctionRuntime}, NotNull(&moduleResolver), - builtinTypes, + getBuiltins(), NotNull(&ice), - frontend.globals.globalScope, - frontend.globals.globalTypeFunctionScope, + getFrontend().globals.globalScope, + getFrontend().globals.globalTypeFunctionScope, /*prepareModuleScope*/ nullptr, &logger, NotNull{dfg.get()}, @@ -60,8 +61,7 @@ void ConstraintGeneratorFixture::solve(const std::string& code) {}, &logger, NotNull{dfg.get()}, - {} - }; + {}}; cs.run(); } diff --git a/tests/ConstraintGeneratorFixture.h b/tests/ConstraintGeneratorFixture.h index 800bf873..57257eae 100644 --- a/tests/ConstraintGeneratorFixture.h +++ b/tests/ConstraintGeneratorFixture.h @@ -20,7 +20,7 @@ struct ConstraintGeneratorFixture : Fixture ModulePtr mainModule; DcrLogger logger; UnifierSharedState sharedState{&ice}; - Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; + Normalizer normalizer{&arena, getBuiltins(), NotNull{&sharedState}}; SimplifierPtr simplifier; TypeCheckLimits limits; TypeFunctionRuntime typeFunctionRuntime{NotNull{&ice}, NotNull{&limits}}; diff --git a/tests/EqSatSimplification.test.cpp b/tests/EqSatSimplification.test.cpp index 63998be3..ff741c0b 100644 --- a/tests/EqSatSimplification.test.cpp +++ b/tests/EqSatSimplification.test.cpp @@ -25,17 +25,17 @@ struct ESFixture : Fixture TypeId genericU = arena_.addType(GenericType{"U"}); TypeId numberToString = - arena_.addType(FunctionType{arena_.addTypePack({builtinTypes->numberType}), arena_.addTypePack({builtinTypes->stringType})}); + arena_.addType(FunctionType{arena_.addTypePack({getBuiltins()->numberType}), arena_.addTypePack({getBuiltins()->stringType})}); TypeId stringToNumber = - arena_.addType(FunctionType{arena_.addTypePack({builtinTypes->stringType}), arena_.addTypePack({builtinTypes->numberType})}); + arena_.addType(FunctionType{arena_.addTypePack({getBuiltins()->stringType}), arena_.addTypePack({getBuiltins()->numberType})}); ESFixture() - : simplifier(newSimplifier(arena, builtinTypes)) + : simplifier(newSimplifier(arena, getBuiltins())) { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); - ScopePtr moduleScope = frontend.globals.globalScope; + ScopePtr moduleScope = getFrontend().globals.globalScope; parentClass = moduleScope->linearSearchForBinding("Parent")->typeId; childClass = moduleScope->linearSearchForBinding("Child")->typeId; @@ -60,116 +60,116 @@ TEST_SUITE_BEGIN("EqSatSimplification"); TEST_CASE_FIXTURE(ESFixture, "primitive") { - CHECK("number" == simplifyStr(builtinTypes->numberType)); + CHECK("number" == simplifyStr(getBuiltins()->numberType)); } TEST_CASE_FIXTURE(ESFixture, "number | number") { - TypeId ty = arena->addType(UnionType{{builtinTypes->numberType, builtinTypes->numberType}}); + TypeId ty = arena->addType(UnionType{{getBuiltins()->numberType, getBuiltins()->numberType}}); CHECK("number" == simplifyStr(ty)); } TEST_CASE_FIXTURE(ESFixture, "number | string") { - CHECK("number | string" == simplifyStr(arena->addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType}}))); + CHECK("number | string" == simplifyStr(arena->addType(UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}}))); } TEST_CASE_FIXTURE(ESFixture, "t1 where t1 = number | t1") { - TypeId ty = arena->freshType(builtinTypes, nullptr); - asMutable(ty)->ty.emplace(std::vector{builtinTypes->numberType, ty}); + TypeId ty = arena->freshType(getBuiltins(), nullptr); + asMutable(ty)->ty.emplace(std::vector{getBuiltins()->numberType, ty}); CHECK("number" == simplifyStr(ty)); } TEST_CASE_FIXTURE(ESFixture, "number | string | number") { - TypeId ty = arena->addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType, builtinTypes->numberType}}); + TypeId ty = arena->addType(UnionType{{getBuiltins()->numberType, getBuiltins()->stringType, getBuiltins()->numberType}}); CHECK("number | string" == simplifyStr(ty)); } TEST_CASE_FIXTURE(ESFixture, "string | (number | string) | number") { - TypeId u1 = arena->addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType}}); - TypeId u2 = arena->addType(UnionType{{builtinTypes->stringType, u1, builtinTypes->numberType}}); + TypeId u1 = arena->addType(UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}}); + TypeId u2 = arena->addType(UnionType{{getBuiltins()->stringType, u1, getBuiltins()->numberType}}); CHECK("number | string" == simplifyStr(u2)); } TEST_CASE_FIXTURE(ESFixture, "string | any") { - CHECK("any" == simplifyStr(arena->addType(UnionType{{builtinTypes->stringType, builtinTypes->anyType}}))); + CHECK("any" == simplifyStr(arena->addType(UnionType{{getBuiltins()->stringType, getBuiltins()->anyType}}))); } TEST_CASE_FIXTURE(ESFixture, "any | string") { - CHECK("any" == simplifyStr(arena->addType(UnionType{{builtinTypes->anyType, builtinTypes->stringType}}))); + CHECK("any" == simplifyStr(arena->addType(UnionType{{getBuiltins()->anyType, getBuiltins()->stringType}}))); } TEST_CASE_FIXTURE(ESFixture, "any | never") { - CHECK("any" == simplifyStr(arena->addType(UnionType{{builtinTypes->anyType, builtinTypes->neverType}}))); + CHECK("any" == simplifyStr(arena->addType(UnionType{{getBuiltins()->anyType, getBuiltins()->neverType}}))); } TEST_CASE_FIXTURE(ESFixture, "string | unknown") { - CHECK("unknown" == simplifyStr(arena->addType(UnionType{{builtinTypes->stringType, builtinTypes->unknownType}}))); + CHECK("unknown" == simplifyStr(arena->addType(UnionType{{getBuiltins()->stringType, getBuiltins()->unknownType}}))); } TEST_CASE_FIXTURE(ESFixture, "unknown | string") { - CHECK("unknown" == simplifyStr(arena->addType(UnionType{{builtinTypes->unknownType, builtinTypes->stringType}}))); + CHECK("unknown" == simplifyStr(arena->addType(UnionType{{getBuiltins()->unknownType, getBuiltins()->stringType}}))); } TEST_CASE_FIXTURE(ESFixture, "unknown | never") { - CHECK("unknown" == simplifyStr(arena->addType(UnionType{{builtinTypes->unknownType, builtinTypes->neverType}}))); + CHECK("unknown" == simplifyStr(arena->addType(UnionType{{getBuiltins()->unknownType, getBuiltins()->neverType}}))); } TEST_CASE_FIXTURE(ESFixture, "string | never") { - CHECK("string" == simplifyStr(arena->addType(UnionType{{builtinTypes->stringType, builtinTypes->neverType}}))); + CHECK("string" == simplifyStr(arena->addType(UnionType{{getBuiltins()->stringType, getBuiltins()->neverType}}))); } TEST_CASE_FIXTURE(ESFixture, "string | never | number") { - CHECK("number | string" == simplifyStr(arena->addType(UnionType{{builtinTypes->stringType, builtinTypes->neverType, builtinTypes->numberType}}))); + CHECK("number | string" == simplifyStr(arena->addType(UnionType{{getBuiltins()->stringType, getBuiltins()->neverType, getBuiltins()->numberType}}))); } TEST_CASE_FIXTURE(ESFixture, "string & string") { - CHECK("string" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->stringType, builtinTypes->stringType}}))); + CHECK("string" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->stringType, getBuiltins()->stringType}}))); } TEST_CASE_FIXTURE(ESFixture, "string & number") { - CHECK("never" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->stringType, builtinTypes->numberType}}))); + CHECK("never" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->stringType, getBuiltins()->numberType}}))); } TEST_CASE_FIXTURE(ESFixture, "string & unknown") { - CHECK("string" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->stringType, builtinTypes->unknownType}}))); + CHECK("string" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->stringType, getBuiltins()->unknownType}}))); } TEST_CASE_FIXTURE(ESFixture, "never & string") { - CHECK("never" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->neverType, builtinTypes->stringType}}))); + CHECK("never" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->neverType, getBuiltins()->stringType}}))); } TEST_CASE_FIXTURE(ESFixture, "string & (unknown | never)") { CHECK( "string" == simplifyStr(arena->addType( - IntersectionType{{builtinTypes->stringType, arena->addType(UnionType{{builtinTypes->unknownType, builtinTypes->neverType}})}} + IntersectionType{{getBuiltins()->stringType, arena->addType(UnionType{{getBuiltins()->unknownType, getBuiltins()->neverType}})}} )) ); } TEST_CASE_FIXTURE(ESFixture, "true | false") { - CHECK("boolean" == simplifyStr(arena->addType(UnionType{{builtinTypes->trueType, builtinTypes->falseType}}))); + CHECK("boolean" == simplifyStr(arena->addType(UnionType{{getBuiltins()->trueType, getBuiltins()->falseType}}))); } /* @@ -185,9 +185,9 @@ TEST_CASE_FIXTURE(ESFixture, "true | false") TEST_CASE_FIXTURE(ESFixture, "t1 where t1 = string & (number | t1)") { TypeId intersectionTy = arena->addType(BlockedType{}); - TypeId unionTy = arena->addType(UnionType{{builtinTypes->numberType, intersectionTy}}); + TypeId unionTy = arena->addType(UnionType{{getBuiltins()->numberType, intersectionTy}}); - asMutable(intersectionTy)->ty.emplace(std::vector{builtinTypes->stringType, unionTy}); + asMutable(intersectionTy)->ty.emplace(std::vector{getBuiltins()->stringType, unionTy}); CHECK("string" == simplifyStr(intersectionTy)); } @@ -195,21 +195,21 @@ TEST_CASE_FIXTURE(ESFixture, "t1 where t1 = string & (number | t1)") TEST_CASE_FIXTURE(ESFixture, "t1 where t1 = string & (unknown | t1)") { TypeId intersectionTy = arena->addType(BlockedType{}); - TypeId unionTy = arena->addType(UnionType{{builtinTypes->unknownType, intersectionTy}}); + TypeId unionTy = arena->addType(UnionType{{getBuiltins()->unknownType, intersectionTy}}); - asMutable(intersectionTy)->ty.emplace(std::vector{builtinTypes->stringType, unionTy}); + asMutable(intersectionTy)->ty.emplace(std::vector{getBuiltins()->stringType, unionTy}); CHECK("string" == simplifyStr(intersectionTy)); } TEST_CASE_FIXTURE(ESFixture, "error | unknown") { - CHECK("any" == simplifyStr(arena->addType(UnionType{{builtinTypes->errorType, builtinTypes->unknownType}}))); + CHECK("any" == simplifyStr(arena->addType(UnionType{{getBuiltins()->errorType, getBuiltins()->unknownType}}))); } TEST_CASE_FIXTURE(ESFixture, "\"hello\" | string") { - CHECK("string" == simplifyStr(arena->addType(UnionType{{arena->addType(SingletonType{StringSingleton{"hello"}}), builtinTypes->stringType}}))); + CHECK("string" == simplifyStr(arena->addType(UnionType{{arena->addType(SingletonType{StringSingleton{"hello"}}), getBuiltins()->stringType}}))); } TEST_CASE_FIXTURE(ESFixture, "\"hello\" | \"world\" | \"hello\"") @@ -227,22 +227,22 @@ TEST_CASE_FIXTURE(ESFixture, "nil | boolean | number | string | thread | functio { CHECK( "unknown" == simplifyStr(arena->addType(UnionType{{ - builtinTypes->nilType, - builtinTypes->booleanType, - builtinTypes->numberType, - builtinTypes->stringType, - builtinTypes->threadType, - builtinTypes->functionType, - builtinTypes->tableType, - builtinTypes->externType, - builtinTypes->bufferType, + getBuiltins()->nilType, + getBuiltins()->booleanType, + getBuiltins()->numberType, + getBuiltins()->stringType, + getBuiltins()->threadType, + getBuiltins()->functionType, + getBuiltins()->tableType, + getBuiltins()->externType, + getBuiltins()->bufferType, }})) ); } TEST_CASE_FIXTURE(ESFixture, "Parent & number") { - CHECK("never" == simplifyStr(arena->addType(IntersectionType{{parentClass, builtinTypes->numberType}}))); + CHECK("never" == simplifyStr(arena->addType(IntersectionType{{parentClass, getBuiltins()->numberType}}))); } TEST_CASE_FIXTURE(ESFixture, "Child & Parent") @@ -262,12 +262,12 @@ TEST_CASE_FIXTURE(ESFixture, "Child | Parent") TEST_CASE_FIXTURE(ESFixture, "class | Child") { - CHECK("class" == simplifyStr(arena->addType(UnionType{{builtinTypes->externType, childClass}}))); + CHECK("class" == simplifyStr(arena->addType(UnionType{{getBuiltins()->externType, childClass}}))); } TEST_CASE_FIXTURE(ESFixture, "Parent | class | Child") { - CHECK("class" == simplifyStr(arena->addType(UnionType{{parentClass, builtinTypes->externType, childClass}}))); + CHECK("class" == simplifyStr(arena->addType(UnionType{{parentClass, getBuiltins()->externType, childClass}}))); } TEST_CASE_FIXTURE(ESFixture, "Parent | Unrelated") @@ -277,16 +277,16 @@ TEST_CASE_FIXTURE(ESFixture, "Parent | Unrelated") TEST_CASE_FIXTURE(ESFixture, "never | Parent | Unrelated") { - CHECK("Parent | Unrelated" == simplifyStr(arena->addType(UnionType{{builtinTypes->neverType, parentClass, unrelatedClass}}))); + CHECK("Parent | Unrelated" == simplifyStr(arena->addType(UnionType{{getBuiltins()->neverType, parentClass, unrelatedClass}}))); } TEST_CASE_FIXTURE(ESFixture, "never | Parent | (number & string) | Unrelated") { CHECK( "Parent | Unrelated" == simplifyStr(arena->addType(UnionType{ - {builtinTypes->neverType, + {getBuiltins()->neverType, parentClass, - arena->addType(IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}), + arena->addType(IntersectionType{{getBuiltins()->numberType, getBuiltins()->stringType}}), unrelatedClass} })) ); @@ -299,33 +299,33 @@ TEST_CASE_FIXTURE(ESFixture, "T & U") TEST_CASE_FIXTURE(ESFixture, "boolean & true") { - CHECK("true" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->booleanType, builtinTypes->trueType}}))); + CHECK("true" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->booleanType, getBuiltins()->trueType}}))); } TEST_CASE_FIXTURE(ESFixture, "boolean & (true | number | string | thread | function | table | class | buffer)") { TypeId truthy = arena->addType(UnionType{{ - builtinTypes->trueType, - builtinTypes->numberType, - builtinTypes->stringType, - builtinTypes->threadType, - builtinTypes->functionType, - builtinTypes->tableType, - builtinTypes->externType, - builtinTypes->bufferType, + getBuiltins()->trueType, + getBuiltins()->numberType, + getBuiltins()->stringType, + getBuiltins()->threadType, + getBuiltins()->functionType, + getBuiltins()->tableType, + getBuiltins()->externType, + getBuiltins()->bufferType, }}); - CHECK("true" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->booleanType, truthy}}))); + CHECK("true" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->booleanType, truthy}}))); } TEST_CASE_FIXTURE(ESFixture, "boolean & ~(false?)") { - CHECK("true" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->booleanType, builtinTypes->truthyType}}))); + CHECK("true" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->booleanType, getBuiltins()->truthyType}}))); } TEST_CASE_FIXTURE(ESFixture, "false & ~(false?)") { - CHECK("never" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->falseType, builtinTypes->truthyType}}))); + CHECK("never" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->falseType, getBuiltins()->truthyType}}))); } TEST_CASE_FIXTURE(ESFixture, "(number) -> string & (number) -> string") @@ -340,28 +340,28 @@ TEST_CASE_FIXTURE(ESFixture, "(number) -> string | (number) -> string") TEST_CASE_FIXTURE(ESFixture, "(number) -> string & function") { - CHECK("(number) -> string" == simplifyStr(arena->addType(IntersectionType{{numberToString, builtinTypes->functionType}}))); + CHECK("(number) -> string" == simplifyStr(arena->addType(IntersectionType{{numberToString, getBuiltins()->functionType}}))); } TEST_CASE_FIXTURE(ESFixture, "(number) -> string & boolean") { - CHECK("never" == simplifyStr(arena->addType(IntersectionType{{numberToString, builtinTypes->booleanType}}))); + CHECK("never" == simplifyStr(arena->addType(IntersectionType{{numberToString, getBuiltins()->booleanType}}))); } TEST_CASE_FIXTURE(ESFixture, "(number) -> string & string") { - CHECK("never" == simplifyStr(arena->addType(IntersectionType{{numberToString, builtinTypes->stringType}}))); + CHECK("never" == simplifyStr(arena->addType(IntersectionType{{numberToString, getBuiltins()->stringType}}))); } TEST_CASE_FIXTURE(ESFixture, "(number) -> string & ~function") { - TypeId notFunction = arena->addType(NegationType{builtinTypes->functionType}); + TypeId notFunction = arena->addType(NegationType{getBuiltins()->functionType}); CHECK("never" == simplifyStr(arena->addType(IntersectionType{{numberToString, notFunction}}))); } TEST_CASE_FIXTURE(ESFixture, "(number) -> string | function") { - CHECK("function" == simplifyStr(arena->addType(UnionType{{numberToString, builtinTypes->functionType}}))); + CHECK("function" == simplifyStr(arena->addType(UnionType{{numberToString, getBuiltins()->functionType}}))); } TEST_CASE_FIXTURE(ESFixture, "(number) -> string & (string) -> number") @@ -378,7 +378,7 @@ TEST_CASE_FIXTURE(ESFixture, "add") { CHECK( "number" == - simplifyStr(arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().addFunc, {builtinTypes->numberType, builtinTypes->numberType}})) + simplifyStr(arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().addFunc, {getBuiltins()->numberType, getBuiltins()->numberType}})) ); } @@ -386,14 +386,14 @@ TEST_CASE_FIXTURE(ESFixture, "union") { CHECK( "number" == - simplifyStr(arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().unionFunc, {builtinTypes->numberType, builtinTypes->numberType}})) + simplifyStr(arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().unionFunc, {getBuiltins()->numberType, getBuiltins()->numberType}})) ); } TEST_CASE_FIXTURE(ESFixture, "never & ~string") { CHECK( - "never" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->neverType, arena->addType(NegationType{builtinTypes->stringType})}})) + "never" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->neverType, arena->addType(NegationType{getBuiltins()->stringType})}})) ); } @@ -401,15 +401,15 @@ TEST_CASE_FIXTURE(ESFixture, "blocked & never") { const TypeId blocked = arena->addType(BlockedType{}); - CHECK("never" == simplifyStr(arena->addType(IntersectionType{{blocked, builtinTypes->neverType}}))); + CHECK("never" == simplifyStr(arena->addType(IntersectionType{{blocked, getBuiltins()->neverType}}))); } TEST_CASE_FIXTURE(ESFixture, "blocked & ~number & function") { const TypeId blocked = arena->addType(BlockedType{}); - const TypeId notNumber = arena->addType(NegationType{builtinTypes->numberType}); + const TypeId notNumber = arena->addType(NegationType{getBuiltins()->numberType}); - const TypeId ty = arena->addType(IntersectionType{{blocked, notNumber, builtinTypes->functionType}}); + const TypeId ty = arena->addType(IntersectionType{{blocked, notNumber, getBuiltins()->functionType}}); std::string expected = toString(blocked) + " & function"; @@ -419,24 +419,24 @@ TEST_CASE_FIXTURE(ESFixture, "blocked & ~number & function") TEST_CASE_FIXTURE(ESFixture, "(number | boolean | string | nil | table) & (false | nil)") { const TypeId t1 = arena->addType( - UnionType{{builtinTypes->numberType, builtinTypes->booleanType, builtinTypes->stringType, builtinTypes->nilType, builtinTypes->tableType}} + UnionType{{getBuiltins()->numberType, getBuiltins()->booleanType, getBuiltins()->stringType, getBuiltins()->nilType, getBuiltins()->tableType}} ); - CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, builtinTypes->falsyType}}))); + CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, getBuiltins()->falsyType}}))); } TEST_CASE_FIXTURE(ESFixture, "(number | boolean | nil) & (false | nil)") { - const TypeId t1 = arena->addType(UnionType{{builtinTypes->numberType, builtinTypes->booleanType, builtinTypes->nilType}}); + const TypeId t1 = arena->addType(UnionType{{getBuiltins()->numberType, getBuiltins()->booleanType, getBuiltins()->nilType}}); - CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, builtinTypes->falsyType}}))); + CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, getBuiltins()->falsyType}}))); } TEST_CASE_FIXTURE(ESFixture, "(boolean | nil) & (false | nil)") { - const TypeId t1 = arena->addType(UnionType{{builtinTypes->booleanType, builtinTypes->nilType}}); + const TypeId t1 = arena->addType(UnionType{{getBuiltins()->booleanType, getBuiltins()->nilType}}); - CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, builtinTypes->falsyType}}))); + CHECK("false?" == simplifyStr(arena->addType(IntersectionType{{t1, getBuiltins()->falsyType}}))); } // (('a & false) | ('a & nil)) | number @@ -450,16 +450,16 @@ TEST_CASE_FIXTURE(ESFixture, "(boolean | nil) & (false | nil)") TEST_CASE_FIXTURE(ESFixture, "free & string & number") { - Scope scope{builtinTypes->anyTypePack}; - const TypeId freeTy = arena->freshType(builtinTypes, &scope); + Scope scope{getBuiltins()->anyTypePack}; + const TypeId freeTy = arena->freshType(getBuiltins(), &scope); - CHECK("never" == simplifyStr(arena->addType(IntersectionType{{freeTy, builtinTypes->numberType, builtinTypes->stringType}}))); + CHECK("never" == simplifyStr(arena->addType(IntersectionType{{freeTy, getBuiltins()->numberType, getBuiltins()->stringType}}))); } TEST_CASE_FIXTURE(ESFixture, "(blocked & number) | (blocked & number)") { const TypeId blocked = arena->addType(BlockedType{}); - const TypeId u = arena->addType(IntersectionType{{blocked, builtinTypes->numberType}}); + const TypeId u = arena->addType(IntersectionType{{blocked, getBuiltins()->numberType}}); const TypeId ty = arena->addType(UnionType{{u, u}}); const std::string blockedStr = toString(blocked); @@ -469,23 +469,23 @@ TEST_CASE_FIXTURE(ESFixture, "(blocked & number) | (blocked & number)") TEST_CASE_FIXTURE(ESFixture, "{} & unknown") { - CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), builtinTypes->unknownType}}))); + CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), getBuiltins()->unknownType}}))); } TEST_CASE_FIXTURE(ESFixture, "{} & table") { - CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), builtinTypes->tableType}}))); + CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), getBuiltins()->tableType}}))); } TEST_CASE_FIXTURE(ESFixture, "{} & ~(false?)") { - CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), builtinTypes->truthyType}}))); + CHECK("{ }" == simplifyStr(arena->addType(IntersectionType{{tbl({}), getBuiltins()->truthyType}}))); } TEST_CASE_FIXTURE(ESFixture, "{x: number?} & {x: number}") { - const TypeId hasOptionalX = tbl({{"x", builtinTypes->optionalNumberType}}); - const TypeId hasX = tbl({{"x", builtinTypes->numberType}}); + const TypeId hasOptionalX = tbl({{"x", getBuiltins()->optionalNumberType}}); + const TypeId hasX = tbl({{"x", getBuiltins()->numberType}}); const TypeId ty = arena->addType(IntersectionType{{hasOptionalX, hasX}}); auto res = eqSatSimplify(NotNull{simplifier.get()}, ty); @@ -498,8 +498,8 @@ TEST_CASE_FIXTURE(ESFixture, "{x: number?} & {x: number}") TEST_CASE_FIXTURE(ESFixture, "{x: number?} & {x: ~(false?)}") { - const TypeId hasOptionalX = tbl({{"x", builtinTypes->optionalNumberType}}); - const TypeId hasX = tbl({{"x", builtinTypes->truthyType}}); + const TypeId hasOptionalX = tbl({{"x", getBuiltins()->optionalNumberType}}); + const TypeId hasX = tbl({{"x", getBuiltins()->truthyType}}); const TypeId ty = arena->addType(IntersectionType{{hasOptionalX, hasX}}); auto res = eqSatSimplify(NotNull{simplifier.get()}, ty); @@ -510,10 +510,10 @@ TEST_CASE_FIXTURE(ESFixture, "{x: number?} & {x: ~(false?)}") TEST_CASE_FIXTURE(ESFixture, "(({ x: number? }?) & { x: ~(false?) }") { // {x: number?}? - const TypeId xWithOptionalNumber = arena->addType(UnionType{{tbl({{"x", builtinTypes->optionalNumberType}}), builtinTypes->nilType}}); + const TypeId xWithOptionalNumber = arena->addType(UnionType{{tbl({{"x", getBuiltins()->optionalNumberType}}), getBuiltins()->nilType}}); // {x: ~(false?)} - const TypeId xWithTruthy = tbl({{"x", builtinTypes->truthyType}}); + const TypeId xWithTruthy = tbl({{"x", getBuiltins()->truthyType}}); const TypeId ty = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy}}); @@ -523,15 +523,15 @@ TEST_CASE_FIXTURE(ESFixture, "(({ x: number? }?) & { x: ~(false?) }") TEST_CASE_FIXTURE(ESFixture, "never | (({ x: number? }?) & { x: ~(false?) })") { // {x: number?}? - const TypeId xWithOptionalNumber = arena->addType(UnionType{{tbl({{"x", builtinTypes->optionalNumberType}}), builtinTypes->nilType}}); + const TypeId xWithOptionalNumber = arena->addType(UnionType{{tbl({{"x", getBuiltins()->optionalNumberType}}), getBuiltins()->nilType}}); // {x: ~(false?)} - const TypeId xWithTruthy = tbl({{"x", builtinTypes->truthyType}}); + const TypeId xWithTruthy = tbl({{"x", getBuiltins()->truthyType}}); // ({x: number?}?) & {x: ~(false?)} const TypeId intersectionTy = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy}}); - const TypeId ty = arena->addType(UnionType{{builtinTypes->neverType, intersectionTy}}); + const TypeId ty = arena->addType(UnionType{{getBuiltins()->neverType, intersectionTy}}); CHECK("{ x: number }" == simplifyStr(ty)); } @@ -539,13 +539,13 @@ TEST_CASE_FIXTURE(ESFixture, "never | (({ x: number? }?) & { x: ~(false?) })") TEST_CASE_FIXTURE(ESFixture, "({ x: number? }?) & { x: ~(false?) } & ~(false?)") { // {x: number?}? - const TypeId xWithOptionalNumber = arena->addType(UnionType{{tbl({{"x", builtinTypes->optionalNumberType}}), builtinTypes->nilType}}); + const TypeId xWithOptionalNumber = arena->addType(UnionType{{tbl({{"x", getBuiltins()->optionalNumberType}}), getBuiltins()->nilType}}); // {x: ~(false?)} - const TypeId xWithTruthy = tbl({{"x", builtinTypes->truthyType}}); + const TypeId xWithTruthy = tbl({{"x", getBuiltins()->truthyType}}); // ({x: number?}?) & {x: ~(false?)} & ~(false?) - const TypeId intersectionTy = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy, builtinTypes->truthyType}}); + const TypeId intersectionTy = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy, getBuiltins()->truthyType}}); CHECK("{ x: number }" == simplifyStr(intersectionTy)); } @@ -555,10 +555,10 @@ TEST_CASE_FIXTURE(ESFixture, "({ x: number? }?) & { x: ~(false?) } & ~(false?)") TEST_CASE_FIXTURE(ESFixture, "(({ x: number? }?) & { x: ~(false?) } & ~(false?)) | number") { // ({ x: number? }?) & { x: ~(false?) } & ~(false?) - const TypeId xWithOptionalNumber = tbl({{"x", builtinTypes->optionalNumberType}}); - const TypeId xWithTruthy = tbl({{"x", builtinTypes->truthyType}}); - const TypeId intersectionTy = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy, builtinTypes->truthyType}}); - const TypeId ty = arena->addType(UnionType{{intersectionTy, builtinTypes->numberType}}); + const TypeId xWithOptionalNumber = tbl({{"x", getBuiltins()->optionalNumberType}}); + const TypeId xWithTruthy = tbl({{"x", getBuiltins()->truthyType}}); + const TypeId intersectionTy = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy, getBuiltins()->truthyType}}); + const TypeId ty = arena->addType(UnionType{{intersectionTy, getBuiltins()->numberType}}); CHECK("{ x: number } | number" == simplifyStr(ty)); } @@ -566,22 +566,22 @@ TEST_CASE_FIXTURE(ESFixture, "(({ x: number? }?) & { x: ~(false?) } & ~(false?)) TEST_CASE_FIXTURE(ESFixture, "number & no-refine") { - CHECK("number" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->numberType, builtinTypes->noRefineType}}))); + CHECK("number" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->numberType, getBuiltins()->noRefineType}}))); } TEST_CASE_FIXTURE(ESFixture, "{ x: number } & ~boolean") { - const TypeId tblTy = tbl(TableType::Props{{"x", builtinTypes->numberType}}); + const TypeId tblTy = tbl(TableType::Props{{"x", getBuiltins()->numberType}}); - const TypeId ty = arena->addType(IntersectionType{{tblTy, arena->addType(NegationType{builtinTypes->booleanType})}}); + const TypeId ty = arena->addType(IntersectionType{{tblTy, arena->addType(NegationType{getBuiltins()->booleanType})}}); CHECK("{ x: number }" == simplifyStr(ty)); } TEST_CASE_FIXTURE(ESFixture, "(nil & string)?") { - const TypeId nilAndString = arena->addType(IntersectionType{{builtinTypes->nilType, builtinTypes->stringType}}); - const TypeId ty = arena->addType(UnionType{{nilAndString, builtinTypes->nilType}}); + const TypeId nilAndString = arena->addType(IntersectionType{{getBuiltins()->nilType, getBuiltins()->stringType}}); + const TypeId ty = arena->addType(UnionType{{nilAndString, getBuiltins()->nilType}}); CHECK("nil" == simplifyStr(ty)); } @@ -590,7 +590,7 @@ TEST_CASE_FIXTURE(ESFixture, "string & \"hi\"") { const TypeId hi = arena->addType(SingletonType{StringSingleton{"hi"}}); - CHECK("\"hi\"" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->stringType, hi}}))); + CHECK("\"hi\"" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->stringType, hi}}))); } TEST_CASE_FIXTURE(ESFixture, "string & (\"hi\" | \"bye\")") @@ -598,7 +598,7 @@ TEST_CASE_FIXTURE(ESFixture, "string & (\"hi\" | \"bye\")") const TypeId hi = arena->addType(SingletonType{StringSingleton{"hi"}}); const TypeId bye = arena->addType(SingletonType{StringSingleton{"bye"}}); - CHECK("\"bye\" | \"hi\"" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->stringType, arena->addType(UnionType{{hi, bye}})}}))); + CHECK("\"bye\" | \"hi\"" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->stringType, arena->addType(UnionType{{hi, bye}})}}))); } TEST_CASE_FIXTURE(ESFixture, "(\"err\" | \"ok\") & ~\"ok\"") @@ -622,7 +622,7 @@ TEST_CASE_FIXTURE(ESFixture, "(Child | Unrelated) & ~Child") TEST_CASE_FIXTURE(ESFixture, "string & ~Child") { - CHECK("string" == simplifyStr(arena->addType(IntersectionType{{builtinTypes->stringType, arena->addType(NegationType{childClass})}}))); + CHECK("string" == simplifyStr(arena->addType(IntersectionType{{getBuiltins()->stringType, arena->addType(NegationType{childClass})}}))); } TEST_CASE_FIXTURE(ESFixture, "(Child | Unrelated) & Child") @@ -637,22 +637,22 @@ TEST_CASE_FIXTURE(ESFixture, "(Child | AnotherChild) & ~Child") TEST_CASE_FIXTURE(ESFixture, "{ tag: \"Part\", x: never }") { - const TypeId ty = tbl({{"tag", arena->addType(SingletonType{StringSingleton{"Part"}})}, {"x", builtinTypes->neverType}}); + const TypeId ty = tbl({{"tag", arena->addType(SingletonType{StringSingleton{"Part"}})}, {"x", getBuiltins()->neverType}}); CHECK("never" == simplifyStr(ty)); } TEST_CASE_FIXTURE(ESFixture, "{ tag: \"Part\", x: number? } & { x: string }") { - const TypeId leftTable = tbl({{"tag", arena->addType(SingletonType{StringSingleton{"Part"}})}, {"x", builtinTypes->optionalNumberType}}); - const TypeId rightTable = tbl({{"x", builtinTypes->stringType}}); + const TypeId leftTable = tbl({{"tag", arena->addType(SingletonType{StringSingleton{"Part"}})}, {"x", getBuiltins()->optionalNumberType}}); + const TypeId rightTable = tbl({{"x", getBuiltins()->stringType}}); CHECK("never" == simplifyStr(arena->addType(IntersectionType{{leftTable, rightTable}}))); } TEST_CASE_FIXTURE(ESFixture, "Child & add") { - const TypeId u = arena->addType(UnionType{{childClass, anotherChild, builtinTypes->stringType}}); + const TypeId u = arena->addType(UnionType{{childClass, anotherChild, getBuiltins()->stringType}}); const TypeId intersectTf = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().addFunc, {u, parentClass}, {}}); const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}}); @@ -662,7 +662,7 @@ TEST_CASE_FIXTURE(ESFixture, "Child & add TEST_CASE_FIXTURE(ESFixture, "Child & intersect") { - const TypeId u = arena->addType(UnionType{{childClass, anotherChild, builtinTypes->stringType}}); + const TypeId u = arena->addType(UnionType{{childClass, anotherChild, getBuiltins()->stringType}}); const TypeId intersectTf = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().intersectFunc, {u, parentClass}, {}}); const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}}); @@ -673,10 +673,10 @@ TEST_CASE_FIXTURE(ESFixture, "Child & intersect == boolean") { std::vector> cases{ - {builtinTypes->numberType, arena->addType(BlockedType{})}, - {builtinTypes->stringType, arena->addType(BlockedType{})}, - {arena->addType(BlockedType{}), builtinTypes->numberType}, - {arena->addType(BlockedType{}), builtinTypes->stringType}, + {getBuiltins()->numberType, arena->addType(BlockedType{})}, + {getBuiltins()->stringType, arena->addType(BlockedType{})}, + {arena->addType(BlockedType{}), getBuiltins()->numberType}, + {arena->addType(BlockedType{}), getBuiltins()->stringType}, }; for (const auto& [lhs, rhs] : cases) @@ -689,7 +689,7 @@ TEST_CASE_FIXTURE(ESFixture, "lt == boolean") TEST_CASE_FIXTURE(ESFixture, "unknown & ~string") { CHECK_EQ( - "~string", simplifyStr(arena->addType(IntersectionType{{builtinTypes->unknownType, arena->addType(NegationType{builtinTypes->stringType})}})) + "~string", simplifyStr(arena->addType(IntersectionType{{getBuiltins()->unknownType, arena->addType(NegationType{getBuiltins()->stringType})}})) ); } @@ -698,7 +698,7 @@ TEST_CASE_FIXTURE(ESFixture, "string & ~\"foo\"") CHECK_EQ( "string & ~\"foo\"", simplifyStr(arena->addType( - IntersectionType{{builtinTypes->stringType, arena->addType(NegationType{arena->addType(SingletonType{StringSingleton{"foo"}})})}} + IntersectionType{{getBuiltins()->stringType, arena->addType(NegationType{arena->addType(SingletonType{StringSingleton{"foo"}})})}} )) ); } diff --git a/tests/Error.test.cpp b/tests/Error.test.cpp index 15c317fc..87a07b20 100644 --- a/tests/Error.test.cpp +++ b/tests/Error.test.cpp @@ -18,7 +18,7 @@ TEST_CASE("TypeError_code_should_return_nonzero_code") TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_names_show_instead_of_tables") { - frontend.options.retainFullTypeGraphs = false; + getFrontend().options.retainFullTypeGraphs = false; CheckResult result = check(R"( --!strict @@ -38,7 +38,7 @@ local x: Account = 5 TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_function_errors") { - frontend.options.retainFullTypeGraphs = false; + getFrontend().options.retainFullTypeGraphs = false; CheckResult result = check(R"( --!strict @@ -58,7 +58,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_function_errors") TEST_CASE_FIXTURE(BuiltinsFixture, "unary_op_type_function_errors") { - frontend.options.retainFullTypeGraphs = false; + getFrontend().options.retainFullTypeGraphs = false; CheckResult result = check(R"( --!strict diff --git a/tests/Fixture.cpp b/tests/Fixture.cpp index b3c7c776..060a32f2 100644 --- a/tests/Fixture.cpp +++ b/tests/Fixture.cpp @@ -257,37 +257,8 @@ const Config& TestConfigResolver::getConfig(const ModuleName& name) const } Fixture::Fixture(bool prepareAutocomplete) - : frontend( - &fileResolver, - &configResolver, - {/* retainFullTypeGraphs= */ true, /* forAutocomplete */ false, /* runLintChecks */ false, /* randomConstraintResolutionSeed */ randomSeed} - ) - , builtinTypes(frontend.builtinTypes) + : forAutocomplete(prepareAutocomplete) { - configResolver.defaultConfig.mode = Mode::Strict; - configResolver.defaultConfig.enabledLint.warningMask = ~0ull; - configResolver.defaultConfig.parseOptions.captureComments = true; - - Luau::freeze(frontend.globals.globalTypes); - Luau::freeze(frontend.globalsForAutocomplete.globalTypes); - - Luau::setPrintLine([](auto s) {}); - - if (FFlag::DebugLuauLogSolverToJsonFile) - { - frontend.writeJsonLog = [&](const Luau::ModuleName& moduleName, std::string log) - { - std::string path = moduleName + ".log.json"; - size_t pos = moduleName.find_last_of('/'); - if (pos != std::string::npos) - path = moduleName.substr(pos + 1); - - std::ofstream os(path); - - os << log << std::endl; - MESSAGE("Wrote JSON log to ", path); - }; - } } Fixture::~Fixture() @@ -318,27 +289,27 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars *sourceModule, mode, {}, - builtinTypes, + getBuiltins(), NotNull{&ice}, NotNull{&moduleResolver}, NotNull{&fileResolver}, - frontend.globals.globalScope, - frontend.globals.globalTypeFunctionScope, + getFrontend().globals.globalScope, + getFrontend().globals.globalTypeFunctionScope, /*prepareModuleScope*/ nullptr, - frontend.options, + getFrontend().options, {}, false, {} ); - Luau::lint(sourceModule->root, *sourceModule->names, frontend.globals.globalScope, module.get(), sourceModule->hotcomments, {}); + Luau::lint(sourceModule->root, *sourceModule->names, getFrontend().globals.globalScope, module.get(), sourceModule->hotcomments, {}); } else { - TypeChecker typeChecker(frontend.globals.globalScope, &moduleResolver, builtinTypes, &frontend.iceHandler); + TypeChecker typeChecker(getFrontend().globals.globalScope, &moduleResolver, getBuiltins(), &getFrontend().iceHandler); ModulePtr module = typeChecker.check(*sourceModule, sourceModule->mode.value_or(Luau::Mode::Nonstrict), std::nullopt); - Luau::lint(sourceModule->root, *sourceModule->names, frontend.globals.globalScope, module.get(), sourceModule->hotcomments, {}); + Luau::lint(sourceModule->root, *sourceModule->names, getFrontend().globals.globalScope, module.get(), sourceModule->hotcomments, {}); } } @@ -350,12 +321,14 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars CheckResult Fixture::check(Mode mode, const std::string& source, std::optional options) { + // Force the frontend here + getFrontend(); ModuleName mm = fromString(mainModuleName); configResolver.defaultConfig.mode = mode; fileResolver.source[mm] = std::move(source); - frontend.markDirty(mm); + getFrontend().markDirty(mm); - CheckResult result = frontend.check(mm, options); + CheckResult result = getFrontend().check(mm, options); return result; } @@ -370,18 +343,18 @@ LintResult Fixture::lint(const std::string& source, const std::optional& lintOptions) { - FrontendOptions options = frontend.options; + FrontendOptions options = getFrontend().options; options.runLintChecks = true; options.enabledLintWarnings = lintOptions; - CheckResult result = frontend.check(moduleName, options); + CheckResult result = getFrontend().check(moduleName, options); return result.lintResult; } @@ -450,14 +423,14 @@ ParseResult Fixture::matchParseErrorPrefix(const std::string& source, const std: ModulePtr Fixture::getMainModule(bool forAutocomplete) { if (forAutocomplete && !FFlag::LuauSolverV2) - return frontend.moduleResolverForAutocomplete.getModule(fromString(mainModuleName)); + return getFrontend().moduleResolverForAutocomplete.getModule(fromString(mainModuleName)); - return frontend.moduleResolver.getModule(fromString(mainModuleName)); + return getFrontend().moduleResolver.getModule(fromString(mainModuleName)); } SourceModule* Fixture::getMainSourceModule() { - return frontend.getSourceModule(fromString(mainModuleName)); + return getFrontend().getSourceModule(fromString(mainModuleName)); } std::optional Fixture::getPrimitiveType(TypeId ty) @@ -497,7 +470,7 @@ TypeId Fixture::requireType(const std::string& name) TypeId Fixture::requireType(const ModuleName& moduleName, const std::string& name) { - ModulePtr module = frontend.moduleResolver.getModule(moduleName); + ModulePtr module = getFrontend().moduleResolver.getModule(moduleName); REQUIRE(module); return requireType(module, name); } @@ -573,7 +546,7 @@ TypeId Fixture::requireTypeAlias(const std::string& name) TypeId Fixture::requireExportedType(const ModuleName& moduleName, const std::string& name) { - ModulePtr module = frontend.moduleResolver.getModule(moduleName); + ModulePtr module = getFrontend().moduleResolver.getModule(moduleName); REQUIRE(module); auto it = module->exportedTypeBindings.find(name); @@ -586,10 +559,10 @@ std::string Fixture::decorateWithTypes(const std::string& code) { fileResolver.source[mainModuleName] = code; - Luau::CheckResult typeInfo = frontend.check(mainModuleName); + Luau::CheckResult typeInfo = getFrontend().check(mainModuleName); - SourceModule* sourceModule = frontend.getSourceModule(mainModuleName); - attachTypeData(*sourceModule, *frontend.moduleResolver.getModule(mainModuleName)); + SourceModule* sourceModule = getFrontend().getSourceModule(mainModuleName); + attachTypeData(*sourceModule, *getFrontend().moduleResolver.getModule(mainModuleName)); return transpileWithTypes(*sourceModule->root); } @@ -621,9 +594,9 @@ void Fixture::dumpErrors(std::ostream& os, const std::vector& errors) void Fixture::registerTestTypes() { - addGlobalBinding(frontend.globals, "game", builtinTypes->anyType, "@luau"); - addGlobalBinding(frontend.globals, "workspace", builtinTypes->anyType, "@luau"); - addGlobalBinding(frontend.globals, "script", builtinTypes->anyType, "@luau"); + addGlobalBinding(getFrontend().globals, "game", getBuiltins()->anyType, "@luau"); + addGlobalBinding(getFrontend().globals, "workspace", getBuiltins()->anyType, "@luau"); + addGlobalBinding(getFrontend().globals, "script", getBuiltins()->anyType, "@luau"); } void Fixture::dumpErrors(const CheckResult& cr) @@ -682,9 +655,9 @@ void Fixture::validateErrors(const std::vector& errors) LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool forAutocomplete) { - GlobalTypes& globals = forAutocomplete ? frontend.globalsForAutocomplete : frontend.globals; + GlobalTypes& globals = forAutocomplete ? getFrontend().globalsForAutocomplete : getFrontend().globals; unfreeze(globals.globalTypes); - LoadDefinitionFileResult result = frontend.loadDefinitionFile( + LoadDefinitionFileResult result = getFrontend().loadDefinitionFile( globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typecheckForAutocomplete */ forAutocomplete ); freeze(globals.globalTypes); @@ -695,19 +668,80 @@ LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool return result; } +NotNull Fixture::getBuiltins() +{ + if (!builtinTypes) + getFrontend(); + return NotNull{builtinTypes}; +} + +Frontend& Fixture::getFrontend() +{ + if (frontend) + return *frontend; + + Frontend& f = frontend.emplace( + &fileResolver, + &configResolver, + FrontendOptions{ + /* retainFullTypeGraphs= */ true, /* forAutocomplete */ false, /* runLintChecks */ false, /* randomConstraintResolutionSeed */ randomSeed} + ); + + builtinTypes = f.builtinTypes; + // Fixture::Fixture begins here + configResolver.defaultConfig.mode = Mode::Strict; + configResolver.defaultConfig.enabledLint.warningMask = ~0ull; + configResolver.defaultConfig.parseOptions.captureComments = true; + + Luau::freeze(f.globals.globalTypes); + Luau::freeze(f.globalsForAutocomplete.globalTypes); + + Luau::setPrintLine([](auto s) {}); + + if (FFlag::DebugLuauLogSolverToJsonFile) + { + f.writeJsonLog = [&](const Luau::ModuleName& moduleName, std::string log) + { + std::string path = moduleName + ".log.json"; + size_t pos = moduleName.find_last_of('/'); + if (pos != std::string::npos) + path = moduleName.substr(pos + 1); + + std::ofstream os(path); + + os << log << std::endl; + MESSAGE("Wrote JSON log to ", path); + }; + } + return *frontend; +} + + BuiltinsFixture::BuiltinsFixture(bool prepareAutocomplete) : Fixture(prepareAutocomplete) { - Luau::unfreeze(frontend.globals.globalTypes); - Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); + +} - registerBuiltinGlobals(frontend, frontend.globals); - if (prepareAutocomplete) - registerBuiltinGlobals(frontend, frontend.globalsForAutocomplete, /*typeCheckForAutocomplete*/ true); +Frontend& BuiltinsFixture::getFrontend() +{ + if (frontend) + return *frontend; + + Frontend& f = Fixture::getFrontend(); + // Do builtins fixture things now + Luau::unfreeze(f.globals.globalTypes); + Luau::unfreeze(f.globalsForAutocomplete.globalTypes); + + registerBuiltinGlobals(f, f.globals); + if (forAutocomplete) + registerBuiltinGlobals(f, f.globalsForAutocomplete, /*typeCheckForAutocomplete*/ true); registerTestTypes(); - Luau::freeze(frontend.globals.globalTypes); - Luau::freeze(frontend.globalsForAutocomplete.globalTypes); + Luau::freeze(f.globals.globalTypes); + Luau::freeze(f.globalsForAutocomplete.globalTypes); + + return *frontend; } static std::vector parsePathExpr(const AstExpr& pathExpr) @@ -831,9 +865,9 @@ std::optional linearSearchForBinding(Scope* scope, const char* name) return std::nullopt; } -void registerHiddenTypes(Frontend* frontend) +void registerHiddenTypes(Frontend& frontend) { - GlobalTypes& globals = frontend->globals; + GlobalTypes& globals = frontend.globals; unfreeze(globals.globalTypes); @@ -846,24 +880,24 @@ void registerHiddenTypes(Frontend* frontend) ScopePtr globalScope = globals.globalScope; globalScope->exportedTypeBindings["Not"] = TypeFun{{genericT}, globals.globalTypes.addType(NegationType{t})}; globalScope->exportedTypeBindings["Mt"] = TypeFun{{genericT, genericU}, globals.globalTypes.addType(MetatableType{t, u})}; - globalScope->exportedTypeBindings["fun"] = TypeFun{{}, frontend->builtinTypes->functionType}; - globalScope->exportedTypeBindings["cls"] = TypeFun{{}, frontend->builtinTypes->externType}; - globalScope->exportedTypeBindings["err"] = TypeFun{{}, frontend->builtinTypes->errorType}; - globalScope->exportedTypeBindings["tbl"] = TypeFun{{}, frontend->builtinTypes->tableType}; + globalScope->exportedTypeBindings["fun"] = TypeFun{{}, frontend.builtinTypes->functionType}; + globalScope->exportedTypeBindings["cls"] = TypeFun{{}, frontend.builtinTypes->externType}; + globalScope->exportedTypeBindings["err"] = TypeFun{{}, frontend.builtinTypes->errorType}; + globalScope->exportedTypeBindings["tbl"] = TypeFun{{}, frontend.builtinTypes->tableType}; freeze(globals.globalTypes); } -void createSomeExternTypes(Frontend* frontend) +void createSomeExternTypes(Frontend& frontend) { - GlobalTypes& globals = frontend->globals; + GlobalTypes& globals = frontend.globals; TypeArena& arena = globals.globalTypes; unfreeze(arena); ScopePtr moduleScope = globals.globalScope; - TypeId parentType = arena.addType(ExternType{"Parent", {}, frontend->builtinTypes->externType, std::nullopt, {}, nullptr, "Test", {}}); + TypeId parentType = arena.addType(ExternType{"Parent", {}, frontend.builtinTypes->externType, std::nullopt, {}, nullptr, "Test", {}}); ExternType* parentExternType = getMutable(parentType); parentExternType->props["method"] = {makeFunction(arena, parentType, {}, {})}; @@ -883,7 +917,7 @@ void createSomeExternTypes(Frontend* frontend) addGlobalBinding(globals, "AnotherChild", {anotherChildType}); moduleScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildType}; - TypeId unrelatedType = arena.addType(ExternType{"Unrelated", {}, frontend->builtinTypes->externType, std::nullopt, {}, nullptr, "Test", {}}); + TypeId unrelatedType = arena.addType(ExternType{"Unrelated", {}, frontend.builtinTypes->externType, std::nullopt, {}, nullptr, "Test", {}}); addGlobalBinding(globals, "Unrelated", {unrelatedType}); moduleScope->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType}; diff --git a/tests/Fixture.h b/tests/Fixture.h index 91d126bf..e1ee2753 100644 --- a/tests/Fixture.h +++ b/tests/Fixture.h @@ -160,9 +160,8 @@ struct Fixture TestConfigResolver configResolver; NullModuleResolver moduleResolver; std::unique_ptr sourceModule; - Frontend frontend; InternalErrorReporter ice; - NotNull builtinTypes; + std::string decorateWithTypes(const std::string& code); @@ -179,9 +178,15 @@ struct Fixture void registerTestTypes(); LoadDefinitionFileResult loadDefinition(const std::string& source, bool forAutocomplete = false); - + // TODO: test theory about dynamic dispatch + NotNull getBuiltins(); + virtual Frontend& getFrontend(); private: bool hasDumpedErrors = false; +protected: + bool forAutocomplete = false; + std::optional frontend; + BuiltinTypes* builtinTypes = nullptr; }; struct BuiltinsFixture : Fixture @@ -190,6 +195,7 @@ struct BuiltinsFixture : Fixture // For the purpose of our tests, we're always the latest version of type functions. ScopedFastFlag sff_optionalInTypeFunctionLib{FFlag::LuauTypeFunOptional, true}; + Frontend& getFrontend() override; }; std::optional pathExprToModuleName(const ModuleName& currentModuleName, const std::vector& segments); @@ -220,8 +226,8 @@ std::optional lookupName(ScopePtr scope, const std::string& name); // Wa std::optional linearSearchForBinding(Scope* scope, const char* name); -void registerHiddenTypes(Frontend* frontend); -void createSomeExternTypes(Frontend* frontend); +void registerHiddenTypes(Frontend& frontend); +void createSomeExternTypes(Frontend& frontend); template const E* findError(const CheckResult& result) diff --git a/tests/FragmentAutocomplete.test.cpp b/tests/FragmentAutocomplete.test.cpp index 04ff813c..e5797d6c 100644 --- a/tests/FragmentAutocomplete.test.cpp +++ b/tests/FragmentAutocomplete.test.cpp @@ -51,9 +51,9 @@ static FrontendOptions getOptions() return options; } -static ModuleResolver& getModuleResolver(Luau::Frontend& frontend) +static ModuleResolver& getModuleResolver(Frontend& frontend) { - return FFlag::LuauSolverV2 ? frontend.moduleResolver : frontend.moduleResolverForAutocomplete; + return FFlag::LuauSolverV2 ?frontend.moduleResolver : frontend.moduleResolverForAutocomplete; } template @@ -130,7 +130,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType ) { ParseResult p = parseHelper(document); - auto [_, result] = Luau::typecheckFragment(this->frontend, "MainModule", cursorPos, getOptions(), document, fragmentEndPosition, p.root); + auto [_, result] = Luau::typecheckFragment(this->getFrontend(), "MainModule", cursorPos, getOptions(), document, fragmentEndPosition, p.root); return result; } @@ -145,7 +145,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType ParseResult parseResult = parseHelper(document); FrontendOptions options = getOptions(); FragmentContext context{document, parseResult, options, fragmentEndPosition}; - return Luau::tryFragmentAutocomplete(this->frontend, "MainModule", cursorPos, context, nullCallback); + return Luau::tryFragmentAutocomplete(this->getFrontend(), "MainModule", cursorPos, context, nullCallback); } void autocompleteFragmentInNewSolver( @@ -211,7 +211,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType ) { ParseResult pr = parseHelper(document); - return Luau::typecheckFragment(this->frontend, module, cursorPos, getOptions(), document, fragmentEndPosition, pr.root); + return Luau::typecheckFragment(this->getFrontend(), module, cursorPos, getOptions(), document, fragmentEndPosition, pr.root); } FragmentAutocompleteStatusResult autocompleteFragmentForModule( @@ -226,7 +226,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType ParseResult parseResult = parseHelper(document); FrontendOptions options; FragmentContext context{document, parseResult, options, fragmentEndPosition}; - return Luau::tryFragmentAutocomplete(this->frontend, module, cursorPos, context, nullCallback); + return Luau::tryFragmentAutocomplete(this->getFrontend(). module, cursorPos, context, nullCallback); } SourceModule& getSource() @@ -244,10 +244,10 @@ struct FragmentAutocompleteFixture : FragmentAutocompleteFixtureImpl FragmentAutocompleteFixture() : FragmentAutocompleteFixtureImpl() { - addGlobalBinding(frontend.globals, "table", Binding{builtinTypes->anyType}); - addGlobalBinding(frontend.globals, "math", Binding{builtinTypes->anyType}); - addGlobalBinding(frontend.globalsForAutocomplete, "table", Binding{builtinTypes->anyType}); - addGlobalBinding(frontend.globalsForAutocomplete, "math", Binding{builtinTypes->anyType}); + addGlobalBinding(getFrontend().globals, "table", Binding{getBuiltins()->anyType}); + addGlobalBinding(getFrontend().globals, "math", Binding{getBuiltins()->anyType}); + addGlobalBinding(getFrontend().globalsForAutocomplete, "table", Binding{getBuiltins()->anyType}); + addGlobalBinding(getFrontend().globalsForAutocomplete, "math", Binding{getBuiltins()->anyType}); } }; @@ -272,8 +272,8 @@ end loadDefinition(fakeVecDecl); loadDefinition(fakeVecDecl, /* For Autocomplete Module */ true); - addGlobalBinding(frontend.globals, "game", Binding{builtinTypes->anyType}); - addGlobalBinding(frontend.globalsForAutocomplete, "game", Binding{builtinTypes->anyType}); + addGlobalBinding(getFrontend().globals, "game", Binding{getBuiltins()->anyType}); + addGlobalBinding(getFrontend().globalsForAutocomplete, "game", Binding{getBuiltins()->anyType}); } }; @@ -1317,7 +1317,7 @@ abc("bar") CHECK_EQ(Position{3, 1}, parent->location.end); } -TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "respects_frontend_options") +TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "respects_getFrontend().options") { DOES_NOT_PASS_NEW_SOLVER_GUARD(); @@ -1330,21 +1330,21 @@ t FrontendOptions opts; opts.forAutocomplete = true; - frontend.check("game/A", opts); - CHECK_NE(frontend.moduleResolverForAutocomplete.getModule("game/A"), nullptr); - CHECK_EQ(frontend.moduleResolver.getModule("game/A"), nullptr); + getFrontend().check("game/A", opts); + CHECK_NE(getFrontend().moduleResolverForAutocomplete.getModule("game/A"), nullptr); + CHECK_EQ(getFrontend().moduleResolver.getModule("game/A"), nullptr); ParseOptions parseOptions; parseOptions.captureComments = true; SourceModule sourceMod; ParseResult parseResult = Parser::parse(source.c_str(), source.length(), *sourceMod.names, *sourceMod.allocator, parseOptions); FragmentContext context{source, parseResult, opts, std::nullopt}; - FragmentAutocompleteStatusResult frag = Luau::tryFragmentAutocomplete(frontend, "game/A", Position{2, 1}, context, nullCallback); + FragmentAutocompleteStatusResult frag = Luau::tryFragmentAutocomplete(getFrontend(), "game/A", Position{2, 1}, context, nullCallback); REQUIRE(frag.result); REQUIRE(frag.result->incrementalModule); CHECK_EQ("game/A", frag.result->incrementalModule->name); - CHECK_NE(frontend.moduleResolverForAutocomplete.getModule("game/A"), nullptr); - CHECK_EQ(frontend.moduleResolver.getModule("game/A"), nullptr); + CHECK_NE(getFrontend().moduleResolverForAutocomplete.getModule("game/A"), nullptr); + CHECK_EQ(getFrontend().moduleResolver.getModule("game/A"), nullptr); } TEST_SUITE_END(); @@ -1496,16 +1496,16 @@ return { hello = B } const std::string sourceB = "game/Gui/Modules/B"; fileResolver.source[sourceB] = R"(return {hello = "hello"})"; - CheckResult result = frontend.check(sourceA, getOptions()); - CHECK(!frontend.isDirty(sourceA, getOptions().forAutocomplete)); + CheckResult result = getFrontend().check(sourceA, getOptions()); + CHECK(!getFrontend().isDirty(sourceA, getOptions().forAutocomplete)); - std::weak_ptr weakModule = getModuleResolver(frontend).getModule(sourceB); + std::weak_ptr weakModule = getModuleResolver(getFrontend()).getModule(sourceB); REQUIRE(!weakModule.expired()); - frontend.markDirty(sourceB); - CHECK(frontend.isDirty(sourceA, getOptions().forAutocomplete)); + getFrontend().markDirty(sourceB); + CHECK(getFrontend().isDirty(sourceA, getOptions().forAutocomplete)); - frontend.check(sourceB, getOptions()); + getFrontend().check(sourceB, getOptions()); CHECK(weakModule.expired()); auto [status, _] = typecheckFragmentForModule(sourceA, fileResolver.source[sourceA], Luau::Position(0, 0)); diff --git a/tests/Frontend.test.cpp b/tests/Frontend.test.cpp index 2b0a299d..150c1ca5 100644 --- a/tests/Frontend.test.cpp +++ b/tests/Frontend.test.cpp @@ -17,7 +17,7 @@ LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) namespace { @@ -59,8 +59,8 @@ struct FrontendFixture : BuiltinsFixture { FrontendFixture() { - addGlobalBinding(frontend.globals, "game", builtinTypes->anyType, "@test"); - addGlobalBinding(frontend.globals, "script", builtinTypes->anyType, "@test"); + addGlobalBinding(getFrontend().globals, "game", getBuiltins()->anyType, "@test"); + addGlobalBinding(getFrontend().globals, "script", getBuiltins()->anyType, "@test"); } }; @@ -128,9 +128,9 @@ TEST_CASE_FIXTURE(FrontendFixture, "automatically_check_dependent_scripts") return {b_value = A.hello} )"; - frontend.check("game/Gui/Modules/B"); + getFrontend().check("game/Gui/Modules/B"); - ModulePtr bModule = frontend.moduleResolver.getModule("game/Gui/Modules/B"); + ModulePtr bModule = getFrontend().moduleResolver.getModule("game/Gui/Modules/B"); REQUIRE(bModule != nullptr); CHECK(bModule->errors.empty()); Luau::dumpErrors(bModule); @@ -169,7 +169,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "automatically_check_cyclically_dependent_scr return {} )"; - CheckResult result1 = frontend.check("game/Gui/Modules/B"); + CheckResult result1 = getFrontend().check("game/Gui/Modules/B"); LUAU_REQUIRE_ERROR_COUNT(4, result1); CHECK_MESSAGE(get(result1.errors[0]), "Should have been a ModuleHasCyclicDependency: " << toString(result1.errors[0])); @@ -178,7 +178,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "automatically_check_cyclically_dependent_scr CHECK_MESSAGE(get(result1.errors[2]), "Should have been a ModuleHasCyclicDependency: " << toString(result1.errors[2])); - CheckResult result2 = frontend.check("game/Gui/Modules/D"); + CheckResult result2 = getFrontend().check("game/Gui/Modules/D"); LUAU_REQUIRE_ERROR_COUNT(0, result2); } @@ -195,7 +195,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "any_annotation_breaks_cycle") return {hello = A.hello} )"; - CheckResult result = frontend.check("game/Gui/Modules/A"); + CheckResult result = getFrontend().check("game/Gui/Modules/A"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -218,16 +218,16 @@ TEST_CASE_FIXTURE(FrontendFixture, "nocheck_modules_are_typed") local five : A.Foo = 5 )"; - CheckResult result = frontend.check("game/Gui/Modules/C"); + CheckResult result = getFrontend().check("game/Gui/Modules/C"); LUAU_REQUIRE_NO_ERRORS(result); - ModulePtr aModule = frontend.moduleResolver.getModule("game/Gui/Modules/A"); + ModulePtr aModule = getFrontend().moduleResolver.getModule("game/Gui/Modules/A"); REQUIRE(bool(aModule)); std::optional aExports = first(aModule->returnType); REQUIRE(bool(aExports)); - ModulePtr bModule = frontend.moduleResolver.getModule("game/Gui/Modules/B"); + ModulePtr bModule = getFrontend().moduleResolver.getModule("game/Gui/Modules/B"); REQUIRE(bool(bModule)); std::optional bExports = first(bModule->returnType); @@ -250,7 +250,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_detection_between_check_and_nocheck") return {hello = A.hello} )"; - CheckResult result = frontend.check("game/Gui/Modules/A"); + CheckResult result = getFrontend().check("game/Gui/Modules/A"); LUAU_REQUIRE_ERROR_COUNT(1, result); } @@ -276,10 +276,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "nocheck_cycle_used_by_checked") return {a=A, b=B} )"; - CheckResult result = frontend.check("game/Gui/Modules/C"); + CheckResult result = getFrontend().check("game/Gui/Modules/C"); LUAU_REQUIRE_NO_ERRORS(result); - ModulePtr cModule = frontend.moduleResolver.getModule("game/Gui/Modules/C"); + ModulePtr cModule = getFrontend().moduleResolver.getModule("game/Gui/Modules/C"); REQUIRE(bool(cModule)); std::optional cExports = first(cModule->returnType); @@ -306,7 +306,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_detection_disabled_in_nocheck") return {hello = A.hello} )"; - CheckResult result = frontend.check("game/Gui/Modules/A"); + CheckResult result = getFrontend().check("game/Gui/Modules/A"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -323,7 +323,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_errors_can_be_fixed") return {hello = A.hello} )"; - CheckResult result1 = frontend.check("game/Gui/Modules/A"); + CheckResult result1 = getFrontend().check("game/Gui/Modules/A"); LUAU_REQUIRE_ERROR_COUNT(2, result1); CHECK_MESSAGE(get(result1.errors[0]), "Should have been a ModuleHasCyclicDependency: " << toString(result1.errors[0])); @@ -333,9 +333,9 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_errors_can_be_fixed") fileResolver.source["game/Gui/Modules/B"] = R"( return {hello = 42} )"; - frontend.markDirty("game/Gui/Modules/B"); + getFrontend().markDirty("game/Gui/Modules/B"); - CheckResult result2 = frontend.check("game/Gui/Modules/A"); + CheckResult result2 = getFrontend().check("game/Gui/Modules/A"); LUAU_REQUIRE_NO_ERRORS(result2); } @@ -352,7 +352,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_error_paths") return {hello = A.hello} )"; - CheckResult result = frontend.check("game/Gui/Modules/A"); + CheckResult result = getFrontend().check("game/Gui/Modules/A"); LUAU_REQUIRE_ERROR_COUNT(2, result); auto ce1 = get(result.errors[0]); @@ -376,16 +376,16 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_incremental_type_surface") return {hello = 2} )"; - CheckResult result = frontend.check("game/A"); + CheckResult result = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(result); fileResolver.source["game/A"] = R"( local me = require(game.A) return {hello = 2} )"; - frontend.markDirty("game/A"); + getFrontend().markDirty("game/A"); - result = frontend.check("game/A"); + result = getFrontend().check("game/A"); LUAU_REQUIRE_ERRORS(result); auto ty = requireType("game/A", "me"); @@ -398,7 +398,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_incremental_type_surface_longer") return {mod_a = 2} )"; - CheckResult result = frontend.check("game/A"); + CheckResult result = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(result); fileResolver.source["game/B"] = R"( @@ -406,7 +406,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_incremental_type_surface_longer") return {mod_b = 4} )"; - result = frontend.check("game/B"); + result = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(result); fileResolver.source["game/A"] = R"( @@ -414,16 +414,16 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_incremental_type_surface_longer") return {mod_a_prime = 3} )"; - frontend.markDirty("game/A"); - frontend.markDirty("game/B"); + getFrontend().markDirty("game/A"); + getFrontend().markDirty("game/B"); - result = frontend.check("game/A"); + result = getFrontend().check("game/A"); LUAU_REQUIRE_ERRORS(result); TypeId tyA = requireType("game/A", "me"); CHECK_EQ(toString(tyA), "any"); - result = frontend.check("game/B"); + result = getFrontend().check("game/B"); LUAU_REQUIRE_ERRORS(result); TypeId tyB = requireType("game/B", "me"); @@ -452,10 +452,10 @@ return {mod_b = 2} ToStringOptions opts; opts.exhaustive = true; - CheckResult resultA = frontend.check("game/A"); + CheckResult resultA = getFrontend().check("game/A"); LUAU_REQUIRE_ERRORS(resultA); - CheckResult resultB = frontend.check("game/B"); + CheckResult resultB = getFrontend().check("game/B"); LUAU_REQUIRE_ERRORS(resultB); TypeId tyB = requireExportedType("game/B", "btype"); @@ -470,8 +470,8 @@ return {mod_b = 2} else CHECK_EQ(toString(tyA, opts), "{| x: any |}"); - frontend.markDirty("game/B"); - resultB = frontend.check("game/B"); + getFrontend().markDirty("game/B"); + resultB = getFrontend().check("game/B"); LUAU_REQUIRE_ERRORS(resultB); tyB = requireExportedType("game/B", "btype"); @@ -522,14 +522,14 @@ TEST_CASE_FIXTURE(FrontendFixture, "dont_recheck_script_that_hasnt_been_marked_d return {b_value = A.hello} )"; - frontend.check("game/Gui/Modules/B"); + getFrontend().check("game/Gui/Modules/B"); fileResolver.source["game/Gui/Modules/A"] = - "Massively incorrect syntax haha oops! However! The frontend doesn't know that this file needs reparsing!"; + "Massively incorrect syntax haha oops! However! The getFrontend().doesn't know that this file needs reparsing!"; - frontend.check("game/Gui/Modules/B"); + getFrontend().check("game/Gui/Modules/B"); - ModulePtr bModule = frontend.moduleResolver.getModule("game/Gui/Modules/B"); + ModulePtr bModule = getFrontend().moduleResolver.getModule("game/Gui/Modules/B"); CHECK(bModule->errors.empty()); Luau::dumpErrors(bModule); } @@ -543,14 +543,14 @@ TEST_CASE_FIXTURE(FrontendFixture, "recheck_if_dependent_script_is_dirty") return {b_value = A.hello} )"; - frontend.check("game/Gui/Modules/B"); + getFrontend().check("game/Gui/Modules/B"); fileResolver.source["game/Gui/Modules/A"] = "return {hello='hi!'}"; - frontend.markDirty("game/Gui/Modules/A"); + getFrontend().markDirty("game/Gui/Modules/A"); - frontend.check("game/Gui/Modules/B"); + getFrontend().check("game/Gui/Modules/B"); - ModulePtr bModule = frontend.moduleResolver.getModule("game/Gui/Modules/B"); + ModulePtr bModule = getFrontend().moduleResolver.getModule("game/Gui/Modules/B"); CHECK(bModule->errors.empty()); Luau::dumpErrors(bModule); @@ -575,10 +575,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "mark_non_immediate_reverse_deps_as_dirty") return {c_value = B.hello} )"; - frontend.check("game/Gui/Modules/C"); + getFrontend().check("game/Gui/Modules/C"); std::vector markedDirty; - frontend.markDirty("game/Gui/Modules/A", &markedDirty); + getFrontend().markDirty("game/Gui/Modules/A", &markedDirty); REQUIRE(markedDirty.size() == 3); CHECK(std::find(markedDirty.begin(), markedDirty.end(), "game/Gui/Modules/A") != markedDirty.end()); @@ -597,11 +597,11 @@ TEST_CASE_FIXTURE(FrontendFixture, "recheck_if_dependent_script_has_a_parse_erro return {} )"; - CheckResult result = frontend.check("Modules/B"); + CheckResult result = getFrontend().check("Modules/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK_EQ("Modules/A", result.errors[0].moduleName); - CheckResult result2 = frontend.check("Modules/B"); + CheckResult result2 = getFrontend().check("Modules/B"); LUAU_REQUIRE_ERROR_COUNT(1, result2); CHECK_EQ(result2.errors[0], result.errors[0]); } @@ -611,8 +611,8 @@ TEST_CASE_FIXTURE(FrontendFixture, "produce_errors_for_unchanged_file_with_a_syn { fileResolver.source["Modules/A"] = "oh no a blatant syntax error!!"; - CheckResult one = frontend.check("Modules/A"); - CheckResult two = frontend.check("Modules/A"); + CheckResult one = getFrontend().check("Modules/A"); + CheckResult two = getFrontend().check("Modules/A"); CHECK(!one.errors.empty()); CHECK(!two.errors.empty()); @@ -622,10 +622,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "produce_errors_for_unchanged_file_with_error { fileResolver.source["Modules/A"] = "local p: number = 'oh no a type error'"; - frontend.check("Modules/A"); + getFrontend().check("Modules/A"); - fileResolver.source["Modules/A"] = "local p = 4 -- We have fixed the problem, but we didn't tell the frontend, so it will not recheck this file!"; - CheckResult secondResult = frontend.check("Modules/A"); + fileResolver.source["Modules/A"] = "local p = 4 -- We have fixed the problem, but we didn't tell the getFrontend(). so it will not recheck this file!"; + CheckResult secondResult = getFrontend().check("Modules/A"); CHECK_EQ(1, secondResult.errors.size()); } @@ -643,7 +643,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "reports_errors_from_multiple_sources") local b: number = 'another one! This is quite distressing!' )"; - CheckResult result = frontend.check("game/Gui/Modules/B"); + CheckResult result = getFrontend().check("game/Gui/Modules/B"); LUAU_REQUIRE_ERROR_COUNT(2, result); CHECK_EQ("game/Gui/Modules/A", result.errors[0].moduleName); @@ -657,7 +657,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "report_require_to_nonexistent_file") local B = require(Modules.B) )"; - CheckResult result = frontend.check("Modules/A"); + CheckResult result = getFrontend().check("Modules/A"); LUAU_REQUIRE_ERROR_COUNT(1, result); std::string s = toString(result.errors[0]); @@ -671,7 +671,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "ignore_require_to_nonexistent_file") local B = require(Modules.B) :: any )"; - CheckResult result = frontend.check("Modules/A"); + CheckResult result = getFrontend().check("Modules/A"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -683,7 +683,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "report_syntax_error_in_required_file") local A = require(Modules.A) )"; - CheckResult result = frontend.check("Modules/B"); + CheckResult result = getFrontend().check("Modules/B"); LUAU_REQUIRE_ERRORS(result); CHECK_EQ("Modules/A", result.errors[0].moduleName); @@ -716,10 +716,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "re_report_type_error_in_required_file") print(A.n) )"; - CheckResult result = frontend.check("Modules/B"); + CheckResult result = getFrontend().check("Modules/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); - CheckResult result2 = frontend.check("Modules/B"); + CheckResult result2 = getFrontend().check("Modules/B"); LUAU_REQUIRE_ERROR_COUNT(1, result2); CHECK_EQ("Modules/A", result.errors[0].moduleName); @@ -739,14 +739,14 @@ TEST_CASE_FIXTURE(FrontendFixture, "accumulate_cached_errors") print(A, b) )"; - CheckResult result1 = frontend.check("Modules/B"); + CheckResult result1 = getFrontend().check("Modules/B"); LUAU_REQUIRE_ERROR_COUNT(2, result1); CHECK_EQ("Modules/A", result1.errors[0].moduleName); CHECK_EQ("Modules/B", result1.errors[1].moduleName); - CheckResult result2 = frontend.check("Modules/B"); + CheckResult result2 = getFrontend().check("Modules/B"); LUAU_REQUIRE_ERROR_COUNT(2, result2); @@ -769,7 +769,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "accumulate_cached_errors_in_consistent_order return {} )"; - CheckResult result1 = frontend.check("Modules/A"); + CheckResult result1 = getFrontend().check("Modules/A"); LUAU_REQUIRE_ERROR_COUNT(4, result1); @@ -779,7 +779,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "accumulate_cached_errors_in_consistent_order CHECK_EQ("Modules/B", result1.errors[0].moduleName); CHECK_EQ("Modules/B", result1.errors[1].moduleName); - CheckResult result2 = frontend.check("Modules/A"); + CheckResult result2 = getFrontend().check("Modules/A"); CHECK_EQ(4, result2.errors.size()); for (size_t i = 0; i < result1.errors.size(); ++i) @@ -817,7 +817,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_lint_uses_correct_config") CHECK_EQ(1, result.warnings.size()); configResolver.configFiles["Module/A"].enabledLint.disableWarning(LintWarning::Code_ForRange); - frontend.markDirty("Module/A"); + getFrontend().markDirty("Module/A"); auto result2 = lintModule("Module/A"); CHECK_EQ(0, result2.warnings.size()); @@ -825,13 +825,13 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_lint_uses_correct_config") LintOptions overrideOptions; overrideOptions.enableWarning(LintWarning::Code_ForRange); - frontend.markDirty("Module/A"); + getFrontend().markDirty("Module/A"); auto result3 = lintModule("Module/A", overrideOptions); CHECK_EQ(1, result3.warnings.size()); overrideOptions.disableWarning(LintWarning::Code_ForRange); - frontend.markDirty("Module/A"); + getFrontend().markDirty("Module/A"); auto result4 = lintModule("Module/A", overrideOptions); CHECK_EQ(0, result4.warnings.size()); @@ -877,7 +877,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "discard_type_graphs") TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; Frontend fe{&fileResolver, &configResolver, {false}}; @@ -932,7 +932,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "trace_requires_in_nonstrict_mode") print(A.f(5)) -- OK )"; - CheckResult result = frontend.check("Module/B"); + CheckResult result = getFrontend().check("Module/B"); LUAU_REQUIRE_ERROR_COUNT(2, result); @@ -942,11 +942,11 @@ TEST_CASE_FIXTURE(FrontendFixture, "trace_requires_in_nonstrict_mode") TEST_CASE_FIXTURE(FrontendFixture, "environments") { - ScopePtr testScope = frontend.addEnvironment("test"); + ScopePtr testScope = getFrontend().addEnvironment("test"); - unfreeze(frontend.globals.globalTypes); - frontend.loadDefinitionFile( - frontend.globals, + unfreeze(getFrontend().globals.globalTypes); + getFrontend().loadDefinitionFile( + getFrontend().globals, testScope, R"( export type Foo = number | string @@ -954,7 +954,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "environments") "@test", /* captureComments */ false ); - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); fileResolver.source["A"] = R"( --!nonstrict @@ -973,16 +973,16 @@ TEST_CASE_FIXTURE(FrontendFixture, "environments") fileResolver.environments["A"] = "test"; - CheckResult resultA = frontend.check("A"); + CheckResult resultA = getFrontend().check("A"); LUAU_REQUIRE_NO_ERRORS(resultA); - CheckResult resultB = frontend.check("B"); + CheckResult resultB = getFrontend().check("B"); if (FFlag::LuauSolverV2 && !FFlag::LuauNewNonStrictVisitTypes2) LUAU_REQUIRE_NO_ERRORS(resultB); else LUAU_REQUIRE_ERROR_COUNT(1, resultB); - CheckResult resultC = frontend.check("C"); + CheckResult resultC = getFrontend().check("C"); LUAU_REQUIRE_ERROR_COUNT(1, resultC); } @@ -1021,18 +1021,18 @@ TEST_CASE_FIXTURE(FrontendFixture, "stats_are_not_reset_between_checks") return {foo = 1} )"; - CheckResult r1 = frontend.check("Module/A"); + CheckResult r1 = getFrontend().check("Module/A"); LUAU_REQUIRE_NO_ERRORS(r1); - Frontend::Stats stats1 = frontend.stats; + Frontend::Stats stats1 = getFrontend().stats; CHECK_EQ(2, stats1.files); - frontend.markDirty("Module/A"); - frontend.markDirty("Module/B"); + getFrontend().markDirty("Module/A"); + getFrontend().markDirty("Module/B"); - CheckResult r2 = frontend.check("Module/A"); + CheckResult r2 = getFrontend().check("Module/A"); LUAU_REQUIRE_NO_ERRORS(r2); - Frontend::Stats stats2 = frontend.stats; + Frontend::Stats stats2 = getFrontend().stats; CHECK_EQ(4, stats2.files); } @@ -1050,19 +1050,19 @@ TEST_CASE_FIXTURE(FrontendFixture, "clearStats") return {foo = 1} )"; - CheckResult r1 = frontend.check("Module/A"); + CheckResult r1 = getFrontend().check("Module/A"); LUAU_REQUIRE_NO_ERRORS(r1); - Frontend::Stats stats1 = frontend.stats; + Frontend::Stats stats1 = getFrontend().stats; CHECK_EQ(2, stats1.files); - frontend.markDirty("Module/A"); - frontend.markDirty("Module/B"); + getFrontend().markDirty("Module/A"); + getFrontend().markDirty("Module/B"); - frontend.clearStats(); - CheckResult r2 = frontend.check("Module/A"); + getFrontend().clearStats(); + CheckResult r2 = getFrontend().check("Module/A"); LUAU_REQUIRE_NO_ERRORS(r2); - Frontend::Stats stats2 = frontend.stats; + Frontend::Stats stats2 = getFrontend().stats; CHECK_EQ(2, stats2.files); } @@ -1073,9 +1073,9 @@ TEST_CASE_FIXTURE(FrontendFixture, "typecheck_twice_for_ast_types") local a = 1 )"; - CheckResult result = frontend.check("Module/A"); + CheckResult result = getFrontend().check("Module/A"); - ModulePtr module = frontend.moduleResolver.getModule("Module/A"); + ModulePtr module = getFrontend().moduleResolver.getModule("Module/A"); REQUIRE_EQ(module->astTypes.size(), 1); auto it = module->astTypes.begin(); @@ -1088,7 +1088,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "imported_table_modification_2") if (FFlag::LuauSolverV2) return; - frontend.options.retainFullTypeGraphs = false; + getFrontend().options.retainFullTypeGraphs = false; fileResolver.source["Module/A"] = R"( --!nonstrict @@ -1112,13 +1112,13 @@ local b = require(script.Parent.B) a:b() -- this should error, since A doesn't define a:b() )"; - CheckResult resultA = frontend.check("Module/A"); + CheckResult resultA = getFrontend().check("Module/A"); LUAU_REQUIRE_NO_ERRORS(resultA); - CheckResult resultB = frontend.check("Module/B"); + CheckResult resultB = getFrontend().check("Module/B"); LUAU_REQUIRE_ERRORS(resultB); - CheckResult resultC = frontend.check("Module/C"); + CheckResult resultC = getFrontend().check("Module/C"); LUAU_REQUIRE_ERRORS(resultC); } @@ -1143,7 +1143,7 @@ return false; )"; // We don't care about the result. That we haven't crashed is enough. - fix.frontend.check("Module/B"); + fix.getFrontend().check("Module/B"); } TEST_CASE("check_without_builtin_next") @@ -1151,7 +1151,6 @@ TEST_CASE("check_without_builtin_next") TestFileResolver fileResolver; TestConfigResolver configResolver; Frontend frontend(&fileResolver, &configResolver); - fileResolver.source["Module/A"] = "for k,v in 2 do end"; fileResolver.source["Module/B"] = "return next"; @@ -1186,7 +1185,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "reexport_cyclic_type") } )"; - CheckResult result = frontend.check("Module/B"); + CheckResult result = getFrontend().check("Module/B"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -1212,23 +1211,23 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "reexport_type_alias") export type TestFileEvent = A.TestFileEvent )"; - CheckResult result = frontend.check("Module/B"); + CheckResult result = getFrontend().check("Module/B"); LUAU_REQUIRE_NO_ERRORS(result); } TEST_CASE_FIXTURE(BuiltinsFixture, "module_scope_check") { - frontend.prepareModuleScope = [this](const ModuleName& name, const ScopePtr& scope, bool forAutocomplete) + getFrontend().prepareModuleScope = [this](const ModuleName& name, const ScopePtr& scope, bool forAutocomplete) { - scope->bindings[Luau::AstName{"x"}] = Luau::Binding{frontend.globals.builtinTypes->numberType}; + scope->bindings[Luau::AstName{"x"}] = Luau::Binding{getFrontend().globals.builtinTypes->numberType}; }; fileResolver.source["game/A"] = R"( local a = x )"; - CheckResult result = frontend.check("game/A"); + CheckResult result = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(result); auto ty = requireType("game/A", "a"); @@ -1248,18 +1247,18 @@ TEST_CASE_FIXTURE(FrontendFixture, "parse_only") local b: number = 2 )"; - frontend.parse("game/Gui/Modules/B"); + getFrontend().parse("game/Gui/Modules/B"); - REQUIRE(frontend.sourceNodes.count("game/Gui/Modules/A")); - REQUIRE(frontend.sourceNodes.count("game/Gui/Modules/B")); + REQUIRE(getFrontend().sourceNodes.count("game/Gui/Modules/A")); + REQUIRE(getFrontend().sourceNodes.count("game/Gui/Modules/B")); - auto node = frontend.sourceNodes["game/Gui/Modules/B"]; + auto node = getFrontend().sourceNodes["game/Gui/Modules/B"]; CHECK(node->requireSet.contains("game/Gui/Modules/A")); REQUIRE_EQ(node->requireLocations.size(), 1); CHECK_EQ(node->requireLocations[0].second, Luau::Location(Position(2, 18), Position(2, 36))); // Early parse doesn't cause typechecking to be skipped - CheckResult result = frontend.check("game/Gui/Modules/B"); + CheckResult result = getFrontend().check("game/Gui/Modules/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK_EQ("game/Gui/Modules/A", result.errors[0].moduleName); @@ -1275,15 +1274,15 @@ TEST_CASE_FIXTURE(FrontendFixture, "markdirty_early_return") { std::vector markedDirty; - frontend.markDirty(moduleName, &markedDirty); + getFrontend().markDirty(moduleName, &markedDirty); CHECK(markedDirty.empty()); } - frontend.parse(moduleName); + getFrontend().parse(moduleName); { std::vector markedDirty; - frontend.markDirty(moduleName, &markedDirty); + getFrontend().markDirty(moduleName, &markedDirty); CHECK(!markedDirty.empty()); } } @@ -1302,7 +1301,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "attribute_ices_to_the_correct_module") try { - frontend.check("game/one"); + getFrontend().check("game/one"); } catch (InternalCompilerError& err) { @@ -1330,19 +1329,19 @@ TEST_CASE_FIXTURE(FrontendFixture, "checked_modules_have_the_correct_mode") local a = 10 )"; - frontend.check("game/A"); - frontend.check("game/B"); - frontend.check("game/C"); + getFrontend().check("game/A"); + getFrontend().check("game/B"); + getFrontend().check("game/C"); - ModulePtr moduleA = frontend.moduleResolver.getModule("game/A"); + ModulePtr moduleA = getFrontend().moduleResolver.getModule("game/A"); REQUIRE(moduleA); CHECK(moduleA->mode == Mode::NoCheck); - ModulePtr moduleB = frontend.moduleResolver.getModule("game/B"); + ModulePtr moduleB = getFrontend().moduleResolver.getModule("game/B"); REQUIRE(moduleB); CHECK(moduleB->mode == Mode::Nonstrict); - ModulePtr moduleC = frontend.moduleResolver.getModule("game/C"); + ModulePtr moduleC = getFrontend().moduleResolver.getModule("game/C"); REQUIRE(moduleC); CHECK(moduleC->mode == Mode::Strict); } @@ -1361,17 +1360,17 @@ TEST_CASE_FIXTURE(FrontendFixture, "separate_caches_for_autocomplete") FrontendOptions opts; opts.forAutocomplete = true; - frontend.check("game/A", opts); + getFrontend().check("game/A", opts); - CHECK(nullptr == frontend.moduleResolver.getModule("game/A")); + CHECK(nullptr == getFrontend().moduleResolver.getModule("game/A")); - ModulePtr acModule = frontend.moduleResolverForAutocomplete.getModule("game/A"); + ModulePtr acModule = getFrontend().moduleResolverForAutocomplete.getModule("game/A"); REQUIRE(acModule != nullptr); CHECK(acModule->mode == Mode::Strict); - frontend.check("game/A"); + getFrontend().check("game/A"); - ModulePtr module = frontend.moduleResolver.getModule("game/A"); + ModulePtr module = getFrontend().moduleResolver.getModule("game/A"); REQUIRE(module != nullptr); CHECK(module->mode == Mode::Nonstrict); @@ -1391,11 +1390,11 @@ TEST_CASE_FIXTURE(FrontendFixture, "no_separate_caches_with_the_new_solver") FrontendOptions opts; opts.forAutocomplete = true; - frontend.check("game/A", opts); + getFrontend().check("game/A", opts); - CHECK(nullptr == frontend.moduleResolverForAutocomplete.getModule("game/A")); + CHECK(nullptr == getFrontend().moduleResolverForAutocomplete.getModule("game/A")); - ModulePtr module = frontend.moduleResolver.getModule("game/A"); + ModulePtr module = getFrontend().moduleResolver.getModule("game/A"); REQUIRE(module != nullptr); CHECK(module->mode == Mode::Nonstrict); @@ -1450,15 +1449,15 @@ TEST_CASE_FIXTURE(FrontendFixture, "get_required_scripts") )"; // isDirty(name) is true, getRequiredScripts should not hit the cache. - frontend.markDirty("game/workspace/MyScript"); - std::vector requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); + getFrontend().markDirty("game/workspace/MyScript"); + std::vector requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript"); REQUIRE(requiredScripts.size() == 2); CHECK(requiredScripts[0] == "game/workspace/MyModuleScript"); CHECK(requiredScripts[1] == "game/workspace/MyModuleScript2"); - // Call frontend.check first, then getRequiredScripts should hit the cache because isDirty(name) is false. - frontend.check("game/workspace/MyScript"); - requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); + // Call getFrontend().check first, then getRequiredScripts should hit the cache because isDirty(name) is false. + getFrontend().check("game/workspace/MyScript"); + requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript"); REQUIRE(requiredScripts.size() == 2); CHECK(requiredScripts[0] == "game/workspace/MyModuleScript"); CHECK(requiredScripts[1] == "game/workspace/MyModuleScript2"); @@ -1478,8 +1477,8 @@ TEST_CASE_FIXTURE(FrontendFixture, "get_required_scripts_dirty") return module )"; - frontend.check("game/workspace/MyScript"); - std::vector requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); + getFrontend().check("game/workspace/MyScript"); + std::vector requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript"); REQUIRE(requiredScripts.size() == 0); fileResolver.source["game/workspace/MyScript"] = R"( @@ -1487,11 +1486,11 @@ TEST_CASE_FIXTURE(FrontendFixture, "get_required_scripts_dirty") MyModuleScript.myPrint() )"; - requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); + requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript"); REQUIRE(requiredScripts.size() == 0); - frontend.markDirty("game/workspace/MyScript"); - requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); + getFrontend().markDirty("game/workspace/MyScript"); + requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript"); REQUIRE(requiredScripts.size() == 1); CHECK(requiredScripts[0] == "game/workspace/MyModuleScript"); } @@ -1502,10 +1501,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_allocator") print("Hello World") )"; - frontend.check("game/workspace/MyScript"); + getFrontend().check("game/workspace/MyScript"); - ModulePtr module = frontend.moduleResolver.getModule("game/workspace/MyScript"); - SourceModule* source = frontend.getSourceModule("game/workspace/MyScript"); + ModulePtr module = getFrontend().moduleResolver.getModule("game/workspace/MyScript"); + SourceModule* source = getFrontend().getSourceModule("game/workspace/MyScript"); CHECK(module); CHECK(source); @@ -1519,10 +1518,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_correct_ast_root") print("Hello World") )"; - frontend.check("game/workspace/MyScript"); + getFrontend().check("game/workspace/MyScript"); - ModulePtr module = frontend.moduleResolver.getModule("game/workspace/MyScript"); - SourceModule* source = frontend.getSourceModule("game/workspace/MyScript"); + ModulePtr module = getFrontend().moduleResolver.getModule("game/workspace/MyScript"); + SourceModule* source = getFrontend().getSourceModule("game/workspace/MyScript"); CHECK(module); CHECK(source); @@ -1539,19 +1538,19 @@ local c = 3 return {x = a, y = b, z = c} )"; - frontend.options.retainFullTypeGraphs = true; - frontend.check("game/A"); + getFrontend().options.retainFullTypeGraphs = true; + getFrontend().check("game/A"); - auto mod = frontend.moduleResolver.getModule("game/A"); + auto mod = getFrontend().moduleResolver.getModule("game/A"); CHECK(!mod->defArena.allocator.empty()); CHECK(!mod->keyArena.allocator.empty()); // We should check that the dfg arena is empty once retainFullTypeGraphs is unset - frontend.options.retainFullTypeGraphs = false; - frontend.markDirty("game/A"); - frontend.check("game/A"); + getFrontend().options.retainFullTypeGraphs = false; + getFrontend().markDirty("game/A"); + getFrontend().check("game/A"); - mod = frontend.moduleResolver.getModule("game/A"); + mod = getFrontend().moduleResolver.getModule("game/A"); CHECK(mod->defArena.allocator.empty()); CHECK(mod->keyArena.allocator.empty()); } @@ -1573,10 +1572,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_traverse_dependents") return {d_value = C.c_value} )"; - frontend.check("game/Gui/Modules/D"); + getFrontend().check("game/Gui/Modules/D"); std::vector visited; - frontend.traverseDependents( + getFrontend().traverseDependents( "game/Gui/Modules/B", [&visited](SourceNode& node) { @@ -1600,10 +1599,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_traverse_dependents_early_exit") return {c_value = B.hello} )"; - frontend.check("game/Gui/Modules/C"); + getFrontend().check("game/Gui/Modules/C"); std::vector visited; - frontend.traverseDependents( + getFrontend().traverseDependents( "game/Gui/Modules/A", [&visited](SourceNode& node) { @@ -1620,19 +1619,19 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_dependents_stored_on_node_as_graph_upda auto updateSource = [&](const std::string& name, const std::string& source) { fileResolver.source[name] = source; - frontend.markDirty(name); + getFrontend().markDirty(name); }; auto validateMatchesRequireLists = [&](const std::string& message) { DenseHashMap> dependents{{}}; - for (const auto& module : frontend.sourceNodes) + for (const auto& module : getFrontend().sourceNodes) { for (const auto& dep : module.second->requireSet) dependents[dep].push_back(module.first); } - for (const auto& module : frontend.sourceNodes) + for (const auto& module : getFrontend().sourceNodes) { Set& dependentsForModule = module.second->dependents; for (const auto& dep : dependents[module.first]) @@ -1642,7 +1641,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_dependents_stored_on_node_as_graph_upda auto validateSecondDependsOnFirst = [&](const std::string& from, const std::string& to, bool expected) { - SourceNode& fromNode = *frontend.sourceNodes[from]; + SourceNode& fromNode = *getFrontend().sourceNodes[from]; CHECK_MESSAGE( fromNode.dependents.count(to) == int(expected), "Expected " << from << " to " << (expected ? std::string() : std::string("not ")) << "have a reverse dependency on " << to @@ -1660,7 +1659,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_dependents_stored_on_node_as_graph_upda local B = require(Modules.B) return {c_value = B} )"); - frontend.check("game/Gui/Modules/C"); + getFrontend().check("game/Gui/Modules/C"); validateMatchesRequireLists("Initial check"); @@ -1674,7 +1673,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_dependents_stored_on_node_as_graph_upda updateSource("game/Gui/Modules/B", R"( return 1 )"); - frontend.check("game/Gui/Modules/C"); + getFrontend().check("game/Gui/Modules/C"); validateMatchesRequireLists("Removing dependency B->A"); validateSecondDependsOnFirst("game/Gui/Modules/A", "game/Gui/Modules/B", false); @@ -1685,7 +1684,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_dependents_stored_on_node_as_graph_upda updateSource("game/Gui/Modules/B", R"( return require(game:GetService('Gui').Modules.A) )"); - frontend.check("game/Gui/Modules/C"); + getFrontend().check("game/Gui/Modules/C"); validateMatchesRequireLists("Adding back B->A"); validateSecondDependsOnFirst("game/Gui/Modules/A", "game/Gui/Modules/B", true); @@ -1699,7 +1698,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_dependents_stored_on_node_as_graph_upda local A = require(game:GetService('Gui').Modules.A) return {d_value = C.c_value} )"); - frontend.check("game/Gui/Modules/D"); + getFrontend().check("game/Gui/Modules/D"); validateMatchesRequireLists("Adding D->C, D->B, D->A"); validateSecondDependsOnFirst("game/Gui/Modules/A", "game/Gui/Modules/D", true); @@ -1711,7 +1710,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_dependents_stored_on_node_as_graph_upda { updateSource("game/Gui/Modules/D", "return require(game:GetService('Gui').Modules.C)"); updateSource("game/Gui/Modules/C", "return require(game:GetService('Gui').Modules.D)"); - frontend.check("game/Gui/Modules/D"); + getFrontend().check("game/Gui/Modules/D"); validateMatchesRequireLists("Adding cycle D->C, C->D"); validateSecondDependsOnFirst("game/Gui/Modules/C", "game/Gui/Modules/D", true); @@ -1721,7 +1720,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_dependents_stored_on_node_as_graph_upda // B -> A, C -> D, D -> error { updateSource("game/Gui/Modules/D", "return require(game:GetService('Gui').Modules.C.)"); - frontend.check("game/Gui/Modules/D"); + getFrontend().check("game/Gui/Modules/D"); validateMatchesRequireLists("Adding error dependency D->C."); validateSecondDependsOnFirst("game/Gui/Modules/D", "game/Gui/Modules/C", true); @@ -1739,17 +1738,17 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_invalid_dependency_tracking_per_module_ FrontendOptions opts; opts.forAutocomplete = false; - frontend.check("game/Gui/Modules/B", opts); - CHECK(frontend.allModuleDependenciesValid("game/Gui/Modules/B", opts.forAutocomplete)); - CHECK(!frontend.allModuleDependenciesValid("game/Gui/Modules/B", !opts.forAutocomplete)); + getFrontend().check("game/Gui/Modules/B", opts); + CHECK(getFrontend().allModuleDependenciesValid("game/Gui/Modules/B", opts.forAutocomplete)); + CHECK(!getFrontend().allModuleDependenciesValid("game/Gui/Modules/B", !opts.forAutocomplete)); opts.forAutocomplete = true; - frontend.check("game/Gui/Modules/A", opts); + getFrontend().check("game/Gui/Modules/A", opts); - CHECK(!frontend.allModuleDependenciesValid("game/Gui/Modules/B", opts.forAutocomplete)); - CHECK(frontend.allModuleDependenciesValid("game/Gui/Modules/B", !opts.forAutocomplete)); - CHECK(frontend.allModuleDependenciesValid("game/Gui/Modules/A", !opts.forAutocomplete)); - CHECK(frontend.allModuleDependenciesValid("game/Gui/Modules/A", opts.forAutocomplete)); + CHECK(!getFrontend().allModuleDependenciesValid("game/Gui/Modules/B", opts.forAutocomplete)); + CHECK(getFrontend().allModuleDependenciesValid("game/Gui/Modules/B", !opts.forAutocomplete)); + CHECK(getFrontend().allModuleDependenciesValid("game/Gui/Modules/A", !opts.forAutocomplete)); + CHECK(getFrontend().allModuleDependenciesValid("game/Gui/Modules/A", opts.forAutocomplete)); } TEST_CASE_FIXTURE(FrontendFixture, "queue_check_simple") @@ -1765,10 +1764,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "queue_check_simple") return {b_value = A.hello} )"; - frontend.queueModuleCheck("game/Gui/Modules/B"); - frontend.checkQueuedModules(); + getFrontend().queueModuleCheck("game/Gui/Modules/B"); + getFrontend().checkQueuedModules(); - auto result = frontend.getCheckResult("game/Gui/Modules/B", true); + auto result = getFrontend().getCheckResult("game/Gui/Modules/B", true); REQUIRE(result); LUAU_REQUIRE_NO_ERRORS(*result); } @@ -1788,10 +1787,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "queue_check_cycle_instant") return {b_value = A.hello} )"; - frontend.queueModuleCheck("game/Gui/Modules/B"); - frontend.checkQueuedModules(); + getFrontend().queueModuleCheck("game/Gui/Modules/B"); + getFrontend().checkQueuedModules(); - auto result = frontend.getCheckResult("game/Gui/Modules/B", true); + auto result = getFrontend().getCheckResult("game/Gui/Modules/B", true); REQUIRE(result); LUAU_REQUIRE_ERROR_COUNT(2, *result); CHECK(toString(result->errors[0]) == "Cyclic module dependency: game/Gui/Modules/B -> game/Gui/Modules/A"); @@ -1819,10 +1818,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "queue_check_cycle_delayed") return {b_value = A.hello + C.c_value} )"; - frontend.queueModuleCheck("game/Gui/Modules/B"); - frontend.checkQueuedModules(); + getFrontend().queueModuleCheck("game/Gui/Modules/B"); + getFrontend().checkQueuedModules(); - auto result = frontend.getCheckResult("game/Gui/Modules/B", true); + auto result = getFrontend().getCheckResult("game/Gui/Modules/B", true); REQUIRE(result); LUAU_REQUIRE_ERROR_COUNT(2, *result); CHECK(toString(result->errors[0]) == "Cyclic module dependency: game/Gui/Modules/B -> game/Gui/Modules/A"); @@ -1838,10 +1837,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "queue_check_propagates_ice") --!strict local a: _luau_ice = 55 )"; - frontend.markDirty(mm); - frontend.queueModuleCheck("MainModule"); + getFrontend().markDirty(mm); + getFrontend().queueModuleCheck("MainModule"); - CHECK_THROWS_AS(frontend.checkQueuedModules(), InternalCompilerError); + CHECK_THROWS_AS(getFrontend().checkQueuedModules(), InternalCompilerError); } TEST_SUITE_END(); diff --git a/tests/Generalization.test.cpp b/tests/Generalization.test.cpp index 4e2dafdf..7b3b1f37 100644 --- a/tests/Generalization.test.cpp +++ b/tests/Generalization.test.cpp @@ -15,7 +15,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(DebugLuauForbidInternalTypes) LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) @@ -227,7 +227,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "('a) -> 'a") TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization3, true}; + ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; TableType tt; tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType}; @@ -261,7 +261,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: number | string)) -> string?") TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization3, true}; + ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; auto [aTy, aFree] = freshType(); auto [bTy, bFree] = freshType(); @@ -430,7 +430,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "avoid_cross_module_mutation_in_bidirectional { ScopedFastFlag sffs[] = { {FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true}, - {FFlag::LuauEagerGeneralization3, true}, + {FFlag::LuauEagerGeneralization4, true}, }; fileResolver.source["Module/ListFns"] = R"( @@ -454,12 +454,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "avoid_cross_module_mutation_in_bidirectional return {} )"; - CheckResult result = frontend.check("Module/ListFns"); - auto modListFns = frontend.moduleResolver.getModule("Module/ListFns"); + CheckResult result = getFrontend().check("Module/ListFns"); + auto modListFns = getFrontend().moduleResolver.getModule("Module/ListFns"); freeze(modListFns->interfaceTypes); freeze(modListFns->internalTypes); LUAU_REQUIRE_NO_ERRORS(result); - CheckResult result2 = frontend.check("Module/B"); + CheckResult result2 = getFrontend().check("Module/B"); LUAU_REQUIRE_NO_ERRORS(result); } diff --git a/tests/InferPolarity.test.cpp b/tests/InferPolarity.test.cpp index d62b8b84..e09c2190 100644 --- a/tests/InferPolarity.test.cpp +++ b/tests/InferPolarity.test.cpp @@ -8,16 +8,17 @@ using namespace Luau; -LUAU_FASTFLAG(LuauEagerGeneralization3); +LUAU_FASTFLAG(LuauEagerGeneralization4); +LUAU_FASTFLAG(LuauInferPolarityOfReadWriteProperties) TEST_SUITE_BEGIN("InferPolarity"); TEST_CASE_FIXTURE(Fixture, "T where T = { m: (a) -> T }") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization3, true}; + ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; TypeArena arena; - ScopePtr globalScope = std::make_shared(builtinTypes->anyTypePack); + ScopePtr globalScope = std::make_shared(getBuiltins()->anyTypePack); TypeId tType = arena.addType(BlockedType{}); TypeId aType = arena.addType(GenericType{globalScope.get(), "a"}); @@ -48,4 +49,40 @@ TEST_CASE_FIXTURE(Fixture, "T where T = { m: (a) -> T }") CHECK(aGeneric->polarity == Polarity::Negative); } +TEST_CASE_FIXTURE(Fixture, "({ read x: a, write x: b }) -> ()") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauEagerGeneralization4, true}, + {FFlag::LuauInferPolarityOfReadWriteProperties, true}, + }; + + TypeArena arena; + ScopePtr globalScope = std::make_shared(getBuiltins()->anyTypePack); + + TypeId aType = arena.addType(GenericType{globalScope.get(), "a"}); + TypeId bType = arena.addType(GenericType{globalScope.get(), "b"}); + + TableType ttv; + ttv.state = TableState::Sealed; + ttv.props["x"] = Property::create({aType}, {bType}); + + TypeId mType = arena.addType(FunctionType{ + TypeLevel{}, + /* generics */ {aType, bType}, + /* genericPacks */ {}, + /* argPack */ arena.addTypePack({arena.addType(std::move(ttv))}), + /* retPack */ builtinTypes->emptyTypePack, + }); + + inferGenericPolarities(NotNull{&arena}, NotNull{globalScope.get()}, mType); + + const GenericType* aGeneric = get(aType); + REQUIRE(aGeneric); + CHECK(aGeneric->polarity == Polarity::Negative); + + const GenericType* bGeneric = get(bType); + REQUIRE(bGeneric); + CHECK(bGeneric->polarity == Polarity::Positive); +} + TEST_SUITE_END(); diff --git a/tests/Instantiation2.test.cpp b/tests/Instantiation2.test.cpp index fcd136fb..cda002de 100644 --- a/tests/Instantiation2.test.cpp +++ b/tests/Instantiation2.test.cpp @@ -16,7 +16,7 @@ TEST_SUITE_BEGIN("Instantiation2Test"); TEST_CASE_FIXTURE(Fixture, "weird_cyclic_instantiation") { TypeArena arena; - Scope scope(builtinTypes->anyTypePack); + Scope scope(getBuiltins()->anyTypePack); TypeId genericT = arena.addType(GenericType{"T"}); @@ -30,11 +30,11 @@ TEST_CASE_FIXTURE(Fixture, "weird_cyclic_instantiation") DenseHashMap genericSubstitutions{nullptr}; DenseHashMap genericPackSubstitutions{nullptr}; - TypeId freeTy = arena.freshType(builtinTypes, &scope); + TypeId freeTy = arena.freshType(getBuiltins(), &scope); FreeType* ft = getMutable(freeTy); REQUIRE(ft); ft->lowerBound = idTy; - ft->upperBound = builtinTypes->unknownType; + ft->upperBound = getBuiltins()->unknownType; genericSubstitutions[genericT] = freeTy; diff --git a/tests/IrLowering.test.cpp b/tests/IrLowering.test.cpp index 2a5c23fd..c8e6632a 100644 --- a/tests/IrLowering.test.cpp +++ b/tests/IrLowering.test.cpp @@ -16,6 +16,8 @@ #include #include +LUAU_FASTFLAG(LuauCodeGenSimplifyImport) + static void luauLibraryConstantLookup(const char* library, const char* member, Luau::CompileConstant* constant) { // While 'vector' library constants are a Luau built-in, their constant value depends on the embedder LUA_VECTOR_SIZE value @@ -498,6 +500,8 @@ bb_bytecode_1: TEST_CASE("DseInitialStackState") { + ScopedFastFlag luauCodeGenSimplifyImport{FFlag::LuauCodeGenSimplifyImport, true}; + CHECK_EQ( "\n" + getCodegenAssembly(R"( local function foo() @@ -518,17 +522,11 @@ bb_bytecode_0: JUMP bb_2 bb_2: CHECK_SAFE_ENV exit(3) - JUMP_EQ_TAG K1 (nil), tnil, bb_fallback_4, bb_3 -bb_3: - %9 = LOAD_TVALUE K1 (nil) - STORE_TVALUE R1, %9 - JUMP bb_5 -bb_5: + GET_CACHED_IMPORT R1, K1 (nil), 1073741824u ('_'), 4u SET_SAVEDPC 7u - %21 = NEW_TABLE 0u, 0u - STORE_POINTER R1, %21 + %14 = NEW_TABLE 0u, 0u + STORE_POINTER R1, %14 STORE_TAG R1, ttable - CHECK_GC STORE_TAG R0, tnil INTERRUPT 9u JUMP bb_bytecode_0 @@ -1569,6 +1567,8 @@ end TEST_CASE("ForInManualAnnotation") { + ScopedFastFlag luauCodeGenSimplifyImport{FFlag::LuauCodeGenSimplifyImport, true}; + CHECK_EQ( "\n" + getCodegenAssembly( R"( @@ -1601,22 +1601,17 @@ bb_bytecode_1: STORE_DOUBLE R1, 0 STORE_TAG R1, tnumber CHECK_SAFE_ENV exit(1) - JUMP_EQ_TAG K1 (nil), tnil, bb_fallback_6, bb_5 -bb_5: - %9 = LOAD_TVALUE K1 (nil) - STORE_TVALUE R2, %9 - JUMP bb_7 -bb_7: - %15 = LOAD_TVALUE R0 - STORE_TVALUE R3, %15 + GET_CACHED_IMPORT R2, K1 (nil), 1073741824u ('ipairs'), 2u + %8 = LOAD_TVALUE R0 + STORE_TVALUE R3, %8 INTERRUPT 4u SET_SAVEDPC 5u CALL R2, 1i, 3i CHECK_SAFE_ENV exit(5) - CHECK_TAG R3, ttable, bb_fallback_8 - CHECK_TAG R4, tnumber, bb_fallback_8 - JUMP_CMP_NUM R4, 0, not_eq, bb_fallback_8, bb_9 -bb_9: + CHECK_TAG R3, ttable, bb_fallback_5 + CHECK_TAG R4, tnumber, bb_fallback_5 + JUMP_CMP_NUM R4, 0, not_eq, bb_fallback_5, bb_6 +bb_6: STORE_TAG R2, tnil STORE_POINTER R4, 0i STORE_EXTRA R4, 128i @@ -1624,41 +1619,41 @@ bb_9: JUMP bb_bytecode_3 bb_bytecode_2: CHECK_TAG R6, ttable, exit(6) - %35 = LOAD_POINTER R6 - %36 = GET_SLOT_NODE_ADDR %35, 6u, K2 ('pos') - CHECK_SLOT_MATCH %36, K2 ('pos'), bb_fallback_10 - %38 = LOAD_TVALUE %36, 0i - STORE_TVALUE R8, %38 - JUMP bb_11 -bb_11: + %28 = LOAD_POINTER R6 + %29 = GET_SLOT_NODE_ADDR %28, 6u, K2 ('pos') + CHECK_SLOT_MATCH %29, K2 ('pos'), bb_fallback_7 + %31 = LOAD_TVALUE %29, 0i + STORE_TVALUE R8, %31 + JUMP bb_8 +bb_8: CHECK_TAG R8, tvector, exit(8) - %45 = LOAD_FLOAT R8, 0i - STORE_DOUBLE R7, %45 + %38 = LOAD_FLOAT R8, 0i + STORE_DOUBLE R7, %38 STORE_TAG R7, tnumber CHECK_TAG R1, tnumber, exit(10) - %52 = LOAD_DOUBLE R1 - %54 = ADD_NUM %52, %45 - STORE_DOUBLE R1, %54 + %45 = LOAD_DOUBLE R1 + %47 = ADD_NUM %45, %38 + STORE_DOUBLE R1, %47 JUMP bb_bytecode_3 bb_bytecode_3: INTERRUPT 11u - CHECK_TAG R2, tnil, bb_fallback_13 - %60 = LOAD_POINTER R3 - %61 = LOAD_INT R4 - %62 = GET_ARR_ADDR %60, %61 - CHECK_ARRAY_SIZE %60, %61, bb_12 - %64 = LOAD_TAG %62 - JUMP_EQ_TAG %64, tnil, bb_12, bb_14 -bb_14: - %66 = ADD_INT %61, 1i - STORE_INT R4, %66 - %68 = INT_TO_NUM %66 - STORE_DOUBLE R5, %68 + CHECK_TAG R2, tnil, bb_fallback_10 + %53 = LOAD_POINTER R3 + %54 = LOAD_INT R4 + %55 = GET_ARR_ADDR %53, %54 + CHECK_ARRAY_SIZE %53, %54, bb_9 + %57 = LOAD_TAG %55 + JUMP_EQ_TAG %57, tnil, bb_9, bb_11 +bb_11: + %59 = ADD_INT %54, 1i + STORE_INT R4, %59 + %61 = INT_TO_NUM %59 + STORE_DOUBLE R5, %61 STORE_TAG R5, tnumber - %71 = LOAD_TVALUE %62 - STORE_TVALUE R6, %71 + %64 = LOAD_TVALUE %55 + STORE_TVALUE R6, %64 JUMP bb_bytecode_2 -bb_12: +bb_9: INTERRUPT 13u RETURN R1, 1i )" diff --git a/tests/Linter.test.cpp b/tests/Linter.test.cpp index cbb5c3cf..656e7d7f 100644 --- a/tests/Linter.test.cpp +++ b/tests/Linter.test.cpp @@ -8,9 +8,7 @@ #include "doctest.h" LUAU_FASTFLAG(LuauSolverV2); -LUAU_FASTFLAG(LintRedundantNativeAttribute); -LUAU_FASTFLAG(LuauDeprecatedAttribute); -LUAU_FASTFLAG(LuauEagerGeneralization3); +LUAU_FASTFLAG(LuauEagerGeneralization4); using namespace Luau; @@ -51,7 +49,7 @@ TEST_CASE_FIXTURE(Fixture, "UnknownGlobal") TEST_CASE_FIXTURE(Fixture, "DeprecatedGlobal") { // Normally this would be defined externally, so hack it in for testing - addGlobalBinding(frontend.globals, "Wait", Binding{builtinTypes->anyType, {}, true, "wait", "@test/global/Wait"}); + addGlobalBinding(getFrontend().globals, "Wait", Binding{getBuiltins()->anyType, {}, true, "wait", "@test/global/Wait"}); LintResult result = lint("Wait(5)"); @@ -63,7 +61,7 @@ TEST_CASE_FIXTURE(Fixture, "DeprecatedGlobalNoReplacement") { // Normally this would be defined externally, so hack it in for testing const char* deprecationReplacementString = ""; - addGlobalBinding(frontend.globals, "Version", Binding{builtinTypes->anyType, {}, true, deprecationReplacementString}); + addGlobalBinding(getFrontend().globals, "Version", Binding{getBuiltins()->anyType, {}, true, deprecationReplacementString}); LintResult result = lint("Version()"); @@ -390,7 +388,7 @@ return bar() TEST_CASE_FIXTURE(Fixture, "ImportUnused") { // Normally this would be defined externally, so hack it in for testing - addGlobalBinding(frontend.globals, "game", builtinTypes->anyType, "@test"); + addGlobalBinding(getFrontend().globals, "game", getBuiltins()->anyType, "@test"); LintResult result = lint(R"( local Roact = require(game.Packages.Roact) @@ -621,16 +619,16 @@ return foo1 TEST_CASE_FIXTURE(Fixture, "UnknownType") { - unfreeze(frontend.globals.globalTypes); + unfreeze(getFrontend().globals.globalTypes); TableType::Props instanceProps{ - {"ClassName", {builtinTypes->anyType}}, + {"ClassName", {getBuiltins()->anyType}}, }; - TableType instanceTable{instanceProps, std::nullopt, frontend.globals.globalScope->level, Luau::TableState::Sealed}; - TypeId instanceType = frontend.globals.globalTypes.addType(instanceTable); + TableType instanceTable{instanceProps, std::nullopt, getFrontend().globals.globalScope->level, Luau::TableState::Sealed}; + TypeId instanceType = getFrontend().globals.globalTypes.addType(instanceTable); TypeFun instanceTypeFun{{}, instanceType}; - frontend.globals.globalScope->exportedTypeBindings["Part"] = instanceTypeFun; + getFrontend().globals.globalScope->exportedTypeBindings["Part"] = instanceTypeFun; LintResult result = lint(R"( local game = ... @@ -1341,10 +1339,10 @@ TEST_CASE_FIXTURE(Fixture, "no_spurious_warning_after_a_function_type_alias") TEST_CASE_FIXTURE(Fixture, "use_all_parent_scopes_for_globals") { - ScopePtr testScope = frontend.addEnvironment("Test"); - unfreeze(frontend.globals.globalTypes); - frontend.loadDefinitionFile( - frontend.globals, + ScopePtr testScope = getFrontend().addEnvironment("Test"); + unfreeze(getFrontend().globals.globalTypes); + getFrontend().loadDefinitionFile( + getFrontend().globals, testScope, R"( declare Foo: number @@ -1352,7 +1350,7 @@ TEST_CASE_FIXTURE(Fixture, "use_all_parent_scopes_for_globals") "@test", /* captureComments */ false ); - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); fileResolver.environments["A"] = "Test"; @@ -1521,32 +1519,32 @@ TEST_CASE_FIXTURE(Fixture, "LintHygieneUAF") TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiTyped") { - unfreeze(frontend.globals.globalTypes); - TypeId instanceType = frontend.globals.globalTypes.addType(ExternType{"Instance", {}, std::nullopt, std::nullopt, {}, {}, "Test", {}}); + unfreeze(getFrontend().globals.globalTypes); + TypeId instanceType = getFrontend().globals.globalTypes.addType(ExternType{"Instance", {}, std::nullopt, std::nullopt, {}, {}, "Test", {}}); persist(instanceType); - frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType}; + getFrontend().globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType}; getMutable(instanceType)->props = { - {"Name", {builtinTypes->stringType}}, - {"DataCost", {builtinTypes->numberType, /* deprecated= */ true}}, - {"Wait", {builtinTypes->anyType, /* deprecated= */ true}}, + {"Name", {getBuiltins()->stringType}}, + {"DataCost", {getBuiltins()->numberType, /* deprecated= */ true}}, + {"Wait", {getBuiltins()->anyType, /* deprecated= */ true}}, }; TypeId colorType = - frontend.globals.globalTypes.addType(TableType{{}, std::nullopt, frontend.globals.globalScope->level, Luau::TableState::Sealed}); + getFrontend().globals.globalTypes.addType(TableType{{}, std::nullopt, getFrontend().globals.globalScope->level, Luau::TableState::Sealed}); - getMutable(colorType)->props = {{"toHSV", {builtinTypes->anyType, /* deprecated= */ true, "Color3:ToHSV"}}}; + getMutable(colorType)->props = {{"toHSV", {getBuiltins()->anyType, /* deprecated= */ true, "Color3:ToHSV"}}}; - addGlobalBinding(frontend.globals, "Color3", Binding{colorType, {}}); + addGlobalBinding(getFrontend().globals, "Color3", Binding{colorType, {}}); - if (TableType* ttv = getMutable(getGlobalBinding(frontend.globals, "table"))) + if (TableType* ttv = getMutable(getGlobalBinding(getFrontend().globals, "table"))) { ttv->props["foreach"].deprecated = true; ttv->props["getn"].deprecated = true; ttv->props["getn"].deprecatedSuggestion = "#"; } - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); LintResult result = lint(R"( return function (i: Instance) @@ -1571,7 +1569,7 @@ end TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiUntyped") { - if (TableType* ttv = getMutable(getGlobalBinding(frontend.globals, "table"))) + if (TableType* ttv = getMutable(getGlobalBinding(getFrontend().globals, "table"))) { ttv->props["foreach"].deprecated = true; ttv->props["getn"].deprecated = true; @@ -2337,8 +2335,6 @@ local _ = a <= (b == 0) TEST_CASE_FIXTURE(Fixture, "RedundantNativeAttribute") { - ScopedFastFlag sff[] = {{FFlag::LintRedundantNativeAttribute, true}}; - LintResult result = lint(R"( --!native diff --git a/tests/Module.test.cpp b/tests/Module.test.cpp index 8d4704c9..47132350 100644 --- a/tests/Module.test.cpp +++ b/tests/Module.test.cpp @@ -79,22 +79,22 @@ TEST_CASE_FIXTURE(Fixture, "is_within_comment_parse_result") TEST_CASE_FIXTURE(Fixture, "dont_clone_persistent_primitive") { TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; // numberType is persistent. We leave it as-is. - TypeId newNumber = clone(builtinTypes->numberType, dest, cloneState); - CHECK_EQ(newNumber, builtinTypes->numberType); + TypeId newNumber = clone(getBuiltins()->numberType, dest, cloneState); + CHECK_EQ(newNumber, getBuiltins()->numberType); } TEST_CASE_FIXTURE(Fixture, "deepClone_non_persistent_primitive") { TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; // Create a new number type that isn't persistent - unfreeze(frontend.globals.globalTypes); - TypeId oldNumber = frontend.globals.globalTypes.addType(PrimitiveType{PrimitiveType::Number}); - freeze(frontend.globals.globalTypes); + unfreeze(getFrontend().globals.globalTypes); + TypeId oldNumber = getFrontend().globals.globalTypes.addType(PrimitiveType{PrimitiveType::Number}); + freeze(getFrontend().globals.globalTypes); TypeId newNumber = clone(oldNumber, dest, cloneState); CHECK_NE(newNumber, oldNumber); @@ -128,7 +128,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table") TypeId ty = requireType("Cyclic"); TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; TypeId cloneTy = clone(ty, dest, cloneState); TableType* ttv = getMutable(cloneTy); @@ -164,7 +164,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table_2") TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; TypeId cloneTy = clone(tableTy, dest, cloneState); TableType* ctt = getMutable(cloneTy); REQUIRE(ctt); @@ -189,7 +189,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena") dumpErrors(result); LUAU_REQUIRE_NO_ERRORS(result); - ModulePtr module = frontend.moduleResolver.getModule("MainModule"); + ModulePtr module = getFrontend().moduleResolver.getModule("MainModule"); std::optional exports = first(module->returnType); REQUIRE(bool(exports)); @@ -202,17 +202,17 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena") REQUIRE(signType != nullptr); CHECK(!isInArena(signType, module->interfaceTypes)); - CHECK(isInArena(signType, frontend.globals.globalTypes)); + CHECK(isInArena(signType, getFrontend().globals.globalTypes)); } TEST_CASE_FIXTURE(Fixture, "deepClone_union") { TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; - unfreeze(frontend.globals.globalTypes); - TypeId oldUnion = frontend.globals.globalTypes.addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType}}); - freeze(frontend.globals.globalTypes); + unfreeze(getFrontend().globals.globalTypes); + TypeId oldUnion = getFrontend().globals.globalTypes.addType(UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}}); + freeze(getFrontend().globals.globalTypes); TypeId newUnion = clone(oldUnion, dest, cloneState); CHECK_NE(newUnion, oldUnion); @@ -223,11 +223,11 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_union") TEST_CASE_FIXTURE(Fixture, "deepClone_intersection") { TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; - unfreeze(frontend.globals.globalTypes); - TypeId oldIntersection = frontend.globals.globalTypes.addType(IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}); - freeze(frontend.globals.globalTypes); + unfreeze(getFrontend().globals.globalTypes); + TypeId oldIntersection = getFrontend().globals.globalTypes.addType(IntersectionType{{getBuiltins()->numberType, getBuiltins()->stringType}}); + freeze(getFrontend().globals.globalTypes); TypeId newIntersection = clone(oldIntersection, dest, cloneState); CHECK_NE(newIntersection, oldIntersection); @@ -240,7 +240,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_class") Type exampleMetaClass{ExternType{ "ExampleClassMeta", { - {"__add", {builtinTypes->anyType}}, + {"__add", {getBuiltins()->anyType}}, }, std::nullopt, std::nullopt, @@ -252,8 +252,8 @@ TEST_CASE_FIXTURE(Fixture, "clone_class") Type exampleClass{ExternType{ "ExampleClass", { - {"PropOne", {builtinTypes->numberType}}, - {"PropTwo", {builtinTypes->stringType}}, + {"PropOne", {getBuiltins()->numberType}}, + {"PropTwo", {getBuiltins()->stringType}}, }, std::nullopt, &exampleMetaClass, @@ -264,7 +264,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_class") }}; TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; TypeId cloned = clone(&exampleClass, dest, cloneState); const ExternType* etv = get(cloned); @@ -281,16 +281,16 @@ TEST_CASE_FIXTURE(Fixture, "clone_class") TEST_CASE_FIXTURE(Fixture, "clone_free_types") { TypeArena arena; - TypeId freeTy = freshType(NotNull{&arena}, builtinTypes, nullptr); + TypeId freeTy = freshType(NotNull{&arena}, getBuiltins(), nullptr); TypePackVar freeTp(FreeTypePack{TypeLevel{}}); TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; TypeId clonedTy = clone(freeTy, dest, cloneState); CHECK(get(clonedTy)); - cloneState = {builtinTypes}; + cloneState = {getBuiltins()}; TypePackId clonedTp = clone(&freeTp, dest, cloneState); CHECK(get(clonedTp)); } @@ -302,7 +302,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_free_tables") ttv->state = TableState::Free; TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; TypeId cloned = clone(&tableTy, dest, cloneState); const TableType* clonedTtv = get(cloned); @@ -323,7 +323,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_self_property") return a; )"; - CheckResult result = frontend.check("Module/A"); + CheckResult result = getFrontend().check("Module/A"); LUAU_REQUIRE_NO_ERRORS(result); fileResolver.source["Module/B"] = R"( @@ -332,7 +332,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_self_property") return a.foo(5) )"; - result = frontend.check("Module/B"); + result = getFrontend().check("Module/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -357,7 +357,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_iteration_limit") } TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; TypeId ty = clone(table, dest, cloneState); CHECK(get(ty)); @@ -373,14 +373,14 @@ TEST_CASE_FIXTURE(Fixture, "clone_cyclic_union") { TypeArena src; - TypeId u = src.addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType}}); + TypeId u = src.addType(UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}}); UnionType* uu = getMutable(u); REQUIRE(uu); uu->options.push_back(u); TypeArena dest; - CloneState cloneState{builtinTypes}; + CloneState cloneState{getBuiltins()}; TypeId cloned = clone(u, dest, cloneState); REQUIRE(cloned); @@ -389,8 +389,8 @@ TEST_CASE_FIXTURE(Fixture, "clone_cyclic_union") REQUIRE(clonedUnion); REQUIRE(3 == clonedUnion->options.size()); - CHECK(builtinTypes->numberType == clonedUnion->options[0]); - CHECK(builtinTypes->stringType == clonedUnion->options[1]); + CHECK(getBuiltins()->numberType == clonedUnion->options[0]); + CHECK(getBuiltins()->stringType == clonedUnion->options[1]); CHECK(cloned == clonedUnion->options[2]); } @@ -403,10 +403,10 @@ type B = A FrontendOptions opts; opts.retainFullTypeGraphs = false; - CheckResult result = frontend.check("Module/A", opts); + CheckResult result = getFrontend().check("Module/A", opts); LUAU_REQUIRE_ERRORS(result); - auto mod = frontend.moduleResolver.getModule("Module/A"); + auto mod = getFrontend().moduleResolver.getModule("Module/A"); auto it = mod->exportedTypeBindings.find("A"); REQUIRE(it != mod->exportedTypeBindings.end()); @@ -429,11 +429,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_reexports") return {} )"; - CheckResult result = frontend.check("Module/B"); + CheckResult result = getFrontend().check("Module/B"); LUAU_REQUIRE_NO_ERRORS(result); - ModulePtr modA = frontend.moduleResolver.getModule("Module/A"); - ModulePtr modB = frontend.moduleResolver.getModule("Module/B"); + ModulePtr modA = getFrontend().moduleResolver.getModule("Module/A"); + ModulePtr modB = getFrontend().moduleResolver.getModule("Module/B"); REQUIRE(modA); REQUIRE(modB); auto modAiter = modA->exportedTypeBindings.find("A"); @@ -460,12 +460,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values") return exports )"; - CheckResult result = frontend.check("Module/B"); + CheckResult result = getFrontend().check("Module/B"); LUAU_REQUIRE_NO_ERRORS(result); - ModulePtr modA = frontend.moduleResolver.getModule("Module/A"); + ModulePtr modA = getFrontend().moduleResolver.getModule("Module/A"); REQUIRE(modA); - ModulePtr modB = frontend.moduleResolver.getModule("Module/B"); + ModulePtr modB = getFrontend().moduleResolver.getModule("Module/B"); REQUIRE(modB); std::optional typeA = first(modA->returnType); @@ -498,7 +498,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_table_bound_to_table_bound_to_table") getMutable(b)->boundTo = c; TypeArena dest; - CloneState state{builtinTypes}; + CloneState state{getBuiltins()}; TypeId res = clone(a, dest, state); REQUIRE(dest.types.size() == 1); @@ -513,11 +513,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_a_bound_type_to_a_persistent_type") { TypeArena arena; - TypeId boundTo = arena.addType(BoundType{builtinTypes->numberType}); - REQUIRE(builtinTypes->numberType->persistent); + TypeId boundTo = arena.addType(BoundType{getBuiltins()->numberType}); + REQUIRE(getBuiltins()->numberType->persistent); TypeArena dest; - CloneState state{builtinTypes}; + CloneState state{getBuiltins()}; TypeId res = clone(boundTo, dest, state); REQUIRE(res == follow(boundTo)); @@ -527,11 +527,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_a_bound_typepack_to_a_persistent_typep { TypeArena arena; - TypePackId boundTo = arena.addTypePack(BoundTypePack{builtinTypes->neverTypePack}); - REQUIRE(builtinTypes->neverTypePack->persistent); + TypePackId boundTo = arena.addTypePack(BoundTypePack{getBuiltins()->neverTypePack}); + REQUIRE(getBuiltins()->neverTypePack->persistent); TypeArena dest; - CloneState state{builtinTypes}; + CloneState state{getBuiltins()}; TypePackId res = clone(boundTo, dest, state); REQUIRE(res == follow(boundTo)); @@ -556,7 +556,7 @@ for i,v in x do end )"); - auto& module = frontend.moduleResolver.getModule("MainModule"); + auto& module = getFrontend().moduleResolver.getModule("MainModule"); CHECK(module->getModuleScope()->children.size() == 7); } diff --git a/tests/NonStrictTypeChecker.test.cpp b/tests/NonStrictTypeChecker.test.cpp index 3b911cc9..ca7041ef 100644 --- a/tests/NonStrictTypeChecker.test.cpp +++ b/tests/NonStrictTypeChecker.test.cpp @@ -65,7 +65,9 @@ struct NonStrictTypeCheckerFixture : Fixture NonStrictTypeCheckerFixture() { - registerHiddenTypes(&frontend); + // Force the frontend + getFrontend(); + registerHiddenTypes(getFrontend()); registerTestTypes(); } @@ -86,7 +88,7 @@ struct NonStrictTypeCheckerFixture : Fixture }; LoadDefinitionFileResult res = loadDefinition(definitions); LUAU_ASSERT(res.success); - return frontend.check(moduleName); + return getFrontend().check(moduleName); } std::string definitions = R"BUILTIN_SRC( @@ -639,14 +641,14 @@ buffer.readi8(b, 0) TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls") { - Luau::unfreeze(frontend.globals.globalTypes); - Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); + Luau::unfreeze(getFrontend().globals.globalTypes); + Luau::unfreeze(getFrontend().globalsForAutocomplete.globalTypes); - registerBuiltinGlobals(frontend, frontend.globals); + registerBuiltinGlobals(getFrontend(), getFrontend().globals); registerTestTypes(); - Luau::freeze(frontend.globals.globalTypes); - Luau::freeze(frontend.globalsForAutocomplete.globalTypes); + Luau::freeze(getFrontend().globals.globalTypes); + Luau::freeze(getFrontend().globalsForAutocomplete.globalTypes); CheckResult result = checkNonStrict(R"( local test = "test" @@ -656,7 +658,7 @@ TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls") LUAU_REQUIRE_NO_ERRORS(result); } -TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_non_strict") +TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_non_strict_1") { CheckResult result = check(Mode::Nonstrict, R"( foo = 5 diff --git a/tests/NonstrictMode.test.cpp b/tests/NonstrictMode.test.cpp index 7ff6ea37..9fcf5f55 100644 --- a/tests/NonstrictMode.test.cpp +++ b/tests/NonstrictMode.test.cpp @@ -67,7 +67,7 @@ TEST_CASE_FIXTURE(Fixture, "return_annotation_is_still_checked") LUAU_REQUIRE_ERROR_COUNT(1, result); - REQUIRE_NE(*builtinTypes->anyType, *requireType("foo")); + REQUIRE_NE(*getBuiltins()->anyType, *requireType("foo")); } #endif @@ -111,7 +111,7 @@ TEST_CASE_FIXTURE(Fixture, "locals_are_any_by_default") LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->anyType, *requireType("m")); + CHECK_EQ(*getBuiltins()->anyType, *requireType("m")); } TEST_CASE_FIXTURE(Fixture, "parameters_having_type_any_are_optional") @@ -180,7 +180,7 @@ TEST_CASE_FIXTURE(Fixture, "table_props_are_any") TypeId fooProp = ttv->props["foo"].type(); REQUIRE(fooProp != nullptr); - CHECK_EQ(*fooProp, *builtinTypes->anyType); + CHECK_EQ(*fooProp, *getBuiltins()->anyType); } TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any") @@ -200,8 +200,8 @@ TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any") TableType* ttv = getMutable(requireType("T")); REQUIRE_MESSAGE(ttv, "Should be a table: " << toString(requireType("T"))); - CHECK_EQ(*builtinTypes->anyType, *ttv->props["one"].type()); - CHECK_EQ(*builtinTypes->anyType, *ttv->props["two"].type()); + CHECK_EQ(*getBuiltins()->anyType, *ttv->props["one"].type()); + CHECK_EQ(*getBuiltins()->anyType, *ttv->props["two"].type()); CHECK_MESSAGE(get(follow(ttv->props["three"].type())), "Should be a function: " << *ttv->props["three"].type()); } diff --git a/tests/Normalize.test.cpp b/tests/Normalize.test.cpp index a48fd28b..c5449583 100644 --- a/tests/Normalize.test.cpp +++ b/tests/Normalize.test.cpp @@ -32,9 +32,9 @@ struct IsSubtypeFixture : Fixture if (!module->hasModuleScope()) FAIL("isSubtype: module scope data is not available"); - SimplifierPtr simplifier = newSimplifier(NotNull{&module->internalTypes}, builtinTypes); + SimplifierPtr simplifier = newSimplifier(NotNull{&module->internalTypes}, getBuiltins()); - return ::Luau::isSubtype(a, b, NotNull{module->getModuleScope().get()}, builtinTypes, NotNull{simplifier.get()}, ice); + return ::Luau::isSubtype(a, b, NotNull{module->getModuleScope().get()}, getBuiltins(), NotNull{simplifier.get()}, ice); } }; } // namespace @@ -325,13 +325,13 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "cyclic_table") TEST_CASE_FIXTURE(IsSubtypeFixture, "extern_types") { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); check(""); // Ensure that we have a main Module. - TypeId p = frontend.globals.globalScope->lookupType("Parent")->type; - TypeId c = frontend.globals.globalScope->lookupType("Child")->type; - TypeId u = frontend.globals.globalScope->lookupType("Unrelated")->type; + TypeId p = getFrontend().globals.globalScope->lookupType("Parent")->type; + TypeId c = getFrontend().globals.globalScope->lookupType("Child")->type; + TypeId u = getFrontend().globals.globalScope->lookupType("Unrelated")->type; CHECK(isSubtype(c, p)); CHECK(!isSubtype(p, c)); @@ -400,10 +400,10 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "error_suppression") { check(""); - TypeId any = builtinTypes->anyType; - TypeId err = builtinTypes->errorType; - TypeId str = builtinTypes->stringType; - TypeId unk = builtinTypes->unknownType; + TypeId any = getBuiltins()->anyType; + TypeId err = getBuiltins()->errorType; + TypeId str = getBuiltins()->stringType; + TypeId unk = getBuiltins()->unknownType; CHECK(!isSubtype(any, err)); CHECK(isSubtype(err, any)); @@ -447,12 +447,12 @@ struct NormalizeFixture : Fixture TypeArena arena; InternalErrorReporter iceHandler; UnifierSharedState unifierState{&iceHandler}; - Normalizer normalizer{&arena, builtinTypes, NotNull{&unifierState}}; - Scope globalScope{builtinTypes->anyTypePack}; + Normalizer normalizer{&arena, getBuiltins(), NotNull{&unifierState}}; + Scope globalScope{getBuiltins()->anyTypePack}; NormalizeFixture() { - registerHiddenTypes(&frontend); + registerHiddenTypes(getFrontend()); } std::shared_ptr toNormalizedType(const std::string& annotation, int expectedErrors = 0) @@ -698,7 +698,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "trivial_intersection_inhabited") { // this test was used to fix a bug in normalization when working with intersections/unions of the same type. - TypeId a = arena.addType(FunctionType{builtinTypes->emptyTypePack, builtinTypes->anyTypePack, std::nullopt, false}); + TypeId a = arena.addType(FunctionType{getBuiltins()->emptyTypePack, getBuiltins()->anyTypePack, std::nullopt, false}); TypeId c = arena.addType(IntersectionType{{a, a}}); std::shared_ptr n = normalizer.normalize(c); @@ -760,7 +760,7 @@ TEST_CASE_FIXTURE(Fixture, "cyclic_table_normalizes_sensibly") TEST_CASE_FIXTURE(BuiltinsFixture, "skip_force_normal_on_external_types") { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CheckResult result = check(R"( export type t0 = { a: Child } @@ -781,7 +781,7 @@ export type t0 = (((any)&({_:l0.t0,n0:t0,_G:any,}))&({_:any,}))&(((any)&({_:l0.t TEST_CASE_FIXTURE(NormalizeFixture, "unions_of_extern_types") { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CHECK("Parent | Unrelated" == toString(normal("Parent | Unrelated"))); CHECK("Parent" == toString(normal("Parent | Child"))); CHECK("Parent | Unrelated" == toString(normal("Parent | Child | Unrelated"))); @@ -789,14 +789,14 @@ TEST_CASE_FIXTURE(NormalizeFixture, "unions_of_extern_types") TEST_CASE_FIXTURE(NormalizeFixture, "intersections_of_extern_types") { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CHECK("Child" == toString(normal("Parent & Child"))); CHECK("never" == toString(normal("Child & Unrelated"))); } TEST_CASE_FIXTURE(NormalizeFixture, "narrow_union_of_extern_types_with_intersection") { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CHECK("Child" == toString(normal("(Child | Unrelated) & Child"))); } @@ -828,8 +828,8 @@ TEST_CASE_FIXTURE(NormalizeFixture, "cyclic_union") { // T where T = any & (number | T) TypeId t = arena.addType(BlockedType{}); - TypeId u = arena.addType(UnionType{{builtinTypes->numberType, t}}); - asMutable(t)->ty.emplace(IntersectionType{{builtinTypes->anyType, u}}); + TypeId u = arena.addType(UnionType{{getBuiltins()->numberType, t}}); + asMutable(t)->ty.emplace(IntersectionType{{getBuiltins()->anyType, u}}); std::shared_ptr nt = normalizer.normalize(t); REQUIRE(nt); @@ -841,8 +841,8 @@ TEST_CASE_FIXTURE(NormalizeFixture, "cyclic_union_of_intersection") { // t1 where t1 = (string & t1) | string TypeId boundTy = arena.addType(BlockedType{}); - TypeId intersectTy = arena.addType(IntersectionType{{builtinTypes->stringType, boundTy}}); - TypeId unionTy = arena.addType(UnionType{{builtinTypes->stringType, intersectTy}}); + TypeId intersectTy = arena.addType(IntersectionType{{getBuiltins()->stringType, boundTy}}); + TypeId unionTy = arena.addType(UnionType{{getBuiltins()->stringType, intersectTy}}); asMutable(boundTy)->reassign(Type{BoundType{unionTy}}); std::shared_ptr nt = normalizer.normalize(unionTy); @@ -854,8 +854,8 @@ TEST_CASE_FIXTURE(NormalizeFixture, "cyclic_intersection_of_unions") { // t1 where t1 = (string & t1) | string TypeId boundTy = arena.addType(BlockedType{}); - TypeId unionTy = arena.addType(UnionType{{builtinTypes->stringType, boundTy}}); - TypeId intersectionTy = arena.addType(IntersectionType{{builtinTypes->stringType, unionTy}}); + TypeId unionTy = arena.addType(UnionType{{getBuiltins()->stringType, boundTy}}); + TypeId intersectionTy = arena.addType(IntersectionType{{getBuiltins()->stringType, unionTy}}); asMutable(boundTy)->reassign(Type{BoundType{intersectionTy}}); std::shared_ptr nt = normalizer.normalize(intersectionTy); @@ -870,7 +870,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "crazy_metatable") TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_extern_types") { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CHECK("(Parent & ~Child) | Unrelated" == toString(normal("(Parent & Not) | Unrelated"))); CHECK("((class & ~Child) | boolean | buffer | function | number | string | table | thread)?" == toString(normal("Not"))); CHECK("never" == toString(normal("Not & Child"))); @@ -885,13 +885,13 @@ TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_extern_types") TEST_CASE_FIXTURE(NormalizeFixture, "extern_types_and_unknown") { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CHECK("Parent" == toString(normal("Parent & unknown"))); } TEST_CASE_FIXTURE(NormalizeFixture, "extern_types_and_never") { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CHECK("never" == toString(normal("Parent & never"))); } @@ -923,17 +923,17 @@ TEST_CASE_FIXTURE(NormalizeFixture, "normalize_blocked_types") TEST_CASE_FIXTURE(NormalizeFixture, "normalize_is_exactly_number") { - std::shared_ptr number = normalizer.normalize(builtinTypes->numberType); + std::shared_ptr number = normalizer.normalize(getBuiltins()->numberType); // 1. all types for which Types::number say true for, NormalizedType::isExactlyNumber should say true as well - CHECK(Luau::isNumber(builtinTypes->numberType) == number->isExactlyNumber()); + CHECK(Luau::isNumber(getBuiltins()->numberType) == number->isExactlyNumber()); // 2. isExactlyNumber should handle cases like `number & number` - TypeId intersection = arena.addType(IntersectionType{{builtinTypes->numberType, builtinTypes->numberType}}); + TypeId intersection = arena.addType(IntersectionType{{getBuiltins()->numberType, getBuiltins()->numberType}}); std::shared_ptr normIntersection = normalizer.normalize(intersection); CHECK(normIntersection->isExactlyNumber()); // 3. isExactlyNumber should reject things that are definitely not precisely numbers `number | any` - TypeId yoonion = arena.addType(UnionType{{builtinTypes->anyType, builtinTypes->numberType}}); + TypeId yoonion = arena.addType(UnionType{{getBuiltins()->anyType, getBuiltins()->numberType}}); std::shared_ptr unionIntersection = normalizer.normalize(yoonion); CHECK(!unionIntersection->isExactlyNumber()); } @@ -972,15 +972,15 @@ TEST_CASE_FIXTURE(NormalizeFixture, "read_only_props_3") TEST_CASE_FIXTURE(NormalizeFixture, "final_types_are_cached") { - std::shared_ptr na1 = normalizer.normalize(builtinTypes->numberType); - std::shared_ptr na2 = normalizer.normalize(builtinTypes->numberType); + std::shared_ptr na1 = normalizer.normalize(getBuiltins()->numberType); + std::shared_ptr na2 = normalizer.normalize(getBuiltins()->numberType); CHECK(na1 == na2); } TEST_CASE_FIXTURE(NormalizeFixture, "non_final_types_can_be_normalized_but_are_not_cached") { - TypeId a = arena.freshType(builtinTypes, &globalScope); + TypeId a = arena.freshType(getBuiltins(), &globalScope); std::shared_ptr na1 = normalizer.normalize(a); std::shared_ptr na2 = normalizer.normalize(a); @@ -990,8 +990,8 @@ TEST_CASE_FIXTURE(NormalizeFixture, "non_final_types_can_be_normalized_but_are_n TEST_CASE_FIXTURE(NormalizeFixture, "intersect_with_not_unknown") { - TypeId notUnknown = arena.addType(NegationType{builtinTypes->unknownType}); - TypeId type = arena.addType(IntersectionType{{builtinTypes->numberType, notUnknown}}); + TypeId notUnknown = arena.addType(NegationType{getBuiltins()->unknownType}); + TypeId type = arena.addType(IntersectionType{{getBuiltins()->numberType, notUnknown}}); std::shared_ptr normalized = normalizer.normalize(type); CHECK("never" == toString(normalizer.typeFromNormal(*normalized.get()))); @@ -1030,12 +1030,12 @@ TEST_CASE_FIXTURE(NormalizeFixture, "truthy_table_property_and_optional_table_wi ScopedFastFlag sff{FFlag::LuauSolverV2, true}; // { x: ~(false?) } - TypeId t1 = arena.addType(TableType{TableType::Props{{"x", builtinTypes->truthyType}}, std::nullopt, TypeLevel{}, TableState::Sealed}); + TypeId t1 = arena.addType(TableType{TableType::Props{{"x", getBuiltins()->truthyType}}, std::nullopt, TypeLevel{}, TableState::Sealed}); // { x: number? }? TypeId t2 = arena.addType(UnionType{ - {arena.addType(TableType{TableType::Props{{"x", builtinTypes->optionalNumberType}}, std::nullopt, TypeLevel{}, TableState::Sealed}), - builtinTypes->nilType} + {arena.addType(TableType{TableType::Props{{"x", getBuiltins()->optionalNumberType}}, std::nullopt, TypeLevel{}, TableState::Sealed}), + getBuiltins()->nilType} }); TypeId intersection = arena.addType(IntersectionType{{t2, t1}}); @@ -1053,8 +1053,8 @@ TEST_CASE_FIXTURE(NormalizeFixture, "free_type_and_not_truthy") {FFlag::LuauSolverV2, true}, // Only because it affects the stringification of free types }; - TypeId freeTy = arena.freshType(builtinTypes, &globalScope); - TypeId notTruthy = arena.addType(NegationType{builtinTypes->truthyType}); // ~~(false?) + TypeId freeTy = arena.freshType(getBuiltins(), &globalScope); + TypeId notTruthy = arena.addType(NegationType{getBuiltins()->truthyType}); // ~~(false?) TypeId intersectionTy = arena.addType(IntersectionType{{freeTy, notTruthy}}); // 'a & ~~(false?) @@ -1073,13 +1073,13 @@ TEST_CASE_FIXTURE(NormalizeFixture, "free_type_intersection_ordering") {FFlag::LuauNormalizationReorderFreeTypeIntersect, true}, }; - TypeId freeTy = arena.freshType(builtinTypes, &globalScope); - TypeId orderA = arena.addType(IntersectionType{{freeTy, builtinTypes->stringType}}); + TypeId freeTy = arena.freshType(getBuiltins(), &globalScope); + TypeId orderA = arena.addType(IntersectionType{{freeTy, getBuiltins()->stringType}}); auto normA = normalizer.normalize(orderA); REQUIRE(normA); CHECK_EQ("'a & string", toString(normalizer.typeFromNormal(*normA))); - TypeId orderB = arena.addType(IntersectionType{{builtinTypes->stringType, freeTy}}); + TypeId orderB = arena.addType(IntersectionType{{getBuiltins()->stringType, freeTy}}); auto normB = normalizer.normalize(orderB); REQUIRE(normB); // Prior to LuauNormalizationReorderFreeTypeIntersect this became `never` :skull: diff --git a/tests/Simplify.test.cpp b/tests/Simplify.test.cpp index 04f9d417..d6dc1ecf 100644 --- a/tests/Simplify.test.cpp +++ b/tests/Simplify.test.cpp @@ -21,30 +21,30 @@ struct SimplifyFixture : Fixture ToStringOptions opts; - Scope scope{builtinTypes->anyTypePack}; + Scope scope{getBuiltins()->anyTypePack}; - const TypeId anyTy = builtinTypes->anyType; - const TypeId unknownTy = builtinTypes->unknownType; - const TypeId neverTy = builtinTypes->neverType; - const TypeId errorTy = builtinTypes->errorType; + const TypeId anyTy = getBuiltins()->anyType; + const TypeId unknownTy = getBuiltins()->unknownType; + const TypeId neverTy = getBuiltins()->neverType; + const TypeId errorTy = getBuiltins()->errorType; - const TypeId functionTy = builtinTypes->functionType; - const TypeId tableTy = builtinTypes->tableType; + const TypeId functionTy = getBuiltins()->functionType; + const TypeId tableTy = getBuiltins()->tableType; - const TypeId numberTy = builtinTypes->numberType; - const TypeId stringTy = builtinTypes->stringType; - const TypeId booleanTy = builtinTypes->booleanType; - const TypeId nilTy = builtinTypes->nilType; + const TypeId numberTy = getBuiltins()->numberType; + const TypeId stringTy = getBuiltins()->stringType; + const TypeId booleanTy = getBuiltins()->booleanType; + const TypeId nilTy = getBuiltins()->nilType; - const TypeId classTy = builtinTypes->externType; + const TypeId classTy = getBuiltins()->externType; - const TypeId trueTy = builtinTypes->trueType; - const TypeId falseTy = builtinTypes->falseType; + const TypeId trueTy = getBuiltins()->trueType; + const TypeId falseTy = getBuiltins()->falseType; - const TypeId truthyTy = builtinTypes->truthyType; - const TypeId falsyTy = builtinTypes->falsyType; + const TypeId truthyTy = getBuiltins()->truthyType; + const TypeId falsyTy = getBuiltins()->falsyType; - const TypeId freeTy = freshType(arena, builtinTypes, &scope); + const TypeId freeTy = freshType(arena, getBuiltins(), &scope); const TypeId genericTy = arena->addType(GenericType{}); const TypeId blockedTy = arena->addType(BlockedType{}); const TypeId pendingTy = arena->addType(PendingExpansionType{{}, {}, {}, {}}); @@ -55,7 +55,7 @@ struct SimplifyFixture : Fixture const TypePackId emptyTypePack = arena->addTypePack({}); const TypeId fn1Ty = arena->addType(FunctionType{emptyTypePack, emptyTypePack}); - const TypeId fn2Ty = arena->addType(FunctionType{builtinTypes->anyTypePack, emptyTypePack}); + const TypeId fn2Ty = arena->addType(FunctionType{getBuiltins()->anyTypePack, emptyTypePack}); TypeId parentClassTy = nullptr; TypeId childClassTy = nullptr; @@ -66,17 +66,17 @@ struct SimplifyFixture : Fixture SimplifyFixture() { - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); - parentClassTy = frontend.globals.globalScope->linearSearchForBinding("Parent")->typeId; - childClassTy = frontend.globals.globalScope->linearSearchForBinding("Child")->typeId; - anotherChildClassTy = frontend.globals.globalScope->linearSearchForBinding("AnotherChild")->typeId; - unrelatedClassTy = frontend.globals.globalScope->linearSearchForBinding("Unrelated")->typeId; + parentClassTy = getFrontend().globals.globalScope->linearSearchForBinding("Parent")->typeId; + childClassTy = getFrontend().globals.globalScope->linearSearchForBinding("Child")->typeId; + anotherChildClassTy = getFrontend().globals.globalScope->linearSearchForBinding("AnotherChild")->typeId; + unrelatedClassTy = getFrontend().globals.globalScope->linearSearchForBinding("Unrelated")->typeId; } TypeId intersect(TypeId a, TypeId b) { - return simplifyIntersection(builtinTypes, arena, a, b).result; + return simplifyIntersection(getBuiltins(), arena, a, b).result; } std::string intersectStr(TypeId a, TypeId b) @@ -110,7 +110,7 @@ struct SimplifyFixture : Fixture TypeId union_(TypeId a, TypeId b) { - return simplifyUnion(builtinTypes, arena, a, b).result; + return simplifyUnion(getBuiltins(), arena, a, b).result; } }; @@ -476,7 +476,7 @@ TEST_CASE_FIXTURE(SimplifyFixture, "union") CHECK(nilTy == intersect(t1, nilTy)); // CHECK(nilTy == intersect(nilTy, t1)); // TODO? - CHECK(builtinTypes->stringType == intersect(builtinTypes->optionalStringType, truthyTy)); + CHECK(getBuiltins()->stringType == intersect(getBuiltins()->optionalStringType, truthyTy)); } TEST_CASE_FIXTURE(SimplifyFixture, "two_unions") @@ -611,12 +611,12 @@ TEST_CASE_FIXTURE(SimplifyFixture, "bound_intersected_by_itself_should_be_itself TEST_CASE_FIXTURE(SimplifyFixture, "cyclic_never_union_and_string") { // t1 where t1 = never | t1 - TypeId leftType = arena->addType(UnionType{{builtinTypes->neverType, builtinTypes->neverType}}); + TypeId leftType = arena->addType(UnionType{{getBuiltins()->neverType, getBuiltins()->neverType}}); UnionType* leftUnion = getMutable(leftType); REQUIRE(leftUnion); leftUnion->options[0] = leftType; - CHECK(builtinTypes->stringType == union_(leftType, builtinTypes->stringType)); + CHECK(getBuiltins()->stringType == union_(leftType, getBuiltins()->stringType)); } TEST_SUITE_END(); diff --git a/tests/Subtyping.test.cpp b/tests/Subtyping.test.cpp index aba64a4a..166220cd 100644 --- a/tests/Subtyping.test.cpp +++ b/tests/Subtyping.test.cpp @@ -16,7 +16,7 @@ #include LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) using namespace Luau; @@ -66,14 +66,14 @@ struct SubtypeFixture : Fixture TypeArena arena; InternalErrorReporter iceReporter; UnifierSharedState sharedState{&ice}; - SimplifierPtr simplifier = newSimplifier(NotNull{&arena}, builtinTypes); - Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; + SimplifierPtr simplifier = newSimplifier(NotNull{&arena}, getBuiltins()); + Normalizer normalizer{&arena, getBuiltins(), NotNull{&sharedState}}; TypeCheckLimits limits; TypeFunctionRuntime typeFunctionRuntime{NotNull{&iceReporter}, NotNull{&limits}}; ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - ScopePtr rootScope{new Scope(builtinTypes->emptyTypePack)}; + ScopePtr rootScope{new Scope(getBuiltins()->emptyTypePack)}; ScopePtr moduleScope{new Scope(rootScope)}; Subtyping subtyping = mkSubtyping(); @@ -82,7 +82,7 @@ struct SubtypeFixture : Fixture Subtyping mkSubtyping() { return Subtyping{ - builtinTypes, NotNull{&arena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter} + getBuiltins(), NotNull{&arena}, NotNull{simplifier.get()}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter} }; } @@ -152,7 +152,7 @@ struct SubtypeFixture : Fixture TypeId cls(const std::string& name, std::optional parent = std::nullopt) { - return arena.addType(ExternType{name, {}, parent.value_or(builtinTypes->externType), {}, {}, nullptr, "", {}}); + return arena.addType(ExternType{name, {}, parent.value_or(getBuiltins()->externType), {}, {}, nullptr, "", {}}); } TypeId cls(const std::string& name, ExternType::Props&& props) @@ -164,7 +164,7 @@ struct SubtypeFixture : Fixture TypeId opt(TypeId ty) { - return join(ty, builtinTypes->nilType); + return join(ty, getBuiltins()->nilType); } TypeId cyclicTable(std::function&& cb) @@ -202,10 +202,10 @@ struct SubtypeFixture : Fixture TypeId trueSingleton = arena.addType(SingletonType{BooleanSingleton{true}}); TypeId falseSingleton = arena.addType(SingletonType{BooleanSingleton{false}}); TypeId helloOrWorldType = join(helloType, worldType); - TypeId trueOrFalseType = join(builtinTypes->trueType, builtinTypes->falseType); + TypeId trueOrFalseType = join(getBuiltins()->trueType, getBuiltins()->falseType); TypeId helloAndWorldType = meet(helloType, worldType); - TypeId booleanAndTrueType = meet(builtinTypes->booleanType, builtinTypes->trueType); + TypeId booleanAndTrueType = meet(getBuiltins()->booleanType, getBuiltins()->trueType); /** * class @@ -228,15 +228,15 @@ struct SubtypeFixture : Fixture TypeId vec2Class = cls("Vec2", { - {"X", builtinTypes->numberType}, - {"Y", builtinTypes->numberType}, + {"X", getBuiltins()->numberType}, + {"Y", getBuiltins()->numberType}, }); TypeId readOnlyVec2Class = cls("ReadOnlyVec2", { - {"X", Property::readonly(builtinTypes->numberType)}, - {"Y", Property::readonly(builtinTypes->numberType)}, + {"X", Property::readonly(getBuiltins()->numberType)}, + {"Y", Property::readonly(getBuiltins()->numberType)}, }); // "hello" | "hello" @@ -246,67 +246,67 @@ struct SubtypeFixture : Fixture const TypeId nothingToNothingType = fn({}, {}); // (number) -> string - const TypeId numberToStringType = fn({builtinTypes->numberType}, {builtinTypes->stringType}); + const TypeId numberToStringType = fn({getBuiltins()->numberType}, {getBuiltins()->stringType}); // (unknown) -> string - const TypeId unknownToStringType = fn({builtinTypes->unknownType}, {builtinTypes->stringType}); + const TypeId unknownToStringType = fn({getBuiltins()->unknownType}, {getBuiltins()->stringType}); // (number) -> () - const TypeId numberToNothingType = fn({builtinTypes->numberType}, {}); + const TypeId numberToNothingType = fn({getBuiltins()->numberType}, {}); // () -> number - const TypeId nothingToNumberType = fn({}, {builtinTypes->numberType}); + const TypeId nothingToNumberType = fn({}, {getBuiltins()->numberType}); // (number) -> number - const TypeId numberToNumberType = fn({builtinTypes->numberType}, {builtinTypes->numberType}); + const TypeId numberToNumberType = fn({getBuiltins()->numberType}, {getBuiltins()->numberType}); // (number) -> unknown - const TypeId numberToUnknownType = fn({builtinTypes->numberType}, {builtinTypes->unknownType}); + const TypeId numberToUnknownType = fn({getBuiltins()->numberType}, {getBuiltins()->unknownType}); // (number) -> (string, string) - const TypeId numberToTwoStringsType = fn({builtinTypes->numberType}, {builtinTypes->stringType, builtinTypes->stringType}); + const TypeId numberToTwoStringsType = fn({getBuiltins()->numberType}, {getBuiltins()->stringType, getBuiltins()->stringType}); // (number) -> (string, unknown) - const TypeId numberToStringAndUnknownType = fn({builtinTypes->numberType}, {builtinTypes->stringType, builtinTypes->unknownType}); + const TypeId numberToStringAndUnknownType = fn({getBuiltins()->numberType}, {getBuiltins()->stringType, getBuiltins()->unknownType}); // (number, number) -> string - const TypeId numberNumberToStringType = fn({builtinTypes->numberType, builtinTypes->numberType}, {builtinTypes->stringType}); + const TypeId numberNumberToStringType = fn({getBuiltins()->numberType, getBuiltins()->numberType}, {getBuiltins()->stringType}); // (unknown, number) -> string - const TypeId unknownNumberToStringType = fn({builtinTypes->unknownType, builtinTypes->numberType}, {builtinTypes->stringType}); + const TypeId unknownNumberToStringType = fn({getBuiltins()->unknownType, getBuiltins()->numberType}, {getBuiltins()->stringType}); // (number, string) -> string - const TypeId numberAndStringToStringType = fn({builtinTypes->numberType, builtinTypes->stringType}, {builtinTypes->stringType}); + const TypeId numberAndStringToStringType = fn({getBuiltins()->numberType, getBuiltins()->stringType}, {getBuiltins()->stringType}); // (number, ...string) -> string const TypeId numberAndStringsToStringType = - fn({builtinTypes->numberType}, VariadicTypePack{builtinTypes->stringType}, {builtinTypes->stringType}); + fn({getBuiltins()->numberType}, VariadicTypePack{getBuiltins()->stringType}, {getBuiltins()->stringType}); // (number, ...string?) -> string const TypeId numberAndOptionalStringsToStringType = - fn({builtinTypes->numberType}, VariadicTypePack{builtinTypes->optionalStringType}, {builtinTypes->stringType}); + fn({getBuiltins()->numberType}, VariadicTypePack{getBuiltins()->optionalStringType}, {getBuiltins()->stringType}); // (...number) -> number const TypeId numbersToNumberType = - arena.addType(FunctionType{arena.addTypePack(VariadicTypePack{builtinTypes->numberType}), arena.addTypePack({builtinTypes->numberType})}); + arena.addType(FunctionType{arena.addTypePack(VariadicTypePack{getBuiltins()->numberType}), arena.addTypePack({getBuiltins()->numberType})}); // (T) -> () - const TypeId genericTToNothingType = arena.addType(FunctionType{{genericT}, {}, arena.addTypePack({genericT}), builtinTypes->emptyTypePack}); + const TypeId genericTToNothingType = arena.addType(FunctionType{{genericT}, {}, arena.addTypePack({genericT}), getBuiltins()->emptyTypePack}); // (T) -> T const TypeId genericTToTType = arena.addType(FunctionType{{genericT}, {}, arena.addTypePack({genericT}), arena.addTypePack({genericT})}); // (U) -> () - const TypeId genericUToNothingType = arena.addType(FunctionType{{genericU}, {}, arena.addTypePack({genericU}), builtinTypes->emptyTypePack}); + const TypeId genericUToNothingType = arena.addType(FunctionType{{genericU}, {}, arena.addTypePack({genericU}), getBuiltins()->emptyTypePack}); // () -> T - const TypeId genericNothingToTType = arena.addType(FunctionType{{genericT}, {}, builtinTypes->emptyTypePack, arena.addTypePack({genericT})}); + const TypeId genericNothingToTType = arena.addType(FunctionType{{genericT}, {}, getBuiltins()->emptyTypePack, arena.addTypePack({genericT})}); // (A...) -> A... const TypeId genericAsToAsType = arena.addType(FunctionType{{}, {genericAs}, genericAs, genericAs}); // (A...) -> number - const TypeId genericAsToNumberType = arena.addType(FunctionType{{}, {genericAs}, genericAs, arena.addTypePack({builtinTypes->numberType})}); + const TypeId genericAsToNumberType = arena.addType(FunctionType{{}, {genericAs}, genericAs, arena.addTypePack({getBuiltins()->numberType})}); // (B...) -> B... const TypeId genericBsToBsType = arena.addType(FunctionType{{}, {genericBs}, genericBs, genericBs}); @@ -315,10 +315,10 @@ struct SubtypeFixture : Fixture const TypeId genericBsToCsType = arena.addType(FunctionType{{}, {genericBs, genericCs}, genericBs, genericCs}); // () -> A... - const TypeId genericNothingToAsType = arena.addType(FunctionType{{}, {genericAs}, builtinTypes->emptyTypePack, genericAs}); + const TypeId genericNothingToAsType = arena.addType(FunctionType{{}, {genericAs}, getBuiltins()->emptyTypePack, genericAs}); // { lower : string -> string } - TypeId tableWithLower = tbl(TableType::Props{{"lower", fn({builtinTypes->stringType}, {builtinTypes->stringType})}}); + TypeId tableWithLower = tbl(TableType::Props{{"lower", fn({getBuiltins()->stringType}, {getBuiltins()->stringType})}}); // { insaneThingNoScalarHas : () -> () } TypeId tableWithoutScalarProp = tbl(TableType::Props{{"insaneThingNoScalarHas", fn({}, {})}}); }; @@ -408,15 +408,15 @@ TEST_SUITE_BEGIN("Subtyping"); // We would like to write numberType, builtinTypes->anyType); -TEST_IS_NOT_SUBTYPE(builtinTypes->numberType, builtinTypes->stringType); +TEST_IS_SUBTYPE(getBuiltins()->numberType, getBuiltins()->anyType); +TEST_IS_NOT_SUBTYPE(getBuiltins()->numberType, getBuiltins()->stringType); TEST_CASE_FIXTURE(SubtypeFixture, "basic_reducible_sub_type_function") { // add <: number TypeId typeFunctionNum = - arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {builtinTypes->numberType, builtinTypes->numberType}, {}}); - TypeId superTy = builtinTypes->numberType; + arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {getBuiltins()->numberType, getBuiltins()->numberType}, {}}); + TypeId superTy = getBuiltins()->numberType; SubtypingResult result = isSubtype(typeFunctionNum, superTy); CHECK(result.isSubtype); } @@ -425,8 +425,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "basic_reducible_super_type_function") { // number <: add ~ number TypeId typeFunctionNum = - arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {builtinTypes->numberType, builtinTypes->numberType}, {}}); - TypeId subTy = builtinTypes->numberType; + arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {getBuiltins()->numberType, getBuiltins()->numberType}, {}}); + TypeId subTy = getBuiltins()->numberType; SubtypingResult result = isSubtype(subTy, typeFunctionNum); CHECK(result.isSubtype); } @@ -435,8 +435,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "basic_irreducible_sub_type_function") { // add ~ never <: number TypeId typeFunctionNum = - arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {builtinTypes->stringType, builtinTypes->booleanType}, {}}); - TypeId superTy = builtinTypes->numberType; + arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {getBuiltins()->stringType, getBuiltins()->booleanType}, {}}); + TypeId superTy = getBuiltins()->numberType; SubtypingResult result = isSubtype(typeFunctionNum, superTy); CHECK(result.isSubtype); } @@ -445,8 +445,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "basic_irreducible_super_type_function") { // number <\: add ~ irreducible/never TypeId typeFunctionNum = - arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {builtinTypes->stringType, builtinTypes->booleanType}, {}}); - TypeId subTy = builtinTypes->numberType; + arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {getBuiltins()->stringType, getBuiltins()->booleanType}, {}}); + TypeId subTy = getBuiltins()->numberType; SubtypingResult result = isSubtype(subTy, typeFunctionNum); CHECK(!result.isSubtype); } @@ -457,7 +457,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "basic_type_function_with_generics") TypeId addTypeFunction = arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.addFunc}, {genericT, genericU}, {}}); FunctionType ft{{genericT, genericU}, {}, arena.addTypePack({genericT, genericU}), arena.addTypePack({addTypeFunction})}; TypeId functionType = arena.addType(std::move(ft)); - FunctionType superFt{arena.addTypePack({builtinTypes->numberType, builtinTypes->numberType}), arena.addTypePack({builtinTypes->numberType})}; + FunctionType superFt{arena.addTypePack({getBuiltins()->numberType, getBuiltins()->numberType}), arena.addTypePack({getBuiltins()->numberType})}; TypeId superFunction = arena.addType(std::move(superFt)); SubtypingResult result = isSubtype(functionType, superFunction); CHECK(result.isSubtype); @@ -465,12 +465,12 @@ TEST_CASE_FIXTURE(SubtypeFixture, "basic_type_function_with_generics") TEST_CASE_FIXTURE(SubtypeFixture, "variadic_subpath_in_pack") { - TypePackId subTArgs = arena.addTypePack(TypePack{{builtinTypes->stringType, builtinTypes->stringType}, builtinTypes->anyTypePack}); - TypePackId superTArgs = arena.addTypePack(TypePack{{builtinTypes->numberType}, builtinTypes->anyTypePack}); + TypePackId subTArgs = arena.addTypePack(TypePack{{getBuiltins()->stringType, getBuiltins()->stringType}, getBuiltins()->anyTypePack}); + TypePackId superTArgs = arena.addTypePack(TypePack{{getBuiltins()->numberType}, getBuiltins()->anyTypePack}); // (string, string, ...any) -> number - TypeId functionSub = arena.addType(FunctionType{subTArgs, arena.addTypePack({builtinTypes->numberType})}); + TypeId functionSub = arena.addType(FunctionType{subTArgs, arena.addTypePack({getBuiltins()->numberType})}); // (number, ...any) -> string - TypeId functionSuper = arena.addType(FunctionType{superTArgs, arena.addTypePack({builtinTypes->stringType})}); + TypeId functionSuper = arena.addType(FunctionType{superTArgs, arena.addTypePack({getBuiltins()->stringType})}); SubtypingResult result = isSubtype(functionSub, functionSuper); @@ -497,37 +497,37 @@ TEST_CASE_FIXTURE(SubtypeFixture, "any <: unknown") { // 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 - CHECK_IS_SUBTYPE(builtinTypes->anyType, builtinTypes->unknownType); + CHECK_IS_SUBTYPE(getBuiltins()->anyType, getBuiltins()->unknownType); } TEST_CASE_FIXTURE(SubtypeFixture, "number? <: unknown") { - CHECK_IS_SUBTYPE(builtinTypes->optionalNumberType, builtinTypes->unknownType); + CHECK_IS_SUBTYPE(getBuiltins()->optionalNumberType, getBuiltins()->unknownType); } TEST_CASE_FIXTURE(SubtypeFixture, "number <: unknown") { - CHECK_IS_SUBTYPE(builtinTypes->numberType, builtinTypes->unknownType); + CHECK_IS_SUBTYPE(getBuiltins()->numberType, getBuiltins()->unknownType); } TEST_CASE_FIXTURE(SubtypeFixture, "number <: number") { - CHECK_IS_SUBTYPE(builtinTypes->numberType, builtinTypes->numberType); + CHECK_IS_SUBTYPE(getBuiltins()->numberType, getBuiltins()->numberType); } TEST_CASE_FIXTURE(SubtypeFixture, "number <: number?") { - CHECK_IS_SUBTYPE(builtinTypes->numberType, builtinTypes->optionalNumberType); + CHECK_IS_SUBTYPE(getBuiltins()->numberType, getBuiltins()->optionalNumberType); } TEST_CASE_FIXTURE(SubtypeFixture, "\"hello\" <: string") { - CHECK_IS_SUBTYPE(helloType, builtinTypes->stringType); + CHECK_IS_SUBTYPE(helloType, getBuiltins()->stringType); } TEST_CASE_FIXTURE(SubtypeFixture, "string stringType, helloType); + CHECK_IS_NOT_SUBTYPE(getBuiltins()->stringType, helloType); } TEST_CASE_FIXTURE(SubtypeFixture, "\"hello\" <: \"hello\"") @@ -537,22 +537,22 @@ TEST_CASE_FIXTURE(SubtypeFixture, "\"hello\" <: \"hello\"") TEST_CASE_FIXTURE(SubtypeFixture, "true <: boolean") { - CHECK_IS_SUBTYPE(builtinTypes->trueType, builtinTypes->booleanType); + CHECK_IS_SUBTYPE(getBuiltins()->trueType, getBuiltins()->booleanType); } TEST_CASE_FIXTURE(SubtypeFixture, "true <: true | false") { - CHECK_IS_SUBTYPE(builtinTypes->trueType, trueOrFalseType); + CHECK_IS_SUBTYPE(getBuiltins()->trueType, trueOrFalseType); } TEST_CASE_FIXTURE(SubtypeFixture, "true | false trueType); + CHECK_IS_NOT_SUBTYPE(trueOrFalseType, getBuiltins()->trueType); } TEST_CASE_FIXTURE(SubtypeFixture, "true | false <: boolean") { - CHECK_IS_SUBTYPE(trueOrFalseType, builtinTypes->booleanType); + CHECK_IS_SUBTYPE(trueOrFalseType, getBuiltins()->booleanType); } TEST_CASE_FIXTURE(SubtypeFixture, "true | false <: true | false") @@ -562,22 +562,22 @@ TEST_CASE_FIXTURE(SubtypeFixture, "true | false <: true | false") TEST_CASE_FIXTURE(SubtypeFixture, "\"hello\" | \"world\" <: number") { - CHECK_IS_NOT_SUBTYPE(helloOrWorldType, builtinTypes->numberType); + CHECK_IS_NOT_SUBTYPE(helloOrWorldType, getBuiltins()->numberType); } TEST_CASE_FIXTURE(SubtypeFixture, "string stringType, helloOrHelloType); + CHECK_IS_NOT_SUBTYPE(getBuiltins()->stringType, helloOrHelloType); } TEST_CASE_FIXTURE(SubtypeFixture, "true <: boolean & true") { - CHECK_IS_SUBTYPE(builtinTypes->trueType, booleanAndTrueType); + CHECK_IS_SUBTYPE(getBuiltins()->trueType, booleanAndTrueType); } TEST_CASE_FIXTURE(SubtypeFixture, "boolean & true <: true") { - CHECK_IS_SUBTYPE(booleanAndTrueType, builtinTypes->trueType); + CHECK_IS_SUBTYPE(booleanAndTrueType, getBuiltins()->trueType); } TEST_CASE_FIXTURE(SubtypeFixture, "boolean & true <: boolean & true") @@ -587,12 +587,12 @@ TEST_CASE_FIXTURE(SubtypeFixture, "boolean & true <: boolean & true") TEST_CASE_FIXTURE(SubtypeFixture, "\"hello\" & \"world\" <: number") { - CHECK_IS_SUBTYPE(helloAndWorldType, builtinTypes->numberType); + CHECK_IS_SUBTYPE(helloAndWorldType, getBuiltins()->numberType); } TEST_CASE_FIXTURE(SubtypeFixture, "false falseType, booleanAndTrueType); + CHECK_IS_NOT_SUBTYPE(getBuiltins()->falseType, booleanAndTrueType); } TEST_CASE_FIXTURE(SubtypeFixture, "(unknown) -> string <: (number) -> string") @@ -734,9 +734,9 @@ TEST_CASE_FIXTURE(SubtypeFixture, "(number) -> () (T) -> ()") TEST_CASE_FIXTURE(SubtypeFixture, "() -> (T, T) (string, number)") { - TypeId nothingToTwoTs = arena.addType(FunctionType{{genericT}, {}, builtinTypes->emptyTypePack, arena.addTypePack({genericT, genericT})}); + TypeId nothingToTwoTs = arena.addType(FunctionType{{genericT}, {}, getBuiltins()->emptyTypePack, arena.addTypePack({genericT, genericT})}); - TypeId nothingToStringAndNumber = fn({}, {builtinTypes->stringType, builtinTypes->numberType}); + TypeId nothingToStringAndNumber = fn({}, {getBuiltins()->stringType, getBuiltins()->numberType}); CHECK_IS_NOT_SUBTYPE(nothingToTwoTs, nothingToStringAndNumber); } @@ -813,27 +813,27 @@ TEST_CASE_FIXTURE(SubtypeFixture, "{} <: {}") TEST_CASE_FIXTURE(SubtypeFixture, "{x: number} <: {}") { - CHECK_IS_SUBTYPE(tbl({{"x", builtinTypes->numberType}}), tbl({})); + CHECK_IS_SUBTYPE(tbl({{"x", getBuiltins()->numberType}}), tbl({})); } TEST_CASE_FIXTURE(SubtypeFixture, "{} numberType}})); + CHECK_IS_NOT_SUBTYPE(tbl({}), tbl({{"x", getBuiltins()->numberType}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{x: number} numberType}}), tbl({{"x", builtinTypes->stringType}})); + CHECK_IS_NOT_SUBTYPE(tbl({{"x", getBuiltins()->numberType}}), tbl({{"x", getBuiltins()->stringType}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{x: number} numberType}}), tbl({{"x", builtinTypes->optionalNumberType}})); + CHECK_IS_NOT_SUBTYPE(tbl({{"x", getBuiltins()->numberType}}), tbl({{"x", getBuiltins()->optionalNumberType}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{x: number?} optionalNumberType}}), tbl({{"x", builtinTypes->numberType}})); + CHECK_IS_NOT_SUBTYPE(tbl({{"x", getBuiltins()->optionalNumberType}}), tbl({{"x", getBuiltins()->numberType}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{x: (T) -> ()} <: {x: (U) -> ()}") @@ -845,43 +845,43 @@ TEST_CASE_FIXTURE(SubtypeFixture, "{ x: number } <: { read x: number }") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - CHECK_IS_SUBTYPE(tbl({{"x", builtinTypes->numberType}}), tbl({{"x", Property::readonly(builtinTypes->numberType)}})); + CHECK_IS_SUBTYPE(tbl({{"x", getBuiltins()->numberType}}), tbl({{"x", Property::readonly(getBuiltins()->numberType)}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ x: number } <: { write x: number }") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - CHECK_IS_SUBTYPE(tbl({{"x", builtinTypes->numberType}}), tbl({{"x", Property::writeonly(builtinTypes->numberType)}})); + CHECK_IS_SUBTYPE(tbl({{"x", getBuiltins()->numberType}}), tbl({{"x", Property::writeonly(getBuiltins()->numberType)}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ x: \"hello\" } <: { read x: string }") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - CHECK_IS_SUBTYPE(tbl({{"x", helloType}}), tbl({{"x", Property::readonly(builtinTypes->stringType)}})); + CHECK_IS_SUBTYPE(tbl({{"x", helloType}}), tbl({{"x", Property::readonly(getBuiltins()->stringType)}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ x: string } <: { write x: string }") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - CHECK_IS_SUBTYPE(tbl({{"x", builtinTypes->stringType}}), tbl({{"x", Property::writeonly(builtinTypes->stringType)}})); + CHECK_IS_SUBTYPE(tbl({{"x", getBuiltins()->stringType}}), tbl({{"x", Property::writeonly(getBuiltins()->stringType)}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ @metatable { x: number } } <: { @metatable {} }") { - CHECK_IS_SUBTYPE(meta({{"x", builtinTypes->numberType}}), meta({})); + CHECK_IS_SUBTYPE(meta({{"x", getBuiltins()->numberType}}), meta({})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ @metatable { x: number } } numberType}}), meta({{"x", builtinTypes->booleanType}})); + CHECK_IS_NOT_SUBTYPE(meta({{"x", getBuiltins()->numberType}}), meta({{"x", getBuiltins()->booleanType}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ @metatable {} } booleanType}})); + CHECK_IS_NOT_SUBTYPE(meta({}), meta({{"x", getBuiltins()->booleanType}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ @metatable {} } <: {}") @@ -891,92 +891,92 @@ TEST_CASE_FIXTURE(SubtypeFixture, "{ @metatable {} } <: {}") TEST_CASE_FIXTURE(SubtypeFixture, "{ @metatable { u: boolean }, x: number } <: { x: number }") { - CHECK_IS_SUBTYPE(meta({{"u", builtinTypes->booleanType}}, {{"x", builtinTypes->numberType}}), tbl({{"x", builtinTypes->numberType}})); + CHECK_IS_SUBTYPE(meta({{"u", getBuiltins()->booleanType}}, {{"x", getBuiltins()->numberType}}), tbl({{"x", getBuiltins()->numberType}})); } TEST_CASE_FIXTURE(SubtypeFixture, "{ @metatable { x: number } } numberType}}), tbl({{"x", builtinTypes->numberType}})); + CHECK_IS_NOT_SUBTYPE(meta({{"x", getBuiltins()->numberType}}), tbl({{"x", getBuiltins()->numberType}})); } -TEST_IS_SUBTYPE(builtinTypes->tableType, tbl({})); -TEST_IS_SUBTYPE(tbl({}), builtinTypes->tableType); +TEST_IS_SUBTYPE(getBuiltins()->tableType, tbl({})); +TEST_IS_SUBTYPE(tbl({}), getBuiltins()->tableType); // Negated subtypes -TEST_IS_NOT_SUBTYPE(negate(builtinTypes->neverType), builtinTypes->stringType); -TEST_IS_SUBTYPE(negate(builtinTypes->unknownType), builtinTypes->stringType); -TEST_IS_SUBTYPE(negate(builtinTypes->anyType), builtinTypes->stringType); -TEST_IS_SUBTYPE(negate(meet(builtinTypes->neverType, builtinTypes->unknownType)), builtinTypes->stringType); -TEST_IS_SUBTYPE(negate(join(builtinTypes->neverType, builtinTypes->unknownType)), builtinTypes->stringType); +TEST_IS_NOT_SUBTYPE(negate(getBuiltins()->neverType), getBuiltins()->stringType); +TEST_IS_SUBTYPE(negate(getBuiltins()->unknownType), getBuiltins()->stringType); +TEST_IS_SUBTYPE(negate(getBuiltins()->anyType), getBuiltins()->stringType); +TEST_IS_SUBTYPE(negate(meet(getBuiltins()->neverType, getBuiltins()->unknownType)), getBuiltins()->stringType); +TEST_IS_SUBTYPE(negate(join(getBuiltins()->neverType, getBuiltins()->unknownType)), getBuiltins()->stringType); // Negated supertypes: never/unknown/any/error -TEST_IS_SUBTYPE(builtinTypes->stringType, negate(builtinTypes->neverType)); -TEST_IS_SUBTYPE(builtinTypes->neverType, negate(builtinTypes->unknownType)); -TEST_IS_NOT_SUBTYPE(builtinTypes->stringType, negate(builtinTypes->unknownType)); -TEST_IS_SUBTYPE(builtinTypes->numberType, negate(builtinTypes->anyType)); -TEST_IS_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->anyType)); +TEST_IS_SUBTYPE(getBuiltins()->stringType, negate(getBuiltins()->neverType)); +TEST_IS_SUBTYPE(getBuiltins()->neverType, negate(getBuiltins()->unknownType)); +TEST_IS_NOT_SUBTYPE(getBuiltins()->stringType, negate(getBuiltins()->unknownType)); +TEST_IS_SUBTYPE(getBuiltins()->numberType, negate(getBuiltins()->anyType)); +TEST_IS_SUBTYPE(getBuiltins()->unknownType, negate(getBuiltins()->anyType)); // Negated supertypes: unions -TEST_IS_SUBTYPE(builtinTypes->booleanType, negate(join(builtinTypes->stringType, builtinTypes->numberType))); -TEST_IS_SUBTYPE(rootClass, negate(join(childClass, builtinTypes->numberType))); -TEST_IS_SUBTYPE(str("foo"), negate(join(builtinTypes->numberType, builtinTypes->booleanType))); -TEST_IS_NOT_SUBTYPE(str("foo"), negate(join(builtinTypes->stringType, builtinTypes->numberType))); -TEST_IS_NOT_SUBTYPE(childClass, negate(join(rootClass, builtinTypes->numberType))); -TEST_IS_NOT_SUBTYPE(numbersToNumberType, negate(join(builtinTypes->functionType, rootClass))); +TEST_IS_SUBTYPE(getBuiltins()->booleanType, negate(join(getBuiltins()->stringType, getBuiltins()->numberType))); +TEST_IS_SUBTYPE(rootClass, negate(join(childClass, getBuiltins()->numberType))); +TEST_IS_SUBTYPE(str("foo"), negate(join(getBuiltins()->numberType, getBuiltins()->booleanType))); +TEST_IS_NOT_SUBTYPE(str("foo"), negate(join(getBuiltins()->stringType, getBuiltins()->numberType))); +TEST_IS_NOT_SUBTYPE(childClass, negate(join(rootClass, getBuiltins()->numberType))); +TEST_IS_NOT_SUBTYPE(numbersToNumberType, negate(join(getBuiltins()->functionType, rootClass))); // Negated supertypes: intersections -TEST_IS_SUBTYPE(builtinTypes->booleanType, negate(meet(builtinTypes->stringType, str("foo")))); -TEST_IS_SUBTYPE(builtinTypes->trueType, negate(meet(builtinTypes->booleanType, builtinTypes->numberType))); -TEST_IS_SUBTYPE(rootClass, negate(meet(builtinTypes->externType, childClass))); -TEST_IS_SUBTYPE(childClass, negate(meet(builtinTypes->externType, builtinTypes->numberType))); -TEST_IS_SUBTYPE(builtinTypes->unknownType, negate(meet(builtinTypes->externType, builtinTypes->numberType))); -TEST_IS_NOT_SUBTYPE(str("foo"), negate(meet(builtinTypes->stringType, negate(str("bar"))))); +TEST_IS_SUBTYPE(getBuiltins()->booleanType, negate(meet(getBuiltins()->stringType, str("foo")))); +TEST_IS_SUBTYPE(getBuiltins()->trueType, negate(meet(getBuiltins()->booleanType, getBuiltins()->numberType))); +TEST_IS_SUBTYPE(rootClass, negate(meet(getBuiltins()->externType, childClass))); +TEST_IS_SUBTYPE(childClass, negate(meet(getBuiltins()->externType, getBuiltins()->numberType))); +TEST_IS_SUBTYPE(getBuiltins()->unknownType, negate(meet(getBuiltins()->externType, getBuiltins()->numberType))); +TEST_IS_NOT_SUBTYPE(str("foo"), negate(meet(getBuiltins()->stringType, negate(str("bar"))))); // Negated supertypes: tables and metatables -TEST_IS_SUBTYPE(tbl({}), negate(builtinTypes->numberType)); -TEST_IS_NOT_SUBTYPE(tbl({}), negate(builtinTypes->tableType)); -TEST_IS_SUBTYPE(meta({}), negate(builtinTypes->numberType)); -TEST_IS_NOT_SUBTYPE(meta({}), negate(builtinTypes->tableType)); +TEST_IS_SUBTYPE(tbl({}), negate(getBuiltins()->numberType)); +TEST_IS_NOT_SUBTYPE(tbl({}), negate(getBuiltins()->tableType)); +TEST_IS_SUBTYPE(meta({}), negate(getBuiltins()->numberType)); +TEST_IS_NOT_SUBTYPE(meta({}), negate(getBuiltins()->tableType)); // Negated supertypes: Functions -TEST_IS_SUBTYPE(numberToNumberType, negate(builtinTypes->externType)); -TEST_IS_NOT_SUBTYPE(numberToNumberType, negate(builtinTypes->functionType)); +TEST_IS_SUBTYPE(numberToNumberType, negate(getBuiltins()->externType)); +TEST_IS_NOT_SUBTYPE(numberToNumberType, negate(getBuiltins()->functionType)); // Negated supertypes: Primitives and singletons -TEST_IS_NOT_SUBTYPE(builtinTypes->stringType, negate(builtinTypes->stringType)); -TEST_IS_SUBTYPE(builtinTypes->stringType, negate(builtinTypes->numberType)); -TEST_IS_SUBTYPE(str("foo"), meet(builtinTypes->stringType, negate(str("bar")))); -TEST_IS_NOT_SUBTYPE(builtinTypes->trueType, negate(builtinTypes->booleanType)); +TEST_IS_NOT_SUBTYPE(getBuiltins()->stringType, negate(getBuiltins()->stringType)); +TEST_IS_SUBTYPE(getBuiltins()->stringType, negate(getBuiltins()->numberType)); +TEST_IS_SUBTYPE(str("foo"), meet(getBuiltins()->stringType, negate(str("bar")))); +TEST_IS_NOT_SUBTYPE(getBuiltins()->trueType, negate(getBuiltins()->booleanType)); TEST_IS_NOT_SUBTYPE(str("foo"), negate(str("foo"))); -TEST_IS_NOT_SUBTYPE(str("foo"), negate(builtinTypes->stringType)); -TEST_IS_SUBTYPE(builtinTypes->falseType, negate(builtinTypes->trueType)); -TEST_IS_SUBTYPE(builtinTypes->falseType, meet(builtinTypes->booleanType, negate(builtinTypes->trueType))); -TEST_IS_NOT_SUBTYPE(builtinTypes->stringType, meet(builtinTypes->booleanType, negate(builtinTypes->trueType))); -TEST_IS_NOT_SUBTYPE(builtinTypes->stringType, negate(str("foo"))); -TEST_IS_NOT_SUBTYPE(builtinTypes->booleanType, negate(builtinTypes->falseType)); +TEST_IS_NOT_SUBTYPE(str("foo"), negate(getBuiltins()->stringType)); +TEST_IS_SUBTYPE(getBuiltins()->falseType, negate(getBuiltins()->trueType)); +TEST_IS_SUBTYPE(getBuiltins()->falseType, meet(getBuiltins()->booleanType, negate(getBuiltins()->trueType))); +TEST_IS_NOT_SUBTYPE(getBuiltins()->stringType, meet(getBuiltins()->booleanType, negate(getBuiltins()->trueType))); +TEST_IS_NOT_SUBTYPE(getBuiltins()->stringType, negate(str("foo"))); +TEST_IS_NOT_SUBTYPE(getBuiltins()->booleanType, negate(getBuiltins()->falseType)); // Negated supertypes: extern types -TEST_IS_SUBTYPE(rootClass, negate(builtinTypes->tableType)); -TEST_IS_NOT_SUBTYPE(rootClass, negate(builtinTypes->externType)); +TEST_IS_SUBTYPE(rootClass, negate(getBuiltins()->tableType)); +TEST_IS_NOT_SUBTYPE(rootClass, negate(getBuiltins()->externType)); TEST_IS_NOT_SUBTYPE(childClass, negate(rootClass)); -TEST_IS_NOT_SUBTYPE(childClass, meet(builtinTypes->externType, negate(rootClass))); -TEST_IS_SUBTYPE(anotherChildClass, meet(builtinTypes->externType, negate(childClass))); +TEST_IS_NOT_SUBTYPE(childClass, meet(getBuiltins()->externType, negate(rootClass))); +TEST_IS_SUBTYPE(anotherChildClass, meet(getBuiltins()->externType, negate(childClass))); // Negated primitives against unknown -TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->booleanType)); -TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->numberType)); -TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->stringType)); -TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->threadType)); -TEST_IS_NOT_SUBTYPE(builtinTypes->unknownType, negate(builtinTypes->bufferType)); +TEST_IS_NOT_SUBTYPE(getBuiltins()->unknownType, negate(getBuiltins()->booleanType)); +TEST_IS_NOT_SUBTYPE(getBuiltins()->unknownType, negate(getBuiltins()->numberType)); +TEST_IS_NOT_SUBTYPE(getBuiltins()->unknownType, negate(getBuiltins()->stringType)); +TEST_IS_NOT_SUBTYPE(getBuiltins()->unknownType, negate(getBuiltins()->threadType)); +TEST_IS_NOT_SUBTYPE(getBuiltins()->unknownType, negate(getBuiltins()->bufferType)); TEST_CASE_FIXTURE(SubtypeFixture, "Root <: class") { - CHECK_IS_SUBTYPE(rootClass, builtinTypes->externType); + CHECK_IS_SUBTYPE(rootClass, getBuiltins()->externType); } TEST_CASE_FIXTURE(SubtypeFixture, "Child | AnotherChild <: class") { - CHECK_IS_SUBTYPE(join(childClass, anotherChildClass), builtinTypes->externType); + CHECK_IS_SUBTYPE(join(childClass, anotherChildClass), getBuiltins()->externType); } TEST_CASE_FIXTURE(SubtypeFixture, "Child | AnotherChild <: Child | AnotherChild") @@ -991,33 +991,33 @@ TEST_CASE_FIXTURE(SubtypeFixture, "Child | Root <: Root") TEST_CASE_FIXTURE(SubtypeFixture, "Child & AnotherChild <: class") { - CHECK_IS_SUBTYPE(meet(childClass, anotherChildClass), builtinTypes->externType); + CHECK_IS_SUBTYPE(meet(childClass, anotherChildClass), getBuiltins()->externType); } TEST_CASE_FIXTURE(SubtypeFixture, "Child & Root <: class") { - CHECK_IS_SUBTYPE(meet(childClass, rootClass), builtinTypes->externType); + CHECK_IS_SUBTYPE(meet(childClass, rootClass), getBuiltins()->externType); } TEST_CASE_FIXTURE(SubtypeFixture, "Child & ~Root <: class") { - CHECK_IS_SUBTYPE(meet(childClass, negate(rootClass)), builtinTypes->externType); + CHECK_IS_SUBTYPE(meet(childClass, negate(rootClass)), getBuiltins()->externType); } TEST_CASE_FIXTURE(SubtypeFixture, "Child & AnotherChild <: number") { - CHECK_IS_SUBTYPE(meet(childClass, anotherChildClass), builtinTypes->numberType); + CHECK_IS_SUBTYPE(meet(childClass, anotherChildClass), getBuiltins()->numberType); } TEST_CASE_FIXTURE(SubtypeFixture, "Child & ~GrandchildOne numberType); + CHECK_IS_NOT_SUBTYPE(meet(childClass, negate(grandchildOneClass)), getBuiltins()->numberType); } TEST_CASE_FIXTURE(SubtypeFixture, "semantic_subtyping_disj") { - TypeId subTy = builtinTypes->unknownType; - TypeId superTy = join(negate(builtinTypes->numberType), negate(builtinTypes->stringType)); + TypeId subTy = getBuiltins()->unknownType; + TypeId superTy = join(negate(getBuiltins()->numberType), negate(getBuiltins()->stringType)); SubtypingResult result = isSubtype(subTy, superTy); CHECK(result.isSubtype); } @@ -1028,14 +1028,14 @@ TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} <: t2 wh TypeId t1 = cyclicTable( [&](TypeId ty, TableType* tt) { - tt->props["trim"] = fn({ty}, {builtinTypes->stringType}); + tt->props["trim"] = fn({ty}, {getBuiltins()->stringType}); } ); TypeId t2 = cyclicTable( [&](TypeId ty, TableType* tt) { - tt->props["trim"] = fn({ty}, {builtinTypes->stringType}); + tt->props["trim"] = fn({ty}, {getBuiltins()->stringType}); } ); @@ -1047,7 +1047,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> string} props["trim"] = fn({ty}, {builtinTypes->stringType}); + tt->props["trim"] = fn({ty}, {getBuiltins()->stringType}); } ); @@ -1073,7 +1073,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> t1} props["trim"] = fn({ty}, {builtinTypes->stringType}); + tt->props["trim"] = fn({ty}, {getBuiltins()->stringType}); } ); @@ -1083,8 +1083,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "t1 where t1 = {trim: (t1) -> t1} numberType}, - {"Y", builtinTypes->numberType}, + {"X", getBuiltins()->numberType}, + {"Y", getBuiltins()->numberType}, }); CHECK_IS_SUBTYPE(vec2Class, xy); @@ -1093,7 +1093,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "Vec2 <: { X: number, Y: number }") TEST_CASE_FIXTURE(SubtypeFixture, "Vec2 <: { X: number }") { TypeId x = tbl({ - {"X", builtinTypes->numberType}, + {"X", getBuiltins()->numberType}, }); CHECK_IS_SUBTYPE(vec2Class, x); @@ -1102,8 +1102,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "Vec2 <: { X: number }") TEST_CASE_FIXTURE(SubtypeFixture, "{ X: number, Y: number } numberType}, - {"Y", builtinTypes->numberType}, + {"X", getBuiltins()->numberType}, + {"Y", getBuiltins()->numberType}, }); CHECK_IS_NOT_SUBTYPE(xy, vec2Class); @@ -1112,7 +1112,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "{ X: number, Y: number } numberType}, + {"X", getBuiltins()->numberType}, }); CHECK_IS_NOT_SUBTYPE(x, vec2Class); @@ -1121,36 +1121,36 @@ TEST_CASE_FIXTURE(SubtypeFixture, "{ X: number } numberType}, - {"Y", builtinTypes->numberType}, + {"X", getBuiltins()->numberType}, + {"Y", getBuiltins()->numberType}, }); - CHECK_IS_NOT_SUBTYPE(meet(builtinTypes->tableType, x), vec2Class); + CHECK_IS_NOT_SUBTYPE(meet(getBuiltins()->tableType, x), vec2Class); } TEST_CASE_FIXTURE(SubtypeFixture, "Vec2 numberType}, - {"Y", builtinTypes->numberType}, + {"X", getBuiltins()->numberType}, + {"Y", getBuiltins()->numberType}, }); - CHECK_IS_NOT_SUBTYPE(vec2Class, meet(builtinTypes->tableType, xy)); + CHECK_IS_NOT_SUBTYPE(vec2Class, meet(getBuiltins()->tableType, xy)); } TEST_CASE_FIXTURE(SubtypeFixture, "ReadOnlyVec2 numberType}, {"Y", builtinTypes->numberType}})); + CHECK_IS_NOT_SUBTYPE(readOnlyVec2Class, tbl({{"X", getBuiltins()->numberType}, {"Y", getBuiltins()->numberType}})); } TEST_CASE_FIXTURE(SubtypeFixture, "ReadOnlyVec2 <: { read X: number, read Y: number}") { CHECK_IS_SUBTYPE( - readOnlyVec2Class, tbl({{"X", Property::readonly(builtinTypes->numberType)}, {"Y", Property::readonly(builtinTypes->numberType)}}) + readOnlyVec2Class, tbl({{"X", Property::readonly(getBuiltins()->numberType)}, {"Y", Property::readonly(getBuiltins()->numberType)}}) ); } -TEST_IS_SUBTYPE(vec2Class, tbl({{"X", Property::readonly(builtinTypes->numberType)}, {"Y", Property::readonly(builtinTypes->numberType)}})); +TEST_IS_SUBTYPE(vec2Class, tbl({{"X", Property::readonly(getBuiltins()->numberType)}, {"Y", Property::readonly(getBuiltins()->numberType)}})); TEST_IS_NOT_SUBTYPE(tbl({{"P", grandchildOneClass}}), tbl({{"P", Property::rw(rootClass)}})); TEST_IS_SUBTYPE(tbl({{"P", grandchildOneClass}}), tbl({{"P", Property::readonly(rootClass)}})); @@ -1173,55 +1173,55 @@ TEST_CASE_FIXTURE(SubtypeFixture, "\"hello\" string }") { - CHECK_IS_SUBTYPE(builtinTypes->stringType, tableWithLower); + CHECK_IS_SUBTYPE(getBuiltins()->stringType, tableWithLower); } TEST_CASE_FIXTURE(SubtypeFixture, "string () }") { - CHECK_IS_NOT_SUBTYPE(builtinTypes->stringType, tableWithoutScalarProp); + CHECK_IS_NOT_SUBTYPE(getBuiltins()->stringType, tableWithoutScalarProp); } TEST_CASE_FIXTURE(SubtypeFixture, "~fun & (string) -> number <: (string) -> number") { - CHECK_IS_SUBTYPE(meet(negate(builtinTypes->functionType), numberToStringType), numberToStringType); + CHECK_IS_SUBTYPE(meet(negate(getBuiltins()->functionType), numberToStringType), numberToStringType); } TEST_CASE_FIXTURE(SubtypeFixture, "(string) -> number <: ~fun & (string) -> number") { - CHECK_IS_NOT_SUBTYPE(numberToStringType, meet(negate(builtinTypes->functionType), numberToStringType)); + CHECK_IS_NOT_SUBTYPE(numberToStringType, meet(negate(getBuiltins()->functionType), numberToStringType)); } TEST_CASE_FIXTURE(SubtypeFixture, "~\"a\" & ~\"b\" & string <: { lower : (string) -> ()}") { - CHECK_IS_SUBTYPE(meet(meet(negate(aType), negate(bType)), builtinTypes->stringType), tableWithLower); + CHECK_IS_SUBTYPE(meet(meet(negate(aType), negate(bType)), getBuiltins()->stringType), tableWithLower); } TEST_CASE_FIXTURE(SubtypeFixture, "\"a\" | (~\"b\" & string) <: { lower : (string) -> ()}") { - CHECK_IS_SUBTYPE(join(aType, meet(negate(bType), builtinTypes->stringType)), tableWithLower); + CHECK_IS_SUBTYPE(join(aType, meet(negate(bType), getBuiltins()->stringType)), tableWithLower); } TEST_CASE_FIXTURE(SubtypeFixture, "(string | number) & (\"a\" | true) <: { lower: (string) -> string }") { - auto base = meet(join(builtinTypes->stringType, builtinTypes->numberType), join(aType, trueSingleton)); + auto base = meet(join(getBuiltins()->stringType, getBuiltins()->numberType), join(aType, trueSingleton)); CHECK_IS_SUBTYPE(base, tableWithLower); } TEST_CASE_FIXTURE(SubtypeFixture, "number <: ~~number") { - CHECK_IS_SUBTYPE(builtinTypes->numberType, negate(negate(builtinTypes->numberType))); + CHECK_IS_SUBTYPE(getBuiltins()->numberType, negate(negate(getBuiltins()->numberType))); } TEST_CASE_FIXTURE(SubtypeFixture, "~~number <: number") { - CHECK_IS_SUBTYPE(negate(negate(builtinTypes->numberType)), builtinTypes->numberType); + CHECK_IS_SUBTYPE(negate(negate(getBuiltins()->numberType)), getBuiltins()->numberType); } // See https://github.com/luau-lang/luau/issues/767 TEST_CASE_FIXTURE(SubtypeFixture, "(...any) -> () <: (T...) -> ()") { - TypeId anysToNothing = arena.addType(FunctionType{builtinTypes->anyTypePack, builtinTypes->emptyTypePack}); - TypeId genericTToAnys = arena.addType(FunctionType{genericAs, builtinTypes->emptyTypePack}); + TypeId anysToNothing = arena.addType(FunctionType{getBuiltins()->anyTypePack, getBuiltins()->emptyTypePack}); + TypeId genericTToAnys = arena.addType(FunctionType{genericAs, getBuiltins()->emptyTypePack}); CHECK_MESSAGE(isSubtype(anysToNothing, genericTToAnys).isSubtype, "(...any) -> () <: (T...) -> ()"); } @@ -1230,8 +1230,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "(...any) -> () <: (T...) -> ()") TEST_CASE_FIXTURE(SubtypeFixture, "(...unknown) -> () <: (T...) -> ()") { TypeId unknownsToNothing = - arena.addType(FunctionType{arena.addTypePack(VariadicTypePack{builtinTypes->unknownType}), builtinTypes->emptyTypePack}); - TypeId genericTToAnys = arena.addType(FunctionType{genericAs, builtinTypes->emptyTypePack}); + arena.addType(FunctionType{arena.addTypePack(VariadicTypePack{getBuiltins()->unknownType}), getBuiltins()->emptyTypePack}); + TypeId genericTToAnys = arena.addType(FunctionType{genericAs, getBuiltins()->emptyTypePack}); CHECK_MESSAGE(isSubtype(unknownsToNothing, genericTToAnys).isSubtype, "(...unknown) -> () <: (T...) -> ()"); } @@ -1239,11 +1239,11 @@ TEST_CASE_FIXTURE(SubtypeFixture, "(...unknown) -> () <: (T...) -> ()") TEST_CASE_FIXTURE(SubtypeFixture, "bill") { TypeId a = arena.addType(TableType{ - {{"a", builtinTypes->stringType}}, TableIndexer{builtinTypes->stringType, builtinTypes->numberType}, TypeLevel{}, nullptr, TableState::Sealed + {{"a", getBuiltins()->stringType}}, TableIndexer{getBuiltins()->stringType, getBuiltins()->numberType}, TypeLevel{}, nullptr, TableState::Sealed }); TypeId b = arena.addType(TableType{ - {{"a", builtinTypes->stringType}}, TableIndexer{builtinTypes->stringType, builtinTypes->numberType}, TypeLevel{}, nullptr, TableState::Sealed + {{"a", getBuiltins()->stringType}}, TableIndexer{getBuiltins()->stringType, getBuiltins()->numberType}, TypeLevel{}, nullptr, TableState::Sealed }); CHECK(isSubtype(a, b).isSubtype); @@ -1255,14 +1255,14 @@ TEST_CASE_FIXTURE(SubtypeFixture, "({[string]: number, a: string}) -> () <: ({[s auto makeTheType = [&]() { TypeId argType = arena.addType(TableType{ - {{"a", builtinTypes->stringType}}, - TableIndexer{builtinTypes->stringType, builtinTypes->numberType}, + {{"a", getBuiltins()->stringType}}, + TableIndexer{getBuiltins()->stringType, getBuiltins()->numberType}, TypeLevel{}, nullptr, TableState::Sealed }); - return arena.addType(FunctionType{arena.addTypePack({argType}), builtinTypes->emptyTypePack}); + return arena.addType(FunctionType{arena.addTypePack({argType}), getBuiltins()->emptyTypePack}); }; TypeId a = makeTheType(); @@ -1285,61 +1285,61 @@ TEST_CASE_FIXTURE(SubtypeFixture, "unknown <: X") TypeId genericX = arena.addType(GenericType(childScope.get(), "X")); - SubtypingResult usingGlobalScope = isSubtype(builtinTypes->unknownType, genericX); - CHECK_MESSAGE(!usingGlobalScope.isSubtype, "Expected " << builtinTypes->unknownType << " unknownType, genericX); + CHECK_MESSAGE(!usingGlobalScope.isSubtype, "Expected " << getBuiltins()->unknownType << " unknownType, genericX, NotNull{childScope.get()}); - CHECK_MESSAGE(usingChildScope.isSubtype, "Expected " << builtinTypes->unknownType << " <: " << genericX); + SubtypingResult usingChildScope = childSubtyping.isSubtype(getBuiltins()->unknownType, genericX, NotNull{childScope.get()}); + CHECK_MESSAGE(usingChildScope.isSubtype, "Expected " << getBuiltins()->unknownType << " <: " << genericX); Subtyping grandChildSubtyping{mkSubtyping()}; - SubtypingResult usingGrandChildScope = grandChildSubtyping.isSubtype(builtinTypes->unknownType, genericX, NotNull{grandChildScope.get()}); - CHECK_MESSAGE(usingGrandChildScope.isSubtype, "Expected " << builtinTypes->unknownType << " <: " << genericX); + SubtypingResult usingGrandChildScope = grandChildSubtyping.isSubtype(getBuiltins()->unknownType, genericX, NotNull{grandChildScope.get()}); + CHECK_MESSAGE(usingGrandChildScope.isSubtype, "Expected " << getBuiltins()->unknownType << " <: " << genericX); } -TEST_IS_SUBTYPE(idx(builtinTypes->numberType, builtinTypes->numberType), tbl({})); -TEST_IS_NOT_SUBTYPE(tbl({}), idx(builtinTypes->numberType, builtinTypes->numberType)); +TEST_IS_SUBTYPE(idx(getBuiltins()->numberType, getBuiltins()->numberType), tbl({})); +TEST_IS_NOT_SUBTYPE(tbl({}), idx(getBuiltins()->numberType, getBuiltins()->numberType)); -TEST_IS_NOT_SUBTYPE(tbl({{"X", builtinTypes->numberType}}), idx(builtinTypes->numberType, builtinTypes->numberType)); -TEST_IS_NOT_SUBTYPE(idx(builtinTypes->numberType, builtinTypes->numberType), tbl({{"X", builtinTypes->numberType}})); +TEST_IS_NOT_SUBTYPE(tbl({{"X", getBuiltins()->numberType}}), idx(getBuiltins()->numberType, getBuiltins()->numberType)); +TEST_IS_NOT_SUBTYPE(idx(getBuiltins()->numberType, getBuiltins()->numberType), tbl({{"X", getBuiltins()->numberType}})); TEST_IS_NOT_SUBTYPE( - idx(join(builtinTypes->numberType, builtinTypes->stringType), builtinTypes->numberType), - idx(builtinTypes->numberType, builtinTypes->numberType) + idx(join(getBuiltins()->numberType, getBuiltins()->stringType), getBuiltins()->numberType), + idx(getBuiltins()->numberType, getBuiltins()->numberType) ); TEST_IS_NOT_SUBTYPE( - idx(builtinTypes->numberType, builtinTypes->numberType), - idx(join(builtinTypes->numberType, builtinTypes->stringType), builtinTypes->numberType) + idx(getBuiltins()->numberType, getBuiltins()->numberType), + idx(join(getBuiltins()->numberType, getBuiltins()->stringType), getBuiltins()->numberType) ); TEST_IS_NOT_SUBTYPE( - idx(builtinTypes->numberType, join(builtinTypes->stringType, builtinTypes->numberType)), - idx(builtinTypes->numberType, builtinTypes->numberType) + idx(getBuiltins()->numberType, join(getBuiltins()->stringType, getBuiltins()->numberType)), + idx(getBuiltins()->numberType, getBuiltins()->numberType) ); TEST_IS_NOT_SUBTYPE( - idx(builtinTypes->numberType, builtinTypes->numberType), - idx(builtinTypes->numberType, join(builtinTypes->stringType, builtinTypes->numberType)) + idx(getBuiltins()->numberType, getBuiltins()->numberType), + idx(getBuiltins()->numberType, join(getBuiltins()->stringType, getBuiltins()->numberType)) ); -TEST_IS_NOT_SUBTYPE(tbl({{"X", builtinTypes->numberType}}), idx(builtinTypes->stringType, builtinTypes->numberType)); -TEST_IS_SUBTYPE(idx(builtinTypes->stringType, builtinTypes->numberType), tbl({{"X", builtinTypes->numberType}})); +TEST_IS_NOT_SUBTYPE(tbl({{"X", getBuiltins()->numberType}}), idx(getBuiltins()->stringType, getBuiltins()->numberType)); +TEST_IS_SUBTYPE(idx(getBuiltins()->stringType, getBuiltins()->numberType), tbl({{"X", getBuiltins()->numberType}})); -TEST_IS_NOT_SUBTYPE(tbl({{"X", opt(builtinTypes->numberType)}}), idx(builtinTypes->stringType, builtinTypes->numberType)); -TEST_IS_NOT_SUBTYPE(idx(builtinTypes->stringType, builtinTypes->numberType), tbl({{"X", opt(builtinTypes->numberType)}})); +TEST_IS_NOT_SUBTYPE(tbl({{"X", opt(getBuiltins()->numberType)}}), idx(getBuiltins()->stringType, getBuiltins()->numberType)); +TEST_IS_NOT_SUBTYPE(idx(getBuiltins()->stringType, getBuiltins()->numberType), tbl({{"X", opt(getBuiltins()->numberType)}})); -TEST_IS_SUBTYPE(tbl({{"X", builtinTypes->numberType}, {"Y", builtinTypes->numberType}}), tbl({{"X", builtinTypes->numberType}})); -TEST_IS_NOT_SUBTYPE(tbl({{"X", builtinTypes->numberType}}), tbl({{"X", builtinTypes->numberType}, {"Y", builtinTypes->numberType}})); +TEST_IS_SUBTYPE(tbl({{"X", getBuiltins()->numberType}, {"Y", getBuiltins()->numberType}}), tbl({{"X", getBuiltins()->numberType}})); +TEST_IS_NOT_SUBTYPE(tbl({{"X", getBuiltins()->numberType}}), tbl({{"X", getBuiltins()->numberType}, {"Y", getBuiltins()->numberType}})); TEST_CASE_FIXTURE(SubtypeFixture, "interior_tests_are_cached") { - TypeId tableA = tbl({{"X", builtinTypes->numberType}, {"Y", builtinTypes->numberType}}); - TypeId tableB = tbl({{"X", builtinTypes->optionalNumberType}, {"Y", builtinTypes->optionalNumberType}}); + TypeId tableA = tbl({{"X", getBuiltins()->numberType}, {"Y", getBuiltins()->numberType}}); + TypeId tableB = tbl({{"X", getBuiltins()->optionalNumberType}, {"Y", getBuiltins()->optionalNumberType}}); CHECK_IS_NOT_SUBTYPE(tableA, tableB); - const SubtypingResult* cachedResult = subtyping.peekCache().find({builtinTypes->numberType, builtinTypes->optionalNumberType}); + const SubtypingResult* cachedResult = subtyping.peekCache().find({getBuiltins()->numberType, getBuiltins()->optionalNumberType}); REQUIRE(cachedResult); CHECK(cachedResult->isSubtype); @@ -1379,7 +1379,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "({ x: T }) -> T <: ({ method: ({ x: T } TypeId tableToPropType = arena.addType(FunctionType{{genericT}, {}, arena.addTypePack({tbl({{"x", genericT}})}), arena.addTypePack({genericT})}); // ({ method: ({ x: T }) -> T, x: number }) -> number - TypeId otherType = fn({tbl({{"method", tableToPropType}, {"x", builtinTypes->numberType}})}, {builtinTypes->numberType}); + TypeId otherType = fn({tbl({{"method", tableToPropType}, {"x", getBuiltins()->numberType}})}, {getBuiltins()->numberType}); CHECK_IS_SUBTYPE(tableToPropType, otherType); } @@ -1387,19 +1387,19 @@ TEST_CASE_FIXTURE(SubtypeFixture, "({ x: T }) -> T <: ({ method: ({ x: T } TEST_CASE_FIXTURE(SubtypeFixture, "subtyping_reasonings_to_follow_a_reduced_type_function_instance") { TypeId longTy = arena.addType(UnionType{ - {builtinTypes->booleanType, - builtinTypes->bufferType, - builtinTypes->externType, - builtinTypes->functionType, - builtinTypes->numberType, - builtinTypes->stringType, - builtinTypes->tableType, - builtinTypes->threadType} + {getBuiltins()->booleanType, + getBuiltins()->bufferType, + getBuiltins()->externType, + getBuiltins()->functionType, + getBuiltins()->numberType, + getBuiltins()->stringType, + getBuiltins()->tableType, + getBuiltins()->threadType} }); - TypeId tblTy = tbl({{"depth", builtinTypes->unknownType}}); + TypeId tblTy = tbl({{"depth", getBuiltins()->unknownType}}); TypeId combined = meet(longTy, tblTy); - TypeId subTy = arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.unionFunc}, {combined, builtinTypes->neverType}, {}}); - TypeId superTy = builtinTypes->neverType; + TypeId subTy = arena.addType(TypeFunctionInstanceType{NotNull{&builtinTypeFunctions.unionFunc}, {combined, getBuiltins()->neverType}, {}}); + TypeId superTy = getBuiltins()->neverType; SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1408,8 +1408,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "subtyping_reasonings_to_follow_a_reduced_type if (reasoning.subPath.empty() && reasoning.superPath.empty()) continue; - std::optional optSubLeaf = traverse(subTy, reasoning.subPath, builtinTypes); - std::optional optSuperLeaf = traverse(superTy, reasoning.superPath, builtinTypes); + std::optional optSubLeaf = traverse(subTy, reasoning.subPath, getBuiltins()); + std::optional optSuperLeaf = traverse(superTy, reasoning.superPath, getBuiltins()); if (!optSubLeaf || !optSuperLeaf) CHECK(false); @@ -1422,8 +1422,8 @@ TEST_SUITE_BEGIN("Subtyping.Subpaths"); TEST_CASE_FIXTURE(SubtypeFixture, "table_property") { - TypeId subTy = tbl({{"X", builtinTypes->numberType}}); - TypeId superTy = tbl({{"X", builtinTypes->booleanType}}); + TypeId subTy = tbl({{"X", getBuiltins()->numberType}}); + TypeId superTy = tbl({{"X", getBuiltins()->booleanType}}); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1439,8 +1439,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "table_property") TEST_CASE_FIXTURE(SubtypeFixture, "table_indexers") { - TypeId subTy = idx(builtinTypes->numberType, builtinTypes->stringType); - TypeId superTy = idx(builtinTypes->stringType, builtinTypes->numberType); + TypeId subTy = idx(getBuiltins()->numberType, getBuiltins()->stringType); + TypeId superTy = idx(getBuiltins()->stringType, getBuiltins()->numberType); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1463,8 +1463,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "table_indexers") TEST_CASE_FIXTURE(SubtypeFixture, "fn_arguments") { - TypeId subTy = fn({builtinTypes->numberType}, {}); - TypeId superTy = fn({builtinTypes->stringType}, {}); + TypeId subTy = fn({getBuiltins()->numberType}, {}); + TypeId superTy = fn({getBuiltins()->stringType}, {}); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1479,7 +1479,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fn_arguments") TEST_CASE_FIXTURE(SubtypeFixture, "arity_mismatch") { - TypeId subTy = fn({builtinTypes->numberType}, {}); + TypeId subTy = fn({getBuiltins()->numberType}, {}); TypeId superTy = fn({}, {}); SubtypingResult result = isSubtype(subTy, superTy); @@ -1495,8 +1495,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "arity_mismatch") TEST_CASE_FIXTURE(SubtypeFixture, "fn_arguments_tail") { - TypeId subTy = fn({}, VariadicTypePack{builtinTypes->numberType}, {}); - TypeId superTy = fn({}, VariadicTypePack{builtinTypes->stringType}, {}); + TypeId subTy = fn({}, VariadicTypePack{getBuiltins()->numberType}, {}); + TypeId superTy = fn({}, VariadicTypePack{getBuiltins()->stringType}, {}); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1511,8 +1511,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fn_arguments_tail") TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets") { - TypeId subTy = fn({}, {builtinTypes->numberType}); - TypeId superTy = fn({}, {builtinTypes->stringType}); + TypeId subTy = fn({}, {getBuiltins()->numberType}); + TypeId superTy = fn({}, {getBuiltins()->stringType}); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1527,8 +1527,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets") TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets_tail") { - TypeId subTy = fn({}, {}, VariadicTypePack{builtinTypes->numberType}); - TypeId superTy = fn({}, {}, VariadicTypePack{builtinTypes->stringType}); + TypeId subTy = fn({}, {}, VariadicTypePack{getBuiltins()->numberType}); + TypeId superTy = fn({}, {}, VariadicTypePack{getBuiltins()->stringType}); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1543,8 +1543,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "fn_rets_tail") TEST_CASE_FIXTURE(SubtypeFixture, "nested_table_properties") { - TypeId subTy = tbl({{"X", tbl({{"Y", tbl({{"Z", builtinTypes->numberType}})}})}}); - TypeId superTy = tbl({{"X", tbl({{"Y", tbl({{"Z", builtinTypes->stringType}})}})}}); + TypeId subTy = tbl({{"X", tbl({{"Y", tbl({{"Z", getBuiltins()->numberType}})}})}}); + TypeId superTy = tbl({{"X", tbl({{"Y", tbl({{"Z", getBuiltins()->stringType}})}})}}); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1560,8 +1560,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "nested_table_properties") TEST_CASE_FIXTURE(SubtypeFixture, "string_table_mt") { - TypeId subTy = builtinTypes->stringType; - TypeId superTy = tbl({{"X", builtinTypes->numberType}}); + TypeId subTy = getBuiltins()->stringType; + TypeId superTy = tbl({{"X", getBuiltins()->numberType}}); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1579,8 +1579,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "string_table_mt") TEST_CASE_FIXTURE(SubtypeFixture, "negation") { - TypeId subTy = builtinTypes->numberType; - TypeId superTy = negate(builtinTypes->numberType); + TypeId subTy = getBuiltins()->numberType; + TypeId superTy = negate(getBuiltins()->numberType); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1594,8 +1594,8 @@ TEST_CASE_FIXTURE(SubtypeFixture, "negation") TEST_CASE_FIXTURE(SubtypeFixture, "multiple_reasonings") { - TypeId subTy = tbl({{"X", builtinTypes->stringType}, {"Y", builtinTypes->numberType}}); - TypeId superTy = tbl({{"X", builtinTypes->numberType}, {"Y", builtinTypes->stringType}}); + TypeId subTy = tbl({{"X", getBuiltins()->stringType}, {"Y", getBuiltins()->numberType}}); + TypeId superTy = tbl({{"X", getBuiltins()->numberType}, {"Y", getBuiltins()->stringType}}); SubtypingResult result = isSubtype(subTy, superTy); CHECK(!result.isSubtype); @@ -1627,12 +1627,12 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation") getMutable(bTy)->scope = moduleScope.get(); TypeId genericFunctionTy = - arena.addType(FunctionType{{aTy, bTy}, {}, arena.addTypePack({aTy, bTy}), arena.addTypePack({join(meet(aTy, builtinTypes->truthyType), bTy)})} + arena.addType(FunctionType{{aTy, bTy}, {}, arena.addTypePack({aTy, bTy}), arena.addTypePack({join(meet(aTy, getBuiltins()->truthyType), bTy)})} ); - const TypeId truthyTy = builtinTypes->truthyType; + const TypeId truthyTy = getBuiltins()->truthyType; - TypeId actualFunctionTy = fn({truthyTy, truthyTy}, {join(meet(truthyTy, builtinTypes->truthyType), truthyTy)}); + TypeId actualFunctionTy = fn({truthyTy, truthyTy}, {join(meet(truthyTy, getBuiltins()->truthyType), truthyTy)}); SubtypingResult result = isSubtype(genericFunctionTy, actualFunctionTy); @@ -1641,15 +1641,15 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation") TEST_CASE_FIXTURE(SubtypeFixture, "free_types_might_be_subtypes") { - ScopedFastFlag sff{FFlag::LuauEagerGeneralization3, true}; + ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true}; - TypeId argTy = arena.freshType(builtinTypes, moduleScope.get()); + TypeId argTy = arena.freshType(getBuiltins(), moduleScope.get()); FreeType* freeArg = getMutable(argTy); REQUIRE(freeArg); freeArg->lowerBound = arena.addType(SingletonType{StringSingleton{"five"}}); - freeArg->upperBound = builtinTypes->stringType; + freeArg->upperBound = getBuiltins()->stringType; - SubtypingResult result = isSubtype(builtinTypes->stringType, argTy); + SubtypingResult result = isSubtype(getBuiltins()->stringType, argTy); CHECK(result.isSubtype); REQUIRE(1 == result.assumedConstraints.size()); } diff --git a/tests/ToDot.test.cpp b/tests/ToDot.test.cpp index 2bda6e81..aea3204c 100644 --- a/tests/ToDot.test.cpp +++ b/tests/ToDot.test.cpp @@ -15,7 +15,7 @@ struct ToDotClassFixture : Fixture { ToDotClassFixture() { - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); @@ -23,17 +23,17 @@ struct ToDotClassFixture : Fixture TypeId baseClassInstanceType = arena.addType(ExternType{"BaseClass", {}, std::nullopt, baseClassMetaType, {}, {}, "Test", {}}); getMutable(baseClassInstanceType)->props = { - {"BaseField", {builtinTypes->numberType}}, + {"BaseField", {getBuiltins()->numberType}}, }; - frontend.globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType}; + getFrontend().globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType}; TypeId childClassInstanceType = arena.addType(ExternType{"ChildClass", {}, baseClassInstanceType, std::nullopt, {}, {}, "Test", {}}); getMutable(childClassInstanceType)->props = { - {"ChildField", {builtinTypes->stringType}}, + {"ChildField", {getBuiltins()->stringType}}, }; - frontend.globals.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType}; + getFrontend().globals.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType}; - for (const auto& [name, ty] : frontend.globals.globalScope->exportedTypeBindings) + for (const auto& [name, ty] : getFrontend().globals.globalScope->exportedTypeBindings) persist(ty.type); freeze(arena); @@ -48,35 +48,35 @@ TEST_CASE_FIXTURE(Fixture, "primitive") R"(digraph graphname { n1 [label="nil"]; })", - toDot(builtinTypes->nilType) + toDot(getBuiltins()->nilType) ); CHECK_EQ( R"(digraph graphname { n1 [label="number"]; })", - toDot(builtinTypes->numberType) + toDot(getBuiltins()->numberType) ); CHECK_EQ( R"(digraph graphname { n1 [label="any"]; })", - toDot(builtinTypes->anyType) + toDot(getBuiltins()->anyType) ); CHECK_EQ( R"(digraph graphname { n1 [label="unknown"]; })", - toDot(builtinTypes->unknownType) + toDot(getBuiltins()->unknownType) ); CHECK_EQ( R"(digraph graphname { n1 [label="never"]; })", - toDot(builtinTypes->neverType) + toDot(getBuiltins()->neverType) ); } @@ -90,28 +90,28 @@ TEST_CASE_FIXTURE(Fixture, "no_duplicatePrimitives") R"(digraph graphname { n1 [label="PrimitiveType number"]; })", - toDot(builtinTypes->numberType, opts) + toDot(getBuiltins()->numberType, opts) ); CHECK_EQ( R"(digraph graphname { n1 [label="AnyType 1"]; })", - toDot(builtinTypes->anyType, opts) + toDot(getBuiltins()->anyType, opts) ); CHECK_EQ( R"(digraph graphname { n1 [label="UnknownType 1"]; })", - toDot(builtinTypes->unknownType, opts) + toDot(getBuiltins()->unknownType, opts) ); CHECK_EQ( R"(digraph graphname { n1 [label="NeverType 1"]; })", - toDot(builtinTypes->neverType, opts) + toDot(getBuiltins()->neverType, opts) ); } @@ -119,7 +119,7 @@ TEST_CASE_FIXTURE(Fixture, "bound") { TypeArena arena; - TypeId ty = arena.addType(BoundType{builtinTypes->numberType}); + TypeId ty = arena.addType(BoundType{getBuiltins()->numberType}); ToDotOptions opts; opts.showPointers = false; @@ -216,7 +216,7 @@ TEST_CASE_FIXTURE(Fixture, "intersection") { TypeArena arena; - TypeId ty = arena.addType(IntersectionType{{builtinTypes->stringType, builtinTypes->numberType}}); + TypeId ty = arena.addType(IntersectionType{{getBuiltins()->stringType, getBuiltins()->numberType}}); ToDotOptions opts; opts.showPointers = false; @@ -322,7 +322,7 @@ n3 [label="TableType 3"]; TEST_CASE_FIXTURE(Fixture, "free") { - Type type{TypeVariant{FreeType{TypeLevel{0, 0}, builtinTypes->neverType, builtinTypes->unknownType}}}; + Type type{TypeVariant{FreeType{TypeLevel{0, 0}, getBuiltins()->neverType, getBuiltins()->unknownType}}}; ToDotOptions opts; opts.showPointers = false; CHECK_EQ( @@ -339,7 +339,7 @@ TEST_CASE_FIXTURE(Fixture, "free_with_constraints") {FFlag::LuauSolverV2, true}, }; - Type type{TypeVariant{FreeType{nullptr, builtinTypes->numberType, builtinTypes->optionalNumberType}}}; + Type type{TypeVariant{FreeType{nullptr, getBuiltins()->numberType, getBuiltins()->optionalNumberType}}}; ToDotOptions opts; opts.showPointers = false; @@ -467,7 +467,7 @@ n1 [label="GenericTypePack T"]; TEST_CASE_FIXTURE(Fixture, "bound_pack") { - TypePackVar pack{TypePackVariant{TypePack{{builtinTypes->numberType}, {}}}}; + TypePackVar pack{TypePackVariant{TypePack{{getBuiltins()->numberType}, {}}}}; TypePackVar bound{TypePackVariant{BoundTypePack{&pack}}}; ToDotOptions opts; @@ -489,7 +489,7 @@ TEST_CASE_FIXTURE(Fixture, "bound_table") TypeArena arena; TypeId ty = arena.addType(TableType{}); - getMutable(ty)->props["x"] = {builtinTypes->numberType}; + getMutable(ty)->props["x"] = {getBuiltins()->numberType}; TypeId boundTy = arena.addType(TableType{}); getMutable(boundTy)->boundTo = ty; @@ -539,7 +539,7 @@ n5 [label="SingletonType boolean: false"]; TEST_CASE_FIXTURE(Fixture, "negation") { TypeArena arena; - TypeId t = arena.addType(NegationType{builtinTypes->stringType}); + TypeId t = arena.addType(NegationType{getBuiltins()->stringType}); ToDotOptions opts; opts.showPointers = false; diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp index 3553593e..b7e7d421 100644 --- a/tests/ToString.test.cpp +++ b/tests/ToString.test.cpp @@ -14,9 +14,8 @@ using namespace Luau; LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauAttributeSyntax) LUAU_FASTFLAG(LuauFixEmptyTypePackStringification) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("ToString"); @@ -232,27 +231,27 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "exhaustive_toString_of_cyclic_table") TEST_CASE_FIXTURE(Fixture, "intersection_parenthesized_only_if_needed") { - auto utv = Type{UnionType{{builtinTypes->numberType, builtinTypes->stringType}}}; - auto itv = Type{IntersectionType{{&utv, builtinTypes->booleanType}}}; + auto utv = Type{UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}}}; + auto itv = Type{IntersectionType{{&utv, getBuiltins()->booleanType}}}; CHECK_EQ(toString(&itv), "(number | string) & boolean"); } TEST_CASE_FIXTURE(Fixture, "union_parenthesized_only_if_needed") { - auto itv = Type{IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}}; - auto utv = Type{UnionType{{&itv, builtinTypes->booleanType}}}; + auto itv = Type{IntersectionType{{getBuiltins()->numberType, getBuiltins()->stringType}}}; + auto utv = Type{UnionType{{&itv, getBuiltins()->booleanType}}}; CHECK_EQ(toString(&utv), "(number & string) | boolean"); } TEST_CASE_FIXTURE(Fixture, "functions_are_always_parenthesized_in_unions_or_intersections") { - auto stringAndNumberPack = TypePackVar{TypePack{{builtinTypes->stringType, builtinTypes->numberType}}}; - auto numberAndStringPack = TypePackVar{TypePack{{builtinTypes->numberType, builtinTypes->stringType}}}; + auto stringAndNumberPack = TypePackVar{TypePack{{getBuiltins()->stringType, getBuiltins()->numberType}}}; + auto numberAndStringPack = TypePackVar{TypePack{{getBuiltins()->numberType, getBuiltins()->stringType}}}; auto sn2ns = Type{FunctionType{&stringAndNumberPack, &numberAndStringPack}}; - auto ns2sn = Type{FunctionType(frontend.globals.globalScope->level, &numberAndStringPack, &stringAndNumberPack)}; + auto ns2sn = Type{FunctionType(getFrontend().globals.globalScope->level, &numberAndStringPack, &stringAndNumberPack)}; auto utv = Type{UnionType{{&ns2sn, &sn2ns}}}; auto itv = Type{IntersectionType{{&ns2sn, &sn2ns}}}; @@ -341,7 +340,7 @@ TEST_CASE_FIXTURE(Fixture, "quit_stringifying_table_type_when_length_is_exceeded { TableType ttv{}; for (char c : std::string("abcdefghijklmno")) - ttv.props[std::string(1, c)] = {builtinTypes->numberType}; + ttv.props[std::string(1, c)] = {getBuiltins()->numberType}; Type tv{ttv}; @@ -358,7 +357,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_is_still_capped_when_exhaust { TableType ttv{}; for (char c : std::string("abcdefg")) - ttv.props[std::string(1, c)] = {builtinTypes->numberType}; + ttv.props[std::string(1, c)] = {getBuiltins()->numberType}; Type tv{ttv}; @@ -444,7 +443,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table { TableType ttv{TableState::Sealed, TypeLevel{}}; for (char c : std::string("abcdefghij")) - ttv.props[std::string(1, c)] = {builtinTypes->numberType}; + ttv.props[std::string(1, c)] = {getBuiltins()->numberType}; Type tv{ttv}; @@ -458,7 +457,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table TEST_CASE_FIXTURE(Fixture, "stringifying_cyclic_union_type_bails_early") { - Type tv{UnionType{{builtinTypes->stringType, builtinTypes->numberType}}}; + Type tv{UnionType{{getBuiltins()->stringType, getBuiltins()->numberType}}}; UnionType* utv = getMutable(&tv); utv->options.push_back(&tv); utv->options.push_back(&tv); @@ -479,11 +478,11 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_cyclic_intersection_type_bails_early") TEST_CASE_FIXTURE(Fixture, "stringifying_array_uses_array_syntax") { TableType ttv{TableState::Sealed, TypeLevel{}}; - ttv.indexer = TableIndexer{builtinTypes->numberType, builtinTypes->stringType}; + ttv.indexer = TableIndexer{getBuiltins()->numberType, getBuiltins()->stringType}; CHECK_EQ("{string}", toString(Type{ttv})); - ttv.props["A"] = {builtinTypes->numberType}; + ttv.props["A"] = {getBuiltins()->numberType}; if (FFlag::LuauSolverV2) CHECK_EQ("{ [number]: string, A: number }", toString(Type{ttv})); else @@ -632,15 +631,15 @@ TEST_CASE_FIXTURE(Fixture, "toString_the_boundTo_table_type_contained_within_a_T Type tv1{TableType{}}; TableType* ttv = getMutable(&tv1); ttv->state = TableState::Sealed; - ttv->props["hello"] = {builtinTypes->numberType}; - ttv->props["world"] = {builtinTypes->numberType}; + ttv->props["hello"] = {getBuiltins()->numberType}; + ttv->props["world"] = {getBuiltins()->numberType}; TypePackVar tpv1{TypePack{{&tv1}}}; Type tv2{TableType{}}; TableType* bttv = getMutable(&tv2); bttv->state = TableState::Free; - bttv->props["hello"] = {builtinTypes->numberType}; + bttv->props["hello"] = {getBuiltins()->numberType}; bttv->boundTo = &tv1; TypePackVar tpv2{TypePack{{&tv2}}}; @@ -661,10 +660,10 @@ TEST_CASE_FIXTURE(Fixture, "toString_the_boundTo_table_type_contained_within_a_T TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_return_type_if_pack_has_an_empty_head_link") { TypeArena arena; - TypePackId realTail = arena.addTypePack({builtinTypes->stringType}); + TypePackId realTail = arena.addTypePack({getBuiltins()->stringType}); TypePackId emptyTail = arena.addTypePack({}, realTail); - TypePackId argList = arena.addTypePack({builtinTypes->stringType}); + TypePackId argList = arena.addTypePack({getBuiltins()->stringType}); TypeId functionType = arena.addType(FunctionType{argList, emptyTail}); @@ -878,7 +877,7 @@ TEST_CASE_FIXTURE(Fixture, "tostring_unsee_ttv_if_array") TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( --!strict @@ -954,12 +953,12 @@ TEST_CASE_FIXTURE(Fixture, "cycle_rooted_in_a_pack") { TypeArena arena; - TypePackId thePack = arena.addTypePack({builtinTypes->numberType, builtinTypes->numberType}); + TypePackId thePack = arena.addTypePack({getBuiltins()->numberType, getBuiltins()->numberType}); TypePack* packPtr = getMutable(thePack); REQUIRE(packPtr); const TableType::Props theProps = { - {"BaseField", Property::readonly(builtinTypes->unknownType)}, + {"BaseField", Property::readonly(getBuiltins()->unknownType)}, {"BaseMethod", Property::readonly(arena.addType(FunctionType{thePack, arena.addTypePack({})}))} }; @@ -978,7 +977,7 @@ TEST_CASE_FIXTURE(Fixture, "correct_stringification_user_defined_type_functions" TypeFunction user{"user", nullptr}; TypeFunctionInstanceType tftt{ NotNull{&user}, - std::vector{builtinTypes->numberType}, // Type Function Arguments + std::vector{getBuiltins()->numberType}, // Type Function Arguments {}, {AstName{"woohoo"}}, // Type Function Name {}, diff --git a/tests/TypeFunction.test.cpp b/tests/TypeFunction.test.cpp index 9aacb315..237a7bb5 100644 --- a/tests/TypeFunction.test.cpp +++ b/tests/TypeFunction.test.cpp @@ -14,9 +14,9 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauErrorSuppressionTypeFunctionArgs) struct TypeFunctionFixture : Fixture @@ -55,14 +55,14 @@ struct TypeFunctionFixture : Fixture } }; - unfreeze(frontend.globals.globalTypes); - TypeId t = frontend.globals.globalTypes.addType(GenericType{"T"}); + unfreeze(getFrontend().globals.globalTypes); + TypeId t = getFrontend().globals.globalTypes.addType(GenericType{"T"}); GenericTypeDefinition genericT{t}; - ScopePtr globalScope = frontend.globals.globalScope; + ScopePtr globalScope = getFrontend().globals.globalScope; globalScope->exportedTypeBindings["Swap"] = - TypeFun{{genericT}, frontend.globals.globalTypes.addType(TypeFunctionInstanceType{NotNull{&swapFunction}, {t}, {}})}; - freeze(frontend.globals.globalTypes); + TypeFun{{genericT}, getFrontend().globals.globalTypes.addType(TypeFunctionInstanceType{NotNull{&swapFunction}, {t}, {}})}; + freeze(getFrontend().globals.globalTypes); } }; @@ -349,7 +349,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type MyObject = { x: number, y: number, z: number } @@ -372,7 +372,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works_with_metatables") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( local metatable = { __index = {w = 1} } @@ -431,7 +431,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_errors_if_it_has_nontabl TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; if (!FFlag::LuauSolverV2) return; @@ -464,7 +464,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_common_subset_if_union_o if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type MyObject = { x: number, y: number, z: number } @@ -502,7 +502,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_works") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type MyObject = { x: number, y: number, z: number } @@ -525,7 +525,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_ignores_metatables") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( local metatable = { __index = {w = 1} } @@ -568,7 +568,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_common_subset_if_unio if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type MyObject = { x: number, y: number, z: number } @@ -606,7 +606,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "keyof_type_function_works_on_extern_types" if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type KeysOfMyObject = keyof @@ -1005,7 +1005,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type MyObject = {a: string, b: number, c: boolean} @@ -1306,7 +1306,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type MyObject = {a: string, b: number, c: boolean} @@ -1588,15 +1588,15 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_returns_correct_metatable_for_u LUAU_REQUIRE_NO_ERRORS(result); - const PrimitiveType* stringType = get(builtinTypes->stringType); + const PrimitiveType* stringType = get(getBuiltins()->stringType); REQUIRE(stringType->metatable); TypeArena arena = TypeArena{}; - std::string expected1 = toString(arena.addType(UnionType{{*stringType->metatable, builtinTypes->emptyTableType}}), {true}); + std::string expected1 = toString(arena.addType(UnionType{{*stringType->metatable, getBuiltins()->emptyTableType}}), {true}); CHECK_EQ(toString(requireTypeAlias("Metatable"), {true}), expected1); - std::string expected2 = toString(arena.addType(IntersectionType{{*stringType->metatable, builtinTypes->emptyTableType}}), {true}); + std::string expected2 = toString(arena.addType(IntersectionType{{*stringType->metatable, getBuiltins()->emptyTableType}}), {true}); CHECK_EQ(toString(requireTypeAlias("IntersectMetatable"), {true}), expected2); } @@ -1612,7 +1612,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "getmetatable_returns_correct_metatable_for_s LUAU_REQUIRE_NO_ERRORS(result); - const PrimitiveType* stringType = get(builtinTypes->stringType); + const PrimitiveType* stringType = get(getBuiltins()->stringType); REQUIRE(stringType->metatable); std::string expected = toString(*stringType->metatable, {true}); @@ -1712,26 +1712,26 @@ struct TFFixture NotNull arena{&arena_}; BuiltinTypes builtinTypes_; - NotNull builtinTypes{&builtinTypes_}; + NotNull getBuiltins(){ return NotNull{&builtinTypes_};} - ScopePtr globalScope = std::make_shared(builtinTypes->anyTypePack); + ScopePtr globalScope = std::make_shared(getBuiltins()->anyTypePack); InternalErrorReporter ice; UnifierSharedState unifierState{&ice}; - SimplifierPtr simplifier = EqSatSimplification::newSimplifier(arena, builtinTypes); - Normalizer normalizer{arena, builtinTypes, NotNull{&unifierState}}; + SimplifierPtr simplifier = EqSatSimplification::newSimplifier(arena, getBuiltins()); + Normalizer normalizer{arena, getBuiltins(), NotNull{&unifierState}}; TypeCheckLimits limits; TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}}; const ScopedFastFlag sff[1] = { - {FFlag::LuauEagerGeneralization3, true}, + {FFlag::LuauEagerGeneralization4, true}, }; BuiltinTypeFunctions builtinTypeFunctions; TypeFunctionContext tfc{ arena, - builtinTypes, + getBuiltins(), NotNull{globalScope.get()}, NotNull{simplifier.get()}, NotNull{&normalizer}, @@ -1745,7 +1745,7 @@ TEST_CASE_FIXTURE(TFFixture, "refine") { TypeId g = arena->addType(GenericType{globalScope.get(), Polarity::Negative}); - TypeId refineTy = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.refineFunc, {g, builtinTypes->truthyType}}); + TypeId refineTy = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.refineFunc, {g, getBuiltins()->truthyType}}); FunctionGraphReductionResult res = reduceTypeFunctions(refineTy, Location{}, tfc); @@ -1758,8 +1758,8 @@ TEST_CASE_FIXTURE(TFFixture, "refine") TEST_CASE_FIXTURE(TFFixture, "or<'a, 'b>") { - TypeId aType = arena->freshType(builtinTypes, globalScope.get()); - TypeId bType = arena->freshType(builtinTypes, globalScope.get()); + TypeId aType = arena->freshType(getBuiltins(), globalScope.get()); + TypeId bType = arena->freshType(getBuiltins(), globalScope.get()); TypeId orType = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.orFunc, {aType, bType}}); diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index cd8ae817..b76fb9ba 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -9,9 +9,11 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauEagerGeneralization3) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauUserTypeFunctionAliases) +LUAU_FASTFLAG(LuauFollowTypeAlias) +LUAU_FASTFLAG(LuauFollowExistingTypeFunction) TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests"); @@ -351,7 +353,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function serialize_union(arg) @@ -371,7 +373,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function numberhuh() @@ -390,7 +392,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works_on_unions") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function foobar() @@ -412,7 +414,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getunion() @@ -441,7 +443,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function serialize_intersection(arg) @@ -463,7 +465,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getintersection() @@ -498,7 +500,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getnegation() @@ -547,7 +549,7 @@ local function notok(idx: fail): never return idx end TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function serialize_table(arg) @@ -567,7 +569,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function gettable() @@ -606,7 +608,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getmetatable() @@ -656,7 +658,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getfunction() @@ -717,7 +719,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_serialization_works2") TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_methods_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getclass(arg) @@ -739,7 +741,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_methods_works") TEST_CASE_FIXTURE(ExternTypeFixture, "write_of_readonly_is_nil") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getclass(arg) @@ -766,7 +768,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "write_of_readonly_is_nil") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function checkmut() @@ -798,7 +800,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function getcopy() @@ -984,7 +986,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_type_cant_call_get_props") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function foo() @@ -1005,7 +1007,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_2") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function first(arg) @@ -1058,7 +1060,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_3") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_unordered") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function bar() @@ -1124,7 +1126,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function optionify(tbl) @@ -1178,7 +1180,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function foo(tbl) @@ -1243,7 +1245,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strip_indexer") if (!FFlag::LuauSolverV2) return; - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type function stripindexer(tbl) @@ -1332,7 +1334,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -1410,7 +1412,7 @@ local a: concat<'first', 'second'> return {} )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(aResult); CHECK(toString(requireType("game/A", "a")) == R"("firstsecond")"); @@ -1463,7 +1465,7 @@ local a: concat<'first', 'second'> return {} )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(aResult); CHECK(toString(requireType("game/A", "a")) == R"("firstsecond")"); @@ -2002,7 +2004,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_eqsat_opaque") )"); TypeArena arena; auto ty = requireType("v"); - auto simplifier = EqSatSimplification::newSimplifier(NotNull{&arena}, frontend.builtinTypes); + auto simplifier = EqSatSimplification::newSimplifier(NotNull{&arena}, getBuiltins()); auto simplified = eqSatSimplify(NotNull{simplifier.get()}, ty); REQUIRE(simplified); CHECK_EQ("t0", toString(simplified->result)); // NOLINT(bugprone-unchecked-optional-access) @@ -2012,7 +2014,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_bool") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // FIXME: CLI-151985 // This test breaks because we can't see that eq is already fully reduced. @@ -2035,7 +2037,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_string") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // FIXME: CLI-151985 // This test breaks because we can't see that eq is already fully reduced. @@ -2335,7 +2337,7 @@ local x: foo<{ a: number }> = 2 return {} )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(aResult); CHECK(toString(requireType("game/A", "x")) == R"(number)"); @@ -2449,4 +2451,37 @@ local x: foo CHECK(toString(requireType("x"), ToStringOptions{true}) == "boolean | number"); } +TEST_CASE_FIXTURE(Fixture, "udtf_type_alias_registration_follows") +{ + ScopedFastFlag luauFollowTypeAlias{FFlag::LuauFollowTypeAlias, true}; + + LUAU_REQUIRE_ERRORS(check(R"( +export type t110 = ""type--" +function _(...):(any)&(any) +end +if _ then +else + export type t110 = ""type--" + function _(...):(any)&(any) + end +end + )")); +} + +TEST_CASE_FIXTURE(Fixture, "udtf_double_definition") +{ + ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; + ScopedFastFlag luauFollowExistingTypeFunction{FFlag::LuauFollowExistingTypeFunction, true}; + + CheckResult result = check(R"( +type function t0() +end +type function t0() +end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(toString(result.errors[0]) == R"(Redefinition of type 't0', previously defined at line 2)"); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index fe5178eb..4ba69f5a 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/tests/TypeInfer.aliases.test.cpp @@ -13,7 +13,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2) LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2) LUAU_FASTFLAG(LuauSkipMalformedTypeAliases) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("TypeAliases"); @@ -84,8 +84,8 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias") Location{{1, 21}, {1, 26}}, getMainSourceModule()->name, TypeMismatch{ - builtinTypes->numberType, - builtinTypes->stringType, + getBuiltins()->numberType, + getBuiltins()->stringType, }, } ); @@ -98,8 +98,8 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias") Location{{1, 8}, {1, 26}}, getMainSourceModule()->name, TypeMismatch{ - builtinTypes->numberType, - builtinTypes->stringType, + getBuiltins()->numberType, + getBuiltins()->stringType, }, } ); @@ -188,7 +188,7 @@ TEST_CASE_FIXTURE(Fixture, "cyclic_types_of_named_table_fields_do_not_expand_whe TypeMismatch* tm = get(result.errors[0]); REQUIRE_MESSAGE(tm, result.errors[0]); CHECK_EQ("Node?", toString(tm->wantedType)); - CHECK_EQ(builtinTypes->numberType, tm->givenType); + CHECK_EQ(getBuiltins()->numberType, tm->givenType); } TEST_CASE_FIXTURE(Fixture, "mutually_recursive_aliases") @@ -210,7 +210,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_aliases") { ScopedFastFlag sff[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -229,7 +229,7 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases") { ScopedFastFlag sff[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -274,9 +274,9 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_errors") LUAU_REQUIRE_ERRORS(result); // We had a UAF in this example caused by not cloning type function arguments - ModulePtr module = frontend.moduleResolver.getModule("MainModule"); + ModulePtr module = getFrontend().moduleResolver.getModule("MainModule"); unfreeze(module->interfaceTypes); - copyErrors(module->errors, module->interfaceTypes, builtinTypes); + copyErrors(module->errors, module->interfaceTypes, getBuiltins()); freeze(module->interfaceTypes); module->internalTypes.clear(); module->astTypes.clear(); @@ -329,7 +329,7 @@ TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_typ TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); CHECK_EQ("Wrapped", toString(tm->wantedType)); - CHECK_EQ(builtinTypes->numberType, tm->givenType); + CHECK_EQ(getBuiltins()->numberType, tm->givenType); } TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_type2") @@ -348,7 +348,7 @@ TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_typ CHECK_EQ("t1 where t1 = ({ a: t1 }) -> string", toString(tm->wantedType)); else CHECK_EQ("t1 where t1 = ({| a: t1 |}) -> string", toString(tm->wantedType)); - CHECK_EQ(builtinTypes->numberType, tm->givenType); + CHECK_EQ(getBuiltins()->numberType, tm->givenType); } // Check that recursive intersection type doesn't generate an OOM @@ -508,7 +508,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "general_require_multi_assign") local b: Bar.myvec3 )"; - CheckResult result = frontend.check("workspace/C"); + CheckResult result = getFrontend().check("workspace/C"); LUAU_REQUIRE_NO_ERRORS(result); TypeId aTypeId = requireType("workspace/C", "a"); @@ -527,7 +527,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_import_mutation") CheckResult result = check("type t10 = typeof(table)"); LUAU_REQUIRE_NO_ERRORS(result); - TypeId ty = getGlobalBinding(frontend.globals, "table"); + TypeId ty = getGlobalBinding(getFrontend().globals, "table"); CHECK(toString(ty) == "typeof(table)"); @@ -602,7 +602,7 @@ export type X = { a: number, b: X? } return {} )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(aResult); CheckResult bResult = check(R"( @@ -627,7 +627,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_of_an_imported_recursive_generic_ return {} )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(aResult); CheckResult bResult = check(R"( @@ -995,7 +995,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_lose_track_of_PendingExpansionTypes_aft end )"; - CheckResult result = frontend.check("game/React/React/ReactHooks"); + CheckResult result = getFrontend().check("game/React/React/ReactHooks"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -1048,7 +1048,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "alias_expands_to_bare_reference_to_imported_ end )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(result); } diff --git a/tests/TypeInfer.annotations.test.cpp b/tests/TypeInfer.annotations.test.cpp index 4029924a..ebbc3ec3 100644 --- a/tests/TypeInfer.annotations.test.cpp +++ b/tests/TypeInfer.annotations.test.cpp @@ -181,7 +181,7 @@ TEST_CASE_FIXTURE(Fixture, "function_return_annotations_are_checked") REQUIRE_EQ(1, tp->head.size()); - REQUIRE_EQ(builtinTypes->anyType, follow(tp->head[0])); + REQUIRE_EQ(getBuiltins()->anyType, follow(tp->head[0])); } TEST_CASE_FIXTURE(Fixture, "function_return_multret_annotations_are_checked") @@ -271,18 +271,18 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_of_value_a_via_typeof_with_assignment") LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK( - result.errors[0] == (TypeError{Location{Position{2, 29}, Position{2, 30}}, TypeMismatch{builtinTypes->nilType, builtinTypes->numberType}}) + result.errors[0] == (TypeError{Location{Position{2, 29}, Position{2, 30}}, TypeMismatch{getBuiltins()->nilType, getBuiltins()->numberType}}) ); } else { - CHECK_EQ(*builtinTypes->numberType, *requireType("a")); - CHECK_EQ(*builtinTypes->numberType, *requireType("b")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("a")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("b")); LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK_EQ( result.errors[0], - (TypeError{Location{Position{4, 12}, Position{4, 17}}, TypeMismatch{builtinTypes->numberType, builtinTypes->stringType}}) + (TypeError{Location{Position{4, 12}, Position{4, 17}}, TypeMismatch{getBuiltins()->numberType, getBuiltins()->stringType}}) ); } } @@ -573,7 +573,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_always_resolve_to_a_real_type") )"); TypeId fType = requireType("aa"); - REQUIRE(follow(fType) == builtinTypes->numberType); + REQUIRE(follow(fType) == getBuiltins()->numberType); LUAU_REQUIRE_NO_ERRORS(result); } @@ -594,7 +594,7 @@ TEST_CASE_FIXTURE(Fixture, "interface_types_belong_to_interface_arena") const TypeFun& a = mod.exportedTypeBindings["A"]; CHECK(isInArena(a.type, mod.interfaceTypes)); - CHECK(!isInArena(a.type, frontend.globals.globalTypes)); + CHECK(!isInArena(a.type, getFrontend().globals.globalTypes)); std::optional exportsType = first(mod.returnType); REQUIRE(exportsType); @@ -673,7 +673,7 @@ TEST_CASE_FIXTURE(Fixture, "cloned_interface_maintains_pointers_between_definiti TEST_CASE_FIXTURE(BuiltinsFixture, "use_type_required_from_another_file") { - addGlobalBinding(frontend.globals, "script", builtinTypes->anyType, "@test"); + addGlobalBinding(getFrontend().globals, "script", getBuiltins()->anyType, "@test"); fileResolver.source["Modules/Main"] = R"( --!strict @@ -692,14 +692,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "use_type_required_from_another_file") return {} )"; - CheckResult result = frontend.check("Modules/Main"); + CheckResult result = getFrontend().check("Modules/Main"); LUAU_REQUIRE_NO_ERRORS(result); } TEST_CASE_FIXTURE(BuiltinsFixture, "cannot_use_nonexported_type") { - addGlobalBinding(frontend.globals, "script", builtinTypes->anyType, "@test"); + addGlobalBinding(getFrontend().globals, "script", getBuiltins()->anyType, "@test"); fileResolver.source["Modules/Main"] = R"( --!strict @@ -718,14 +718,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cannot_use_nonexported_type") return {} )"; - CheckResult result = frontend.check("Modules/Main"); + CheckResult result = getFrontend().check("Modules/Main"); LUAU_REQUIRE_ERROR_COUNT(1, result); } TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_are_not_exported") { - addGlobalBinding(frontend.globals, "script", builtinTypes->anyType, "@test"); + addGlobalBinding(getFrontend().globals, "script", getBuiltins()->anyType, "@test"); fileResolver.source["Modules/Main"] = R"( --!strict @@ -742,7 +742,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_are_not_exported") return {} )"; - CheckResult result = frontend.check("Modules/Main"); + CheckResult result = getFrontend().check("Modules/Main"); LUAU_REQUIRE_ERROR_COUNT(1, result); } @@ -796,7 +796,7 @@ TEST_CASE_FIXTURE(Fixture, "luau_ice_triggers_an_ice_exception_with_flag_handler bool caught = false; - frontend.iceHandler.onInternalError = [&](const char*) + getFrontend().iceHandler.onInternalError = [&](const char*) { caught = true; }; @@ -823,6 +823,10 @@ TEST_CASE_FIXTURE(Fixture, "luau_ice_is_not_special_without_the_flag") TEST_CASE_FIXTURE(BuiltinsFixture, "luau_print_is_magic_if_the_flag_is_set") { + // Force the frontend on here + // The Fixture constructor (now lazy) + // initializes printline. If we do something after that, we'll override it with something empty + getFrontend(); static std::vector output; output.clear(); Luau::setPrintLine( diff --git a/tests/TypeInfer.anyerror.test.cpp b/tests/TypeInfer.anyerror.test.cpp index ceb5cdea..d2972b1b 100644 --- a/tests/TypeInfer.anyerror.test.cpp +++ b/tests/TypeInfer.anyerror.test.cpp @@ -46,7 +46,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any") } } else - CHECK(builtinTypes->anyType == requireType("a")); + CHECK(getBuiltins()->anyType == requireType("a")); } TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any2") @@ -308,7 +308,7 @@ TEST_CASE_FIXTURE(Fixture, "quantify_any_does_not_bind_to_itself") LUAU_REQUIRE_NO_ERRORS(result); TypeId aType = requireType("A"); - CHECK_EQ(aType, builtinTypes->anyType); + CHECK_EQ(aType, getBuiltins()->anyType); } TEST_CASE_FIXTURE(Fixture, "calling_error_type_yields_error") diff --git a/tests/TypeInfer.builtins.test.cpp b/tests/TypeInfer.builtins.test.cpp index a73ea82c..a0987a0a 100644 --- a/tests/TypeInfer.builtins.test.cpp +++ b/tests/TypeInfer.builtins.test.cpp @@ -11,9 +11,10 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauTableCloneClonesType3) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauStringFormatImprovements) +LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling) TEST_SUITE_BEGIN("BuiltinTests"); @@ -110,7 +111,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_concat_returns_string") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->stringType, *requireType("r")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("r")); } TEST_CASE_FIXTURE(BuiltinsFixture, "sort") @@ -170,7 +171,7 @@ TEST_CASE_FIXTURE(Fixture, "strings_have_methods") )LUA"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->stringType, *requireType("s")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("s")); } TEST_CASE_FIXTURE(BuiltinsFixture, "math_max_variatic") @@ -180,7 +181,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "math_max_variatic") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->numberType, *requireType("n")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("n")); } TEST_CASE_FIXTURE(BuiltinsFixture, "math_max_checks_for_numbers") @@ -397,7 +398,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_correctly_infers_type_of_array_ )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(builtinTypes->stringType, requireType("s")); + CHECK_EQ(getBuiltins()->stringType, requireType("s")); } TEST_CASE_FIXTURE(BuiltinsFixture, "table_insert_correctly_infers_type_of_array_3_args_overload") @@ -473,7 +474,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "gcinfo") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->numberType, *requireType("n")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("n")); } TEST_CASE_FIXTURE(BuiltinsFixture, "getfenv") @@ -490,9 +491,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "os_time_takes_optional_date_table") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->numberType, *requireType("n1")); - CHECK_EQ(*builtinTypes->numberType, *requireType("n2")); - CHECK_EQ(*builtinTypes->numberType, *requireType("n3")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("n1")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("n2")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("n3")); } TEST_CASE_FIXTURE(BuiltinsFixture, "thread_is_a_type") @@ -615,8 +616,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_correctly_ordered_types") LUAU_REQUIRE_ERROR_COUNT(1, result); TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK_EQ(tm->wantedType, builtinTypes->stringType); - CHECK_EQ(tm->givenType, builtinTypes->numberType); + CHECK_EQ(tm->wantedType, getBuiltins()->stringType); + CHECK_EQ(tm->givenType, getBuiltins()->numberType); } TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_tostring_specifier") @@ -809,8 +810,8 @@ TEST_CASE_FIXTURE(Fixture, "string_format_as_method") TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK_EQ(tm->wantedType, builtinTypes->stringType); - CHECK_EQ(tm->givenType, builtinTypes->numberType); + CHECK_EQ(tm->wantedType, getBuiltins()->stringType); + CHECK_EQ(tm->givenType, getBuiltins()->numberType); } TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_trivial_arity") @@ -959,9 +960,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_report_all_type_errors_at_corr string.format("%s%d%s", 1, "hello", true) )"); - TypeId stringType = builtinTypes->stringType; - TypeId numberType = builtinTypes->numberType; - TypeId booleanType = builtinTypes->booleanType; + TypeId stringType = getBuiltins()->stringType; + TypeId numberType = getBuiltins()->numberType; + TypeId booleanType = getBuiltins()->booleanType; LUAU_REQUIRE_ERROR_COUNT(6, result); @@ -1308,7 +1309,7 @@ TEST_CASE_FIXTURE(Fixture, "typeof_unresolved_function") TEST_CASE_FIXTURE(BuiltinsFixture, "no_persistent_typelevel_change") { - TypeId mathTy = requireType(frontend.globals.globalScope, "math"); + TypeId mathTy = requireType(getFrontend().globals.globalScope, "math"); REQUIRE(mathTy); TableType* ttv = getMutable(mathTy); REQUIRE(ttv); @@ -1694,8 +1695,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_singleton_types LUAU_REQUIRE_ERROR_COUNT(1, result); TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK_EQ(tm->wantedType, builtinTypes->stringType); - CHECK_EQ(tm->givenType, builtinTypes->numberType); + CHECK_EQ(tm->wantedType, getBuiltins()->stringType); + CHECK_EQ(tm->givenType, getBuiltins()->numberType); } TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_string_is_dynamic") @@ -1717,4 +1718,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_strin ); } +TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion") +{ + ScopedFastFlag _{FFlag::LuauWriteOnlyPropertyMangling, true}; + + // CLI-157307: This currently errors as we claim the literal is not a + // `{ write foo: number }`, which is wrong as every table literal is + // trivially a write-only table. + LUAU_REQUIRE_ERRORS(check(R"( + local function accept(t: { write foo: number }) + end + + accept({ foo = "lol", foo = true }) + )")); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.classes.test.cpp b/tests/TypeInfer.classes.test.cpp index 5e790834..e9484f74 100644 --- a/tests/TypeInfer.classes.test.cpp +++ b/tests/TypeInfer.classes.test.cpp @@ -766,7 +766,7 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); @@ -782,7 +782,7 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties") TypeId partType = arena.addType(ExternType{ "Part", - {{"BrickColor", Property::rw(builtinTypes->stringType)}, {"Parent", Property::rw(workspaceType, instanceType)}}, + {{"BrickColor", Property::rw(getBuiltins()->stringType)}, {"Parent", Property::rw(workspaceType, instanceType)}}, instanceType, nullopt, {}, @@ -793,7 +793,7 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties") getMutable(workspaceType)->props = {{"Script", Property::readonly(scriptType)}, {"Part", Property::readonly(partType)}}; - frontend.globals.globalScope->bindings[frontend.globals.globalNames.names->getOrAdd("script")] = Binding{scriptType}; + getFrontend().globals.globalScope->bindings[getFrontend().globals.globalNames.names->getOrAdd("script")] = Binding{scriptType}; freeze(arena); @@ -807,8 +807,8 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties") CHECK(Location{{1, 40}, {1, 48}} == result.errors[0].location); TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK(builtinTypes->stringType == tm->wantedType); - CHECK(builtinTypes->numberType == tm->givenType); + CHECK(getBuiltins()->stringType == tm->wantedType); + CHECK(getBuiltins()->numberType == tm->givenType); } TEST_CASE_FIXTURE(ExternTypeFixture, "cannot_index_a_class_with_no_indexer") @@ -826,7 +826,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "cannot_index_a_class_with_no_indexer") "Expected DynamicPropertyLookupOnExternTypesUnsafe but got " << result.errors[0] ); - CHECK(builtinTypes->errorType == requireType("c")); + CHECK(getBuiltins()->errorType == requireType("c")); } TEST_CASE_FIXTURE(ExternTypeFixture, "cyclic_tables_are_assumed_to_be_compatible_with_extern_types") diff --git a/tests/TypeInfer.definitions.test.cpp b/tests/TypeInfer.definitions.test.cpp index d17e4369..1b11c56e 100644 --- a/tests/TypeInfer.definitions.test.cpp +++ b/tests/TypeInfer.definitions.test.cpp @@ -22,13 +22,13 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_simple") declare foo2: typeof(foo) )"); - TypeId globalFooTy = getGlobalBinding(frontend.globals, "foo"); + TypeId globalFooTy = getGlobalBinding(getFrontend().globals, "foo"); CHECK_EQ(toString(globalFooTy), "number"); - TypeId globalBarTy = getGlobalBinding(frontend.globals, "bar"); + TypeId globalBarTy = getGlobalBinding(getFrontend().globals, "bar"); CHECK_EQ(toString(globalBarTy), "(number) -> string"); - TypeId globalFoo2Ty = getGlobalBinding(frontend.globals, "foo2"); + TypeId globalFoo2Ty = getGlobalBinding(getFrontend().globals, "foo2"); CHECK_EQ(toString(globalFoo2Ty), "number"); CheckResult result = check(R"( @@ -51,20 +51,20 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_loading") declare function var(...: any): string )"); - TypeId globalFooTy = getGlobalBinding(frontend.globals, "foo"); + TypeId globalFooTy = getGlobalBinding(getFrontend().globals, "foo"); CHECK_EQ(toString(globalFooTy), "number"); - std::optional globalAsdfTy = frontend.globals.globalScope->lookupType("Asdf"); + std::optional globalAsdfTy = getFrontend().globals.globalScope->lookupType("Asdf"); REQUIRE(bool(globalAsdfTy)); CHECK_EQ(toString(globalAsdfTy->type), "number | string"); - TypeId globalBarTy = getGlobalBinding(frontend.globals, "bar"); + TypeId globalBarTy = getGlobalBinding(getFrontend().globals, "bar"); CHECK_EQ(toString(globalBarTy), "(number) -> string"); - TypeId globalFoo2Ty = getGlobalBinding(frontend.globals, "foo2"); + TypeId globalFoo2Ty = getGlobalBinding(getFrontend().globals, "foo2"); CHECK_EQ(toString(globalFoo2Ty), "number"); - TypeId globalVarTy = getGlobalBinding(frontend.globals, "var"); + TypeId globalVarTy = getGlobalBinding(getFrontend().globals, "var"); CHECK_EQ(toString(globalVarTy), "(...any) -> string"); @@ -80,25 +80,25 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_loading") TEST_CASE_FIXTURE(Fixture, "load_definition_file_errors_do_not_pollute_global_scope") { - unfreeze(frontend.globals.globalTypes); - LoadDefinitionFileResult parseFailResult = frontend.loadDefinitionFile( - frontend.globals, - frontend.globals.globalScope, + unfreeze(getFrontend().globals.globalTypes); + LoadDefinitionFileResult parseFailResult = getFrontend().loadDefinitionFile( + getFrontend().globals, + getFrontend().globals.globalScope, R"( declare foo )", "@test", /* captureComments */ false ); - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); REQUIRE(!parseFailResult.success); - std::optional fooTy = tryGetGlobalBinding(frontend.globals, "foo"); + std::optional fooTy = tryGetGlobalBinding(getFrontend().globals, "foo"); CHECK(!fooTy.has_value()); - LoadDefinitionFileResult checkFailResult = frontend.loadDefinitionFile( - frontend.globals, - frontend.globals.globalScope, + LoadDefinitionFileResult checkFailResult = getFrontend().loadDefinitionFile( + getFrontend().globals, + getFrontend().globals.globalScope, R"( local foo: string = 123 declare bar: typeof(foo) @@ -108,7 +108,7 @@ TEST_CASE_FIXTURE(Fixture, "load_definition_file_errors_do_not_pollute_global_sc ); REQUIRE(!checkFailResult.success); - std::optional barTy = tryGetGlobalBinding(frontend.globals, "bar"); + std::optional barTy = tryGetGlobalBinding(getFrontend().globals, "bar"); CHECK(!barTy.has_value()); } @@ -152,10 +152,10 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_extern_types") TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function") { - unfreeze(frontend.globals.globalTypes); - LoadDefinitionFileResult result = frontend.loadDefinitionFile( - frontend.globals, - frontend.globals.globalScope, + unfreeze(getFrontend().globals.globalTypes); + LoadDefinitionFileResult result = getFrontend().loadDefinitionFile( + getFrontend().globals, + getFrontend().globals.globalScope, R"( declare class A X: number @@ -165,7 +165,7 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function") "@test", /* captureComments */ false ); - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); REQUIRE(!result.success); CHECK_EQ(result.parseResult.errors.size(), 0); @@ -178,10 +178,10 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function") TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_extend_non_class") { - unfreeze(frontend.globals.globalTypes); - LoadDefinitionFileResult result = frontend.loadDefinitionFile( - frontend.globals, - frontend.globals.globalScope, + unfreeze(getFrontend().globals.globalTypes); + LoadDefinitionFileResult result = getFrontend().loadDefinitionFile( + getFrontend().globals, + getFrontend().globals.globalScope, R"( type NotAClass = {} @@ -191,7 +191,7 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_extend_non_class") "@test", /* captureComments */ false ); - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); REQUIRE(!result.success); CHECK_EQ(result.parseResult.errors.size(), 0); @@ -204,10 +204,10 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_extend_non_class") TEST_CASE_FIXTURE(Fixture, "no_cyclic_defined_extern_types") { - unfreeze(frontend.globals.globalTypes); - LoadDefinitionFileResult result = frontend.loadDefinitionFile( - frontend.globals, - frontend.globals.globalScope, + unfreeze(getFrontend().globals.globalTypes); + LoadDefinitionFileResult result = getFrontend().loadDefinitionFile( + getFrontend().globals, + getFrontend().globals.globalScope, R"( declare class Foo extends Bar end @@ -218,7 +218,7 @@ TEST_CASE_FIXTURE(Fixture, "no_cyclic_defined_extern_types") "@test", /* captureComments */ false ); - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); REQUIRE(!result.success); } @@ -317,16 +317,16 @@ TEST_CASE_FIXTURE(Fixture, "definitions_documentation_symbols") } )"); - std::optional xBinding = frontend.globals.globalScope->linearSearchForBinding("x"); + std::optional xBinding = getFrontend().globals.globalScope->linearSearchForBinding("x"); REQUIRE(bool(xBinding)); // note: loadDefinition uses the @test package name. CHECK_EQ(xBinding->documentationSymbol, "@test/global/x"); - std::optional fooTy = frontend.globals.globalScope->lookupType("Foo"); + std::optional fooTy = getFrontend().globals.globalScope->lookupType("Foo"); REQUIRE(bool(fooTy)); CHECK_EQ(fooTy->type->documentationSymbol, "@test/globaltype/Foo"); - std::optional barTy = frontend.globals.globalScope->lookupType("Bar"); + std::optional barTy = getFrontend().globals.globalScope->lookupType("Bar"); REQUIRE(bool(barTy)); CHECK_EQ(barTy->type->documentationSymbol, "@test/globaltype/Bar"); @@ -335,7 +335,7 @@ TEST_CASE_FIXTURE(Fixture, "definitions_documentation_symbols") REQUIRE_EQ(barClass->props.count("prop"), 1); CHECK_EQ(barClass->props["prop"].documentationSymbol, "@test/globaltype/Bar.prop"); - std::optional yBinding = frontend.globals.globalScope->linearSearchForBinding("y"); + std::optional yBinding = getFrontend().globals.globalScope->linearSearchForBinding("y"); REQUIRE(bool(yBinding)); CHECK_EQ(yBinding->documentationSymbol, "@test/global/y"); @@ -355,7 +355,7 @@ TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_re declare function myFunc(): MyClass )"); - std::optional myClassTy = frontend.globals.globalScope->lookupType("MyClass"); + std::optional myClassTy = getFrontend().globals.globalScope->lookupType("MyClass"); REQUIRE(bool(myClassTy)); CHECK_EQ(myClassTy->type->documentationSymbol, "@test/globaltype/MyClass"); @@ -382,7 +382,7 @@ TEST_CASE_FIXTURE(Fixture, "documentation_symbols_dont_attach_to_persistent_type export type Evil = string )"); - std::optional ty = frontend.globals.globalScope->lookupType("Evil"); + std::optional ty = getFrontend().globals.globalScope->lookupType("Evil"); REQUIRE(bool(ty)); CHECK_EQ(ty->type->documentationSymbol, std::nullopt); } @@ -448,10 +448,10 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_string_props") TEST_CASE_FIXTURE(Fixture, "class_definition_malformed_string") { - unfreeze(frontend.globals.globalTypes); - LoadDefinitionFileResult result = frontend.loadDefinitionFile( - frontend.globals, - frontend.globals.globalScope, + unfreeze(getFrontend().globals.globalTypes); + LoadDefinitionFileResult result = getFrontend().loadDefinitionFile( + getFrontend().globals, + getFrontend().globals.globalScope, R"( declare class Foo ["a\0property"]: string @@ -460,7 +460,7 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_malformed_string") "@test", /* captureComments */ false ); - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); REQUIRE(!result.success); REQUIRE_EQ(result.parseResult.errors.size(), 1); @@ -487,8 +487,8 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_indexer") REQUIRE(bool(etv->indexer)); - CHECK_EQ(*etv->indexer->indexType, *builtinTypes->numberType); - CHECK_EQ(*etv->indexer->indexResultType, *builtinTypes->stringType); + CHECK_EQ(*etv->indexer->indexType, *getBuiltins()->numberType); + CHECK_EQ(*etv->indexer->indexResultType, *getBuiltins()->stringType); CHECK_EQ(toString(requireType("y")), "string"); } @@ -532,7 +532,7 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_has_source_module_name_set") CHECK_EQ(result.sourceModule.name, "@test"); CHECK_EQ(result.sourceModule.humanReadableName, "@test"); - std::optional fooTy = frontend.globals.globalScope->lookupType("Foo"); + std::optional fooTy = getFrontend().globals.globalScope->lookupType("Foo"); REQUIRE(fooTy); const ExternType* etv = get(fooTy->type); diff --git a/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index 30962d9a..ee6bc94e 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/tests/TypeInfer.functions.test.cpp @@ -22,12 +22,12 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauFormatUseLastPosition) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) TEST_SUITE_BEGIN("TypeInferFunctions"); @@ -77,7 +77,7 @@ TEST_CASE_FIXTURE(Fixture, "tc_function") TEST_CASE_FIXTURE(Fixture, "check_function_bodies") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( function myFunction(): number @@ -103,8 +103,8 @@ TEST_CASE_FIXTURE(Fixture, "check_function_bodies") (TypeError{ Location{Position{3, 16}, Position{3, 20}}, TypeMismatch{ - builtinTypes->numberType, - builtinTypes->booleanType, + getBuiltins()->numberType, + getBuiltins()->booleanType, } }) ); @@ -149,7 +149,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_return_type") std::vector retVec = flatten(takeFiveType->retTypes).first; REQUIRE(!retVec.empty()); - REQUIRE_EQ(*follow(retVec[0]), *builtinTypes->numberType); + REQUIRE_EQ(*follow(retVec[0]), *getBuiltins()->numberType); } TEST_CASE_FIXTURE(Fixture, "infer_from_function_return_type") @@ -157,7 +157,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_from_function_return_type") CheckResult result = check("function take_five() return 5 end local five = take_five()"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->numberType, *follow(requireType("five"))); + CHECK_EQ(*getBuiltins()->numberType, *follow(requireType("five"))); } TEST_CASE_FIXTURE(Fixture, "infer_that_function_does_not_return_a_table") @@ -171,7 +171,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_that_function_does_not_return_a_table") )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - CHECK_EQ(result.errors[0], (TypeError{Location{Position{5, 8}, Position{5, 24}}, NotATable{builtinTypes->numberType}})); + CHECK_EQ(result.errors[0], (TypeError{Location{Position{5, 8}, Position{5, 24}}, NotATable{getBuiltins()->numberType}})); } TEST_CASE_FIXTURE(Fixture, "generalize_table_property") @@ -258,8 +258,8 @@ TEST_CASE_FIXTURE(Fixture, "list_only_alternative_overloads_that_match_argument_ { TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK_EQ(builtinTypes->numberType, tm->wantedType); - CHECK_EQ(builtinTypes->stringType, tm->givenType); + CHECK_EQ(getBuiltins()->numberType, tm->wantedType); + CHECK_EQ(getBuiltins()->stringType, tm->givenType); } ExtraInformation* ei = get(result.errors[1]); @@ -300,8 +300,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_give_other_overloads_message_if_only_one_argume TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK_EQ(builtinTypes->numberType, tm->wantedType); - CHECK_EQ(builtinTypes->stringType, tm->givenType); + CHECK_EQ(getBuiltins()->numberType, tm->wantedType); + CHECK_EQ(getBuiltins()->stringType, tm->givenType); } TEST_CASE_FIXTURE(Fixture, "infer_return_type_from_selected_overload") @@ -991,8 +991,8 @@ TEST_CASE_FIXTURE(Fixture, "calling_function_with_incorrect_argument_type_yields (TypeError{ Location{Position{3, 12}, Position{3, 18}}, TypeMismatch{ - builtinTypes->numberType, - builtinTypes->stringType, + getBuiltins()->numberType, + getBuiltins()->stringType, } }) ); @@ -1002,8 +1002,8 @@ TEST_CASE_FIXTURE(Fixture, "calling_function_with_incorrect_argument_type_yields (TypeError{ Location{Position{3, 20}, Position{3, 23}}, TypeMismatch{ - builtinTypes->stringType, - builtinTypes->numberType, + getBuiltins()->stringType, + getBuiltins()->numberType, } }) ); @@ -1485,7 +1485,7 @@ local a: TableWithFunc = { x = 3, y = 4, f = function(a, b) return a + b end } TEST_CASE_FIXTURE(Fixture, "infer_return_value_type") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( local function f(): {string|number} @@ -1661,7 +1661,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "function_decl_non_self_sealed_overwrite") LUAU_REQUIRE_NO_ERRORS(result); // if 'string' library property was replaced with an internal module type, it will be freed and the next check will crash - frontend.clear(); + getFrontend().clear(); CheckResult result2 = check(R"( print(string.len('hello')) @@ -1686,7 +1686,7 @@ t.f = function(x) end )"); - if (FFlag::LuauEagerGeneralization3 && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2) { // FIXME CLI-151985 LUAU_CHECK_ERROR_COUNT(3, result); @@ -1771,7 +1771,7 @@ t.f = function(x) end )"); - if (FFlag::LuauEagerGeneralization3 && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2) { // FIXME CLI-151985 LUAU_CHECK_ERROR_COUNT(2, result); @@ -1968,7 +1968,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_ CHECK_EQ("(a) -> a", toString(requireType("f"))); - if (FFlag::LuauEagerGeneralization3 && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2) { LUAU_CHECK_NO_ERRORS(result); CHECK("({ read p: { read q: a } }) -> (a & ~(false?))?" == toString(requireType("g"))); @@ -2121,7 +2121,7 @@ z = y -- Not OK, so the line is colorable TEST_CASE_FIXTURE(Fixture, "function_is_supertype_of_concrete_functions") { - registerHiddenTypes(&frontend); + registerHiddenTypes(getFrontend()); CheckResult result = check(R"( function foo(f: fun) end @@ -2139,7 +2139,7 @@ TEST_CASE_FIXTURE(Fixture, "function_is_supertype_of_concrete_functions") TEST_CASE_FIXTURE(Fixture, "concrete_functions_are_not_supertypes_of_function") { - registerHiddenTypes(&frontend); + registerHiddenTypes(getFrontend()); CheckResult result = check(R"( local a: fun = function() end @@ -2168,7 +2168,7 @@ TEST_CASE_FIXTURE(Fixture, "concrete_functions_are_not_supertypes_of_function") TEST_CASE_FIXTURE(Fixture, "other_things_are_not_related_to_function") { - registerHiddenTypes(&frontend); + registerHiddenTypes(getFrontend()); CheckResult result = check(R"( local a: fun = function() end @@ -2429,7 +2429,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_packs_are_not_variadic") TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( function num() @@ -2453,7 +2453,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str") TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_after_num_or_str") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( local function num_or_str() @@ -2643,7 +2643,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_arg_type_2") return; // Make sure the error types are cloned to module interface - frontend.options.retainFullTypeGraphs = false; + getFrontend().options.retainFullTypeGraphs = false; CheckResult result = check(R"( local function escape_fslash(pre) @@ -2912,7 +2912,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types") { ScopedFastFlag sffs[] = { {FFlag::LuauSimplifyOutOfLine2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -2932,7 +2932,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types") { // The new solver should ideally be able to do better here, but this is no worse than the old solver. - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { LUAU_REQUIRE_ERROR_COUNT(2, result); auto tm1 = get(result.errors[0]); @@ -3062,7 +3062,7 @@ end local u,v = id(3), id(id(44)) )"); - CHECK_EQ(builtinTypes->numberType, requireType("v")); + CHECK_EQ(getBuiltins()->numberType, requireType("v")); LUAU_REQUIRE_NO_ERRORS(result); } diff --git a/tests/TypeInfer.generics.test.cpp b/tests/TypeInfer.generics.test.cpp index 8df7421c..64ff3832 100644 --- a/tests/TypeInfer.generics.test.cpp +++ b/tests/TypeInfer.generics.test.cpp @@ -14,11 +14,11 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauIntersectNotNil) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauReportSubtypingErrors) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) using namespace Luau; @@ -34,8 +34,8 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_function") local y: number = id(37) )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(builtinTypes->stringType, requireType("x")); - CHECK_EQ(builtinTypes->numberType, requireType("y")); + CHECK_EQ(getBuiltins()->stringType, requireType("x")); + CHECK_EQ(getBuiltins()->numberType, requireType("y")); } TEST_CASE_FIXTURE(Fixture, "check_generic_local_function") @@ -48,8 +48,8 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_local_function") local y: number = id(37) )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(builtinTypes->stringType, requireType("x")); - CHECK_EQ(builtinTypes->numberType, requireType("y")); + CHECK_EQ(getBuiltins()->stringType, requireType("x")); + CHECK_EQ(getBuiltins()->numberType, requireType("y")); } TEST_CASE_FIXTURE(Fixture, "check_generic_local_function2") @@ -62,8 +62,8 @@ TEST_CASE_FIXTURE(Fixture, "check_generic_local_function2") local y = id(37) )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(builtinTypes->stringType, requireType("x")); - CHECK_EQ(builtinTypes->numberType, requireType("y")); + CHECK_EQ(getBuiltins()->stringType, requireType("x")); + CHECK_EQ(getBuiltins()->numberType, requireType("y")); } TEST_CASE_FIXTURE(BuiltinsFixture, "unions_and_generics") @@ -908,7 +908,7 @@ end TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( --!strict @@ -1121,8 +1121,8 @@ TEST_CASE_FIXTURE(Fixture, "generic_function") LUAU_REQUIRE_NO_ERRORS(result); CHECK_EQ("(a) -> a", toString(requireType("id"))); - CHECK_EQ(*builtinTypes->numberType, *requireType("a")); - CHECK_EQ(*builtinTypes->nilType, *requireType("b")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("a")); + CHECK_EQ(*getBuiltins()->nilType, *requireType("b")); } TEST_CASE_FIXTURE(Fixture, "generic_table_method") @@ -1427,7 +1427,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_infer_generic_functions") ScopedFastFlag _[] = { {FFlag::LuauReportSubtypingErrors, true}, {FFlag::LuauSubtypingCheckFunctionGenericCounts, true}, - {FFlag::LuauEagerGeneralization3, true}, + {FFlag::LuauEagerGeneralization4, true}, }; CheckResult result; diff --git a/tests/TypeInfer.intersectionTypes.test.cpp b/tests/TypeInfer.intersectionTypes.test.cpp index e443d111..427b8442 100644 --- a/tests/TypeInfer.intersectionTypes.test.cpp +++ b/tests/TypeInfer.intersectionTypes.test.cpp @@ -11,7 +11,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauNarrowIntersectionNevers) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("IntersectionTypes"); @@ -450,7 +450,7 @@ Type 'number' could not be converted into 'X')"; TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_all") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type X = { x: number } diff --git a/tests/TypeInfer.loops.test.cpp b/tests/TypeInfer.loops.test.cpp index 482b090b..c588272c 100644 --- a/tests/TypeInfer.loops.test.cpp +++ b/tests/TypeInfer.loops.test.cpp @@ -40,7 +40,7 @@ TEST_CASE_FIXTURE(Fixture, "for_loop") CHECK("number?" == toString(requireType("q"))); } else - CHECK_EQ(*builtinTypes->numberType, *requireType("q")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("q")); } TEST_CASE_FIXTURE(BuiltinsFixture, "iteration_no_table_passed") @@ -151,8 +151,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop") } else { - CHECK_EQ(*builtinTypes->numberType, *requireType("n")); - CHECK_EQ(*builtinTypes->stringType, *requireType("s")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("n")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("s")); } } @@ -180,8 +180,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next") } else { - CHECK_EQ(*builtinTypes->numberType, *requireType("n")); - CHECK_EQ(*builtinTypes->stringType, *requireType("s")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("n")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("s")); } } TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next_and_multiple_elements") @@ -213,8 +213,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next_and_multiple_elements" } else { - CHECK_EQ(*builtinTypes->numberType, *requireType("n")); - CHECK_EQ(*builtinTypes->stringType, *requireType("s")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("n")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("s")); } } @@ -385,8 +385,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_factory_not_returning_t TypeMismatch* tm = get(result.errors[1]); REQUIRE(tm); - CHECK_EQ(builtinTypes->numberType, tm->wantedType); - CHECK_EQ(builtinTypes->stringType, tm->givenType); + CHECK_EQ(getBuiltins()->numberType, tm->wantedType); + CHECK_EQ(getBuiltins()->stringType, tm->givenType); } TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_iterator_requiring_args_but_none_given") @@ -451,8 +451,8 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_with_custom_iterator") TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK_EQ(builtinTypes->numberType, tm->wantedType); - CHECK_EQ(builtinTypes->stringType, tm->givenType); + CHECK_EQ(getBuiltins()->numberType, tm->wantedType); + CHECK_EQ(getBuiltins()->stringType, tm->givenType); } TEST_CASE_FIXTURE(Fixture, "while_loop") @@ -469,7 +469,7 @@ TEST_CASE_FIXTURE(Fixture, "while_loop") if (FFlag::LuauSolverV2) CHECK("number?" == toString(requireType("i"))); else - CHECK_EQ(*builtinTypes->numberType, *requireType("i")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("i")); } TEST_CASE_FIXTURE(Fixture, "repeat_loop") @@ -486,7 +486,7 @@ TEST_CASE_FIXTURE(Fixture, "repeat_loop") if (FFlag::LuauSolverV2) CHECK("string?" == toString(requireType("i"))); else - CHECK_EQ(*builtinTypes->stringType, *requireType("i")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("i")); } TEST_CASE_FIXTURE(Fixture, "repeat_loop_condition_binds_to_its_block") @@ -804,7 +804,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_basic") CHECK("number?" == toString(keyTy)); } else - CHECK_EQ(*builtinTypes->numberType, *requireType("key")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("key")); } TEST_CASE_FIXTURE(Fixture, "loop_iter_trailing_nil") @@ -821,7 +821,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_trailing_nil") )"); LUAU_REQUIRE_ERROR_COUNT(0, result); - CHECK_EQ(*builtinTypes->nilType, *requireType("extra")); + CHECK_EQ(*getBuiltins()->nilType, *requireType("extra")); } TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer_strict") diff --git a/tests/TypeInfer.modules.test.cpp b/tests/TypeInfer.modules.test.cpp index e97f0645..d394cc3b 100644 --- a/tests/TypeInfer.modules.test.cpp +++ b/tests/TypeInfer.modules.test.cpp @@ -13,7 +13,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) using namespace Luau; @@ -36,13 +36,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_require_basic") local b = A.a )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(aResult); - CheckResult bResult = frontend.check("game/B"); + CheckResult bResult = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(bResult); - ModulePtr b = frontend.moduleResolver.getModule("game/B"); + ModulePtr b = getFrontend().moduleResolver.getModule("game/B"); REQUIRE(b != nullptr); std::optional bType = requireType(b, "b"); REQUIRE(bType); @@ -78,15 +78,15 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require") )"; } - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); dumpErrors(aResult); LUAU_REQUIRE_NO_ERRORS(aResult); - CheckResult bResult = frontend.check("game/B"); + CheckResult bResult = getFrontend().check("game/B"); dumpErrors(bResult); LUAU_REQUIRE_NO_ERRORS(bResult); - ModulePtr b = frontend.moduleResolver.getModule("game/B"); + ModulePtr b = getFrontend().moduleResolver.getModule("game/B"); REQUIRE(b != nullptr); @@ -113,10 +113,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require_types") local h: Hooty.Point )"; - CheckResult bResult = frontend.check("workspace/B"); + CheckResult bResult = getFrontend().check("workspace/B"); LUAU_REQUIRE_NO_ERRORS(bResult); - ModulePtr b = frontend.moduleResolver.getModule("workspace/B"); + ModulePtr b = getFrontend().moduleResolver.getModule("workspace/B"); REQUIRE(b != nullptr); TypeId hType = requireType(b, "h"); @@ -136,9 +136,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require_a_variadic_function") local f = A.f )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); - ModulePtr bModule = frontend.moduleResolver.getModule("game/B"); + ModulePtr bModule = getFrontend().moduleResolver.getModule("game/B"); REQUIRE(bModule != nullptr); TypeId f = follow(requireType(bModule, "f")); @@ -169,13 +169,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cross_module_table_freeze") return table.freeze(require(game.A)) )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(aResult); - CheckResult bResult = frontend.check("game/B"); + CheckResult bResult = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(bResult); - ModulePtr a = frontend.moduleResolver.getModule("game/A"); + ModulePtr a = getFrontend().moduleResolver.getModule("game/A"); REQUIRE(a != nullptr); // confirm that no cross-module mutation happened here! if (FFlag::LuauSolverV2) @@ -183,7 +183,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cross_module_table_freeze") else CHECK(toString(a->returnType) == "{| a: number |}"); - ModulePtr b = frontend.moduleResolver.getModule("game/B"); + ModulePtr b = getFrontend().moduleResolver.getModule("game/B"); REQUIRE(b != nullptr); // confirm that no cross-module mutation happened here! if (FFlag::LuauSolverV2) @@ -215,11 +215,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require_module_that_does_not_export") fileResolver.source["game/Workspace/A"] = sourceA; fileResolver.source["game/Workspace/B"] = sourceB; - frontend.check("game/Workspace/A"); - frontend.check("game/Workspace/B"); + getFrontend().check("game/Workspace/A"); + getFrontend().check("game/Workspace/B"); - ModulePtr aModule = frontend.moduleResolver.getModule("game/Workspace/A"); - ModulePtr bModule = frontend.moduleResolver.getModule("game/Workspace/B"); + ModulePtr aModule = getFrontend().moduleResolver.getModule("game/Workspace/A"); + ModulePtr bModule = getFrontend().moduleResolver.getModule("game/Workspace/B"); CHECK(aModule->errors.empty()); REQUIRE_EQ(1, bModule->errors.size()); @@ -239,7 +239,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "warn_if_you_try_to_require_a_non_modulescrip local M = require(script.Parent.A) )"; - CheckResult result = frontend.check("Modules/B"); + CheckResult result = getFrontend().check("Modules/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -260,7 +260,7 @@ local a : string = "" a = tbl.abc.def )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK_EQ("Type 'number' could not be converted into 'string'", toString(result.errors[0])); } @@ -275,7 +275,7 @@ return { def = 4 } local tbl: string = require(game.A) )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); if (FFlag::LuauSolverV2) CHECK_EQ("Type '{ def: number }' could not be converted into 'string'", toString(result.errors[0])); @@ -323,7 +323,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require_failed_module") return unfortunately() )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_ERRORS(aResult); CheckResult result = check(R"( @@ -350,7 +350,7 @@ local x: Type = {} function x:Destroy(): () end )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_ERROR_COUNT(2, result); } @@ -368,7 +368,7 @@ local x: Type = { x = { a = 2 } } type Rename = typeof(x.x) )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -387,7 +387,7 @@ local x: Type = types type Rename = typeof(x.x) )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -461,7 +461,7 @@ local a: A.T = { x = 2 } local b: B.T = a )"; - CheckResult result = frontend.check("game/C"); + CheckResult result = getFrontend().check("game/C"); LUAU_REQUIRE_ERROR_COUNT(1, result); if (FFlag::LuauSolverV2) @@ -507,7 +507,7 @@ local a: A.T = { x = 2 } local b: B.T = a )"; - CheckResult result = frontend.check("game/D"); + CheckResult result = getFrontend().check("game/D"); LUAU_REQUIRE_ERROR_COUNT(1, result); if (FFlag::LuauSolverV2) @@ -538,7 +538,7 @@ local l0 = require(game.A) return l0 )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -583,7 +583,7 @@ return l0 TEST_CASE_FIXTURE(BuiltinsFixture, "ensure_scope_is_nullptr_after_shallow_copy") { ScopedFastFlag _{FFlag::LuauSolverV2, true}; - frontend.options.retainFullTypeGraphs = false; + getFrontend().options.retainFullTypeGraphs = false; fileResolver.source["game/A"] = R"( -- Roughly taken from ReactTypes.lua @@ -777,7 +777,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "leaky_generics") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { CHECK_EQ("(unknown) -> unknown", toString(requireTypeAtPosition({13, 23}))); } @@ -817,9 +817,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cycles_dont_make_everything_any") return module )"; - frontend.check("game/A"); + getFrontend().check("game/A"); - CHECK("module" == toString(frontend.moduleResolver.getModule("game/B")->returnType)); + CHECK("module" == toString(getFrontend().moduleResolver.getModule("game/B")->returnType)); } TEST_CASE_FIXTURE(BuiltinsFixture, "cross_module_function_mutation") @@ -843,7 +843,7 @@ local test2 = require(game.A) return wrapper(test2, 1, "") )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(result); } diff --git a/tests/TypeInfer.negations.test.cpp b/tests/TypeInfer.negations.test.cpp index ed591c61..60316955 100644 --- a/tests/TypeInfer.negations.test.cpp +++ b/tests/TypeInfer.negations.test.cpp @@ -18,7 +18,7 @@ struct NegationFixture : Fixture NegationFixture() { - registerHiddenTypes(&frontend); + registerHiddenTypes(getFrontend()); } }; diff --git a/tests/TypeInfer.oop.test.cpp b/tests/TypeInfer.oop.test.cpp index 45f54ac2..4f709507 100644 --- a/tests/TypeInfer.oop.test.cpp +++ b/tests/TypeInfer.oop.test.cpp @@ -13,6 +13,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) TEST_SUITE_BEGIN("TypeInferOOP"); @@ -420,7 +421,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cycle_between_object_constructor_and_alias") TEST_CASE_FIXTURE(BuiltinsFixture, "promise_type_error_too_complex" * doctest::timeout(2)) { - frontend.options.retainFullTypeGraphs = false; + getFrontend().options.retainFullTypeGraphs = false; // Used `luau-reduce` tool to extract a minimal reproduction. // Credit: https://github.com/evaera/roblox-lua-promise/blob/v4.0.0/lib/init.lua @@ -538,10 +539,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cross_module_metatable") setmetatable(tbl, cls) )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_NO_ERRORS(result); - ModulePtr b = frontend.moduleResolver.getModule("game/B"); + ModulePtr b = getFrontend().moduleResolver.getModule("game/B"); REQUIRE(b); std::optional clsBinding = b->getModuleScope()->linearSearchForBinding("tbl"); @@ -552,4 +553,86 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cross_module_metatable") CHECK("{ @metatable cls, tbl }" == toString(clsType)); } +// https://luau.org/typecheck#adding-types-for-faux-object-oriented-programs +TEST_CASE_FIXTURE(BuiltinsFixture, "textbook_class_pattern") +{ + if (!FFlag::LuauSolverV2) + return; + + ScopedFastFlag sff[] ={ + {FFlag::LuauEagerGeneralization4, true}, + }; + + CheckResult result = check(R"( + local Account = {} + Account.__index = Account + + type AccountData = { + name: string, + balance: number, + } + + export type Account = setmetatable + + function Account.new(name, balance): Account + local self = {} + self.name = name + self.balance = balance + + return setmetatable(self, Account) + end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "textbook_class_pattern_2") +{ + if (!FFlag::LuauSolverV2) + return; + + ScopedFastFlag sff[] ={ + {FFlag::LuauEagerGeneralization4, true}, + }; + + CheckResult result = check(R"( + local Account = {} + Account.__index = Account + + type AccountData = { + name: string, + balance: number, + } + + export type Account = setmetatable + + function Account.new(name, balance): Account + local self = {} + self.name = name + self.balance = balance + + return setmetatable(self, Account) + end + + function Account.deposit(self: Account, credit: number) + self.balance += credit + end + + function Account.withdraw(self: Account, debit: number) + self.balance -= debit + end + + function Account.hasBalance(self: Account, amount: number): boolean + return self.balance >= amount + end + + local account = Account.new("Hina", 500) + + if account:hasBalance(123) then -- TypeError: Value of type 'unknown' could be nil + end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.operators.test.cpp b/tests/TypeInfer.operators.test.cpp index 0938142e..b68d5d20 100644 --- a/tests/TypeInfer.operators.test.cpp +++ b/tests/TypeInfer.operators.test.cpp @@ -17,7 +17,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) TEST_SUITE_BEGIN("TypeInferOperators"); @@ -29,7 +29,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // FIXME: Regression CHECK("(string & ~(false?)) | number" == toString(*requireType("s"))); @@ -51,7 +51,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // FIXME: Regression. CHECK("(string & ~(false?)) | number" == toString(*requireType("s"))); @@ -72,13 +72,13 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union") )"); LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { // FIXME: Regression CHECK("(string & ~(false?)) | string" == toString(requireType("s"))); } else - CHECK_EQ(*requireType("s"), *builtinTypes->stringType); + CHECK_EQ(*requireType("s"), *getBuiltins()->stringType); } TEST_CASE_FIXTURE(Fixture, "and_does_not_always_add_boolean") @@ -98,7 +98,7 @@ TEST_CASE_FIXTURE(Fixture, "and_adds_boolean_no_superfluous_union") local x:boolean = s )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*requireType("x"), *builtinTypes->booleanType); + CHECK_EQ(*requireType("x"), *getBuiltins()->booleanType); } TEST_CASE_FIXTURE(Fixture, "and_or_ternary") @@ -125,9 +125,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "primitive_arith_no_metatable") std::optional retType = first(functionType->retTypes); REQUIRE(retType.has_value()); - CHECK_EQ(builtinTypes->numberType, follow(*retType)); - CHECK_EQ(requireType("n"), builtinTypes->numberType); - CHECK_EQ(requireType("s"), builtinTypes->stringType); + CHECK_EQ(getBuiltins()->numberType, follow(*retType)); + CHECK_EQ(requireType("n"), getBuiltins()->numberType); + CHECK_EQ(requireType("s"), getBuiltins()->stringType); } TEST_CASE_FIXTURE(Fixture, "primitive_arith_no_metatable_with_follows") @@ -138,7 +138,7 @@ TEST_CASE_FIXTURE(Fixture, "primitive_arith_no_metatable_with_follows") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(requireType("SOLAR_MASS"), builtinTypes->numberType); + CHECK_EQ(requireType("SOLAR_MASS"), getBuiltins()->numberType); } TEST_CASE_FIXTURE(Fixture, "primitive_arith_possible_metatable") @@ -421,7 +421,7 @@ TEST_CASE_FIXTURE(Fixture, "compound_assign_mismatch_op") s += true )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - CHECK_EQ(result.errors[0], (TypeError{Location{{2, 13}, {2, 17}}, TypeMismatch{builtinTypes->numberType, builtinTypes->booleanType}})); + CHECK_EQ(result.errors[0], (TypeError{Location{{2, 13}, {2, 17}}, TypeMismatch{getBuiltins()->numberType, getBuiltins()->booleanType}})); } TEST_CASE_FIXTURE(Fixture, "compound_assign_mismatch_result") @@ -434,13 +434,13 @@ TEST_CASE_FIXTURE(Fixture, "compound_assign_mismatch_result") 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}})); + CHECK_EQ(result.errors[0], (TypeError{Location{{2, 8}, {2, 9}}, TypeMismatch{getBuiltins()->numberType, getBuiltins()->stringType}})); } else { LUAU_REQUIRE_ERROR_COUNT(2, result); - CHECK_EQ(result.errors[0], (TypeError{Location{{2, 8}, {2, 9}}, TypeMismatch{builtinTypes->numberType, builtinTypes->stringType}})); - CHECK_EQ(result.errors[1], (TypeError{Location{{2, 8}, {2, 15}}, TypeMismatch{builtinTypes->stringType, builtinTypes->numberType}})); + CHECK_EQ(result.errors[0], (TypeError{Location{{2, 8}, {2, 9}}, TypeMismatch{getBuiltins()->numberType, getBuiltins()->stringType}})); + CHECK_EQ(result.errors[1], (TypeError{Location{{2, 8}, {2, 15}}, TypeMismatch{getBuiltins()->stringType, getBuiltins()->numberType}})); } } @@ -605,7 +605,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus") TypeMismatch* tm = get(result.errors[1]); REQUIRE(tm); CHECK_EQ(toString(tm->givenType), "bar"); - CHECK_EQ(*tm->wantedType, *builtinTypes->numberType); + CHECK_EQ(*tm->wantedType, *getBuiltins()->numberType); } else { @@ -634,7 +634,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error") local a = -foo )"); - if (FFlag::LuauEagerGeneralization3 && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2) { LUAU_REQUIRE_ERROR_COUNT(1, result); @@ -663,7 +663,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error") CHECK_EQ("string", toString(requireType("a"))); TypeMismatch* tm = get(result.errors[0]); - REQUIRE_EQ(*tm->wantedType, *builtinTypes->booleanType); + REQUIRE_EQ(*tm->wantedType, *getBuiltins()->booleanType); // given type is the typeof(foo) which is complex to compare against } } @@ -695,8 +695,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_len_error") TypeMismatch* tm = get(result.errors[0]); REQUIRE_MESSAGE(tm, "Expected a TypeMismatch but got " << result.errors[0]); - REQUIRE_EQ(*tm->wantedType, *builtinTypes->numberType); - REQUIRE_EQ(*tm->givenType, *builtinTypes->stringType); + REQUIRE_EQ(*tm->wantedType, *getBuiltins()->numberType); + REQUIRE_EQ(*tm->givenType, *getBuiltins()->stringType); } TEST_CASE_FIXTURE(BuiltinsFixture, "unary_not_is_boolean") @@ -755,8 +755,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "disallow_string_and_types_without_metatables { TypeMismatch* tm = get(result.errors[0]); REQUIRE_MESSAGE(tm, "Expected a TypeMismatch but got " << result.errors[0]); - CHECK_EQ(*tm->wantedType, *builtinTypes->numberType); - CHECK_EQ(*tm->givenType, *builtinTypes->stringType); + CHECK_EQ(*tm->wantedType, *getBuiltins()->numberType); + CHECK_EQ(*tm->givenType, *getBuiltins()->stringType); GenericError* gen1 = get(result.errors[1]); REQUIRE(gen1); @@ -767,7 +767,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "disallow_string_and_types_without_metatables TypeMismatch* tm2 = get(result.errors[2]); REQUIRE(tm2); - CHECK_EQ(*tm2->wantedType, *builtinTypes->numberType); + CHECK_EQ(*tm2->wantedType, *getBuiltins()->numberType); CHECK_EQ(*tm2->givenType, *requireType("foo")); } } @@ -1017,7 +1017,7 @@ local b: number = 1 or a TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK_EQ(builtinTypes->numberType, tm->wantedType); + CHECK_EQ(getBuiltins()->numberType, tm->wantedType); CHECK_EQ("number?", toString(tm->givenType)); } @@ -1360,8 +1360,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "mm_comparisons_must_return_a_boolean") LUAU_REQUIRE_ERROR_COUNT(4, result); - CHECK(requireType("v1") == builtinTypes->booleanType); - CHECK(requireType("v2") == builtinTypes->booleanType); + CHECK(requireType("v1") == getBuiltins()->booleanType); + CHECK(requireType("v2") == getBuiltins()->booleanType); CHECK(toString(result.errors[1]) == "Metamethod '__lt' must return a boolean"); CHECK(toString(result.errors[3]) == "Metamethod '__lt' must return a boolean"); diff --git a/tests/TypeInfer.primitives.test.cpp b/tests/TypeInfer.primitives.test.cpp index 249341f6..90e66e59 100644 --- a/tests/TypeInfer.primitives.test.cpp +++ b/tests/TypeInfer.primitives.test.cpp @@ -27,7 +27,7 @@ TEST_CASE_FIXTURE(Fixture, "string_length") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(builtinTypes->numberType, requireType("t")); + CHECK_EQ(getBuiltins()->numberType, requireType("t")); } TEST_CASE_FIXTURE(Fixture, "string_index") @@ -53,7 +53,7 @@ TEST_CASE_FIXTURE(Fixture, "string_method") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*requireType("p"), *builtinTypes->numberType); + CHECK_EQ(*requireType("p"), *getBuiltins()->numberType); } TEST_CASE_FIXTURE(Fixture, "string_function_indirect") @@ -65,7 +65,7 @@ TEST_CASE_FIXTURE(Fixture, "string_function_indirect") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*requireType("p"), *builtinTypes->stringType); + CHECK_EQ(*requireType("p"), *getBuiltins()->stringType); } TEST_CASE_FIXTURE(Fixture, "check_methods_of_number") diff --git a/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index 0945a883..88c4ec9b 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/tests/TypeInfer.provisional.test.cpp @@ -336,9 +336,13 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_from_x_not_equal_to_nil") TEST_CASE_FIXTURE(BuiltinsFixture, "bail_early_if_unification_is_too_complicated" * doctest::timeout(0.5)) { + // We have to force this test case up here before the flags kick in. + // The reason for this is that while loading the builtins, the below flags will cause that + // to fail while cloning the public interface. This means that the builtin loading will assert. + // This didn't use to happen because we would initialize the fixture before the test case ran + getFrontend(); ScopedFastInt sffi{FInt::LuauTarjanChildLimit, 1}; ScopedFastInt sffi2{FInt::LuauTypeInferIterationLimit, 1}; - CheckResult result = check(R"LUA( local Result Result = setmetatable({}, {}) @@ -561,19 +565,19 @@ TEST_CASE_FIXTURE(Fixture, "free_options_cannot_be_unified_together") ScopedFastFlag sff{FFlag::LuauSolverV2, false}; TypeArena arena; - TypeId nilType = builtinTypes->nilType; + TypeId nilType = getBuiltins()->nilType; - std::unique_ptr scope = std::make_unique(builtinTypes->anyTypePack); + std::unique_ptr scope = std::make_unique(getBuiltins()->anyTypePack); - TypeId free1 = arena.freshType(builtinTypes, scope.get()); + TypeId free1 = arena.freshType(getBuiltins(), scope.get()); TypeId option1 = arena.addType(UnionType{{nilType, free1}}); - TypeId free2 = arena.freshType(builtinTypes, scope.get()); + TypeId free2 = arena.freshType(getBuiltins(), scope.get()); TypeId option2 = arena.addType(UnionType{{nilType, free2}}); InternalErrorReporter iceHandler; UnifierSharedState sharedState{&iceHandler}; - Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; + Normalizer normalizer{&arena, getBuiltins(), NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant}; u.tryUnify(option1, option2); @@ -621,7 +625,7 @@ local metatable = { return wrapStrictTable )"; - frontend.check("game/A"); + getFrontend().check("game/A"); fileResolver.source["game/B"] = R"( local wrapStrictTable = require(game.A) @@ -631,9 +635,9 @@ local Constants = {} return wrapStrictTable(Constants, "Constants") )"; - frontend.check("game/B"); + getFrontend().check("game/B"); - ModulePtr m = frontend.moduleResolver.getModule("game/B"); + ModulePtr m = getFrontend().moduleResolver.getModule("game/B"); REQUIRE(m); std::optional result = first(m->returnType); @@ -663,7 +667,7 @@ local metatable = { return wrapStrictTable )"; - frontend.check("game/A"); + getFrontend().check("game/A"); fileResolver.source["game/B"] = R"( local wrapStrictTable = require(game.A) @@ -673,9 +677,9 @@ local Constants = {} return wrapStrictTable(Constants, "Constants") )"; - frontend.check("game/B"); + getFrontend().check("game/B"); - ModulePtr m = frontend.moduleResolver.getModule("game/B"); + ModulePtr m = getFrontend().moduleResolver.getModule("game/B"); REQUIRE(m); std::optional result = first(m->returnType); @@ -693,7 +697,7 @@ struct IsSubtypeFixture : Fixture { bool isSubtype(TypeId a, TypeId b) { - SimplifierPtr simplifier = newSimplifier(NotNull{&getMainModule()->internalTypes}, builtinTypes); + SimplifierPtr simplifier = newSimplifier(NotNull{&getMainModule()->internalTypes}, getBuiltins()); ModulePtr module = getMainModule(); REQUIRE(module); @@ -701,7 +705,7 @@ struct IsSubtypeFixture : Fixture if (!module->hasModuleScope()) FAIL("isSubtype: module scope data is not available"); - return ::Luau::isSubtype(a, b, NotNull{module->getModuleScope().get()}, builtinTypes, NotNull{simplifier.get()}, ice); + return ::Luau::isSubtype(a, b, NotNull{module->getModuleScope().get()}, getBuiltins(), NotNull{simplifier.get()}, ice); } }; } // namespace @@ -989,19 +993,19 @@ TEST_CASE_FIXTURE(Fixture, "free_options_can_be_unified_together") ScopedFastFlag sff{FFlag::LuauSolverV2, false}; TypeArena arena; - TypeId nilType = builtinTypes->nilType; + TypeId nilType = getBuiltins()->nilType; - std::unique_ptr scope = std::make_unique(builtinTypes->anyTypePack); + std::unique_ptr scope = std::make_unique(getBuiltins()->anyTypePack); - TypeId free1 = arena.freshType(builtinTypes, scope.get()); + TypeId free1 = arena.freshType(getBuiltins(), scope.get()); TypeId option1 = arena.addType(UnionType{{nilType, free1}}); - TypeId free2 = arena.freshType(builtinTypes, scope.get()); + TypeId free2 = arena.freshType(getBuiltins(), scope.get()); TypeId option2 = arena.addType(UnionType{{nilType, free2}}); InternalErrorReporter iceHandler; UnifierSharedState sharedState{&iceHandler}; - Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; + Normalizer normalizer{&arena, getBuiltins(), NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, NotNull{scope.get()}, Location{}, Variance::Covariant}; u.tryUnify(option1, option2); @@ -1032,7 +1036,7 @@ TEST_CASE_FIXTURE(Fixture, "optional_class_instances_are_invariant_old_solver") { DOES_NOT_PASS_NEW_SOLVER_GUARD(); - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CheckResult result = check(R"( function foo(ref: {current: Parent?}) @@ -1050,7 +1054,7 @@ TEST_CASE_FIXTURE(Fixture, "optional_class_instances_are_invariant_new_solver") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - createSomeExternTypes(&frontend); + createSomeExternTypes(getFrontend()); CheckResult result = check(R"( function foo(ref: {read current: Parent?}) @@ -1105,7 +1109,7 @@ end )"; - CheckResult result = frontend.check("Module/Map"); + CheckResult result = getFrontend().check("Module/Map"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -1143,7 +1147,7 @@ local tbl = require(game.A) tbl:f3() )"; - CheckResult result = frontend.check("game/B"); + CheckResult result = getFrontend().check("game/B"); LUAU_REQUIRE_ERROR_COUNT(1, result); } @@ -1301,15 +1305,15 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "update_phonemes_minimized") TEST_CASE_FIXTURE(Fixture, "table_containing_non_final_type_is_erroneously_cached") { TypeArena arena; - Scope globalScope(builtinTypes->anyTypePack); + Scope globalScope(getBuiltins()->anyTypePack); UnifierSharedState sharedState{&ice}; - Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; + Normalizer normalizer{&arena, getBuiltins(), NotNull{&sharedState}}; TypeId tableTy = arena.addType(TableType{}); TableType* table = getMutable(tableTy); REQUIRE(table); - TypeId freeTy = arena.freshType(builtinTypes, &globalScope); + TypeId freeTy = arena.freshType(getBuiltins(), &globalScope); table->props["foo"] = Property::rw(freeTy); diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 52d6fb6a..96776e12 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -10,7 +10,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauSimplificationTableExternType) @@ -85,62 +85,62 @@ struct RefinementExternTypeFixture : BuiltinsFixture { RefinementExternTypeFixture() { - TypeArena& arena = frontend.globals.globalTypes; - NotNull scope{frontend.globals.globalScope.get()}; + TypeArena& arena = getFrontend().globals.globalTypes; + NotNull scope{getFrontend().globals.globalScope.get()}; - std::optional rootSuper = std::make_optional(builtinTypes->externType); + std::optional rootSuper = std::make_optional(getBuiltins()->externType); unfreeze(arena); TypeId vec3 = arena.addType(ExternType{"Vector3", {}, rootSuper, std::nullopt, {}, nullptr, "Test", {}}); getMutable(vec3)->props = { - {"X", Property{builtinTypes->numberType}}, - {"Y", Property{builtinTypes->numberType}}, - {"Z", Property{builtinTypes->numberType}}, + {"X", Property{getBuiltins()->numberType}}, + {"Y", Property{getBuiltins()->numberType}}, + {"Z", Property{getBuiltins()->numberType}}, }; TypeId inst = arena.addType(ExternType{"Instance", {}, rootSuper, std::nullopt, {}, nullptr, "Test", {}}); - TypePackId isAParams = arena.addTypePack({inst, builtinTypes->stringType}); - TypePackId isARets = arena.addTypePack({builtinTypes->booleanType}); + TypePackId isAParams = arena.addTypePack({inst, getBuiltins()->stringType}); + TypePackId isARets = arena.addTypePack({getBuiltins()->booleanType}); TypeId isA = arena.addType(FunctionType{isAParams, isARets}); getMutable(isA)->magic = std::make_shared(); getMutable(inst)->props = { - {"Name", Property{builtinTypes->stringType}}, + {"Name", Property{getBuiltins()->stringType}}, {"IsA", Property{isA}}, }; TypeId scriptConnection = arena.addType(ExternType("ExternScriptConnection", {}, inst, std::nullopt, {}, nullptr, "Test", {})); TypePackId disconnectArgs = arena.addTypePack({scriptConnection}); - TypeId disconnect = arena.addType(FunctionType{disconnectArgs, builtinTypes->emptyTypePack}); + TypeId disconnect = arena.addType(FunctionType{disconnectArgs, getBuiltins()->emptyTypePack}); getMutable(scriptConnection)->props = { {"Disconnect", Property{disconnect}}, }; - TypeId folder = frontend.globals.globalTypes.addType(ExternType{"Folder", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); - TypeId part = frontend.globals.globalTypes.addType(ExternType{"Part", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); + TypeId folder = getFrontend().globals.globalTypes.addType(ExternType{"Folder", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); + TypeId part = getFrontend().globals.globalTypes.addType(ExternType{"Part", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); getMutable(part)->props = { {"Position", Property{vec3}}, }; - TypeId optionalPart = arena.addType(UnionType{{part, builtinTypes->nilType}}); - TypeId weldConstraint = frontend.globals.globalTypes.addType(ExternType{"WeldConstraint", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); + TypeId optionalPart = arena.addType(UnionType{{part, getBuiltins()->nilType}}); + TypeId weldConstraint = getFrontend().globals.globalTypes.addType(ExternType{"WeldConstraint", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); getMutable(weldConstraint)->props = { {"Part0", Property{optionalPart}}, {"Part1", Property{optionalPart}}, }; - frontend.globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vec3}; - frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, inst}; - frontend.globals.globalScope->exportedTypeBindings["ExternScriptConnection"] = TypeFun{{}, scriptConnection}; - frontend.globals.globalScope->exportedTypeBindings["Folder"] = TypeFun{{}, folder}; - frontend.globals.globalScope->exportedTypeBindings["Part"] = TypeFun{{}, part}; - frontend.globals.globalScope->exportedTypeBindings["WeldConstraint"] = TypeFun{{}, weldConstraint}; + getFrontend().globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vec3}; + getFrontend().globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, inst}; + getFrontend().globals.globalScope->exportedTypeBindings["ExternScriptConnection"] = TypeFun{{}, scriptConnection}; + getFrontend().globals.globalScope->exportedTypeBindings["Folder"] = TypeFun{{}, folder}; + getFrontend().globals.globalScope->exportedTypeBindings["Part"] = TypeFun{{}, part}; + getFrontend().globals.globalScope->exportedTypeBindings["WeldConstraint"] = TypeFun{{}, weldConstraint}; - for (const auto& [name, ty] : frontend.globals.globalScope->exportedTypeBindings) + for (const auto& [name, ty] : getFrontend().globals.globalScope->exportedTypeBindings) persist(ty.type); - freeze(frontend.globals.globalTypes); + freeze(getFrontend().globals.globalTypes); } }; } // namespace @@ -764,7 +764,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonoptional_type_can_narrow_to_nil_if_sense_ LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) { CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil" CHECK("string & unknown & unknown & ~nil" == toString(requireTypeAtPosition({6, 24}))); // type(v) ~= "nil" @@ -1670,7 +1670,7 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "asserting_non_existent_propertie CheckResult result = check(R"( local weld: WeldConstraint = nil :: any assert(weld.Part8) - print(weld) -- hover type should become `never` + print(weld) assert(weld.Part8.Name == "RootPart") local part8 = assert(weld.Part8) local pos = part8.Position @@ -2544,7 +2544,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi end )")); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) // FIXME CLI-114134. We need to simplify types more consistently. CHECK_EQ("nil & string & unknown", toString(requireTypeAtPosition({4, 24}))); else diff --git a/tests/TypeInfer.singletons.test.cpp b/tests/TypeInfer.singletons.test.cpp index 1dfc8715..d1fa937e 100644 --- a/tests/TypeInfer.singletons.test.cpp +++ b/tests/TypeInfer.singletons.test.cpp @@ -7,7 +7,7 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("TypeSingletons"); @@ -280,7 +280,7 @@ TEST_CASE_FIXTURE(Fixture, "tagged_unions_immutable_tag") CannotAssignToNever* tm = get(result.errors[0]); REQUIRE(tm); - CHECK(builtinTypes->stringType == tm->rhsType); + CHECK(getBuiltins()->stringType == tm->rhsType); CHECK(CannotAssignToNever::Reason::PropertyNarrowed == tm->reason); REQUIRE(tm->cause.size() == 2); CHECK("\"Dog\"" == toString(tm->cause[0])); @@ -378,7 +378,7 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -396,7 +396,7 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes") TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_string") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type Cat = { tag: 'cat', catfood: string } diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index 1b755c0d..efc25df1 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -21,12 +21,12 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck) LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauDisablePrimitiveInferenceInLargeTables) LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit) @@ -49,8 +49,8 @@ end const TableType* tType = get(requireType("t")); REQUIRE(tType != nullptr); REQUIRE(tType->indexer); - CHECK_EQ(tType->indexer->indexType, builtinTypes->numberType); - CHECK_EQ(follow(tType->indexer->indexResultType), builtinTypes->unknownType); + CHECK_EQ(tType->indexer->indexType, getBuiltins()->numberType); + CHECK_EQ(follow(tType->indexer->indexResultType), getBuiltins()->unknownType); } TEST_CASE_FIXTURE(BuiltinsFixture, "LUAU_ASSERT_arg_exprs_doesnt_trigger_assert") @@ -306,7 +306,7 @@ TEST_CASE_FIXTURE(Fixture, "call_method") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->numberType, *requireType("a")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("a")); } TEST_CASE_FIXTURE(Fixture, "call_method_with_explicit_self_argument") @@ -653,8 +653,8 @@ TEST_CASE_FIXTURE(Fixture, "infer_array") REQUIRE(bool(ttv->indexer)); - CHECK_EQ(*ttv->indexer->indexType, *builtinTypes->numberType); - CHECK_EQ(*ttv->indexer->indexResultType, *builtinTypes->stringType); + CHECK_EQ(*ttv->indexer->indexType, *getBuiltins()->numberType); + CHECK_EQ(*ttv->indexer->indexResultType, *getBuiltins()->stringType); } /* This is a bit weird. @@ -698,7 +698,7 @@ TEST_CASE_FIXTURE(Fixture, "indexers_get_quantified_too") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization3) + if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization4) CHECK("({a}) -> ()" == toString(requireType("swap"))); else if (FFlag::LuauSolverV2) CHECK("({unknown}) -> ()" == toString(requireType("swap"))); @@ -772,8 +772,8 @@ TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_array_like_table") REQUIRE(bool(ttv->indexer)); const TableIndexer& indexer = *ttv->indexer; - CHECK_EQ(*builtinTypes->numberType, *indexer.indexType); - CHECK_EQ(*builtinTypes->stringType, *indexer.indexResultType); + CHECK_EQ(*getBuiltins()->numberType, *indexer.indexType); + CHECK_EQ(*getBuiltins()->stringType, *indexer.indexResultType); } TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_value_property_in_literal") @@ -834,8 +834,8 @@ TEST_CASE_FIXTURE(Fixture, "infer_indexer_from_its_variable_type_and_unifiable") REQUIRE_MESSAGE(tTy != nullptr, "Expected a table but got " << toString(t2Ty)); REQUIRE(tTy->indexer); - CHECK_EQ(*builtinTypes->numberType, *tTy->indexer->indexType); - CHECK_EQ(*builtinTypes->stringType, *tTy->indexer->indexResultType); + CHECK_EQ(*getBuiltins()->numberType, *tTy->indexer->indexType); + CHECK_EQ(*getBuiltins()->stringType, *tTy->indexer->indexResultType); } TEST_CASE_FIXTURE(Fixture, "indexer_mismatch") @@ -909,7 +909,7 @@ TEST_CASE_FIXTURE(Fixture, "array_factory_function") TEST_CASE_FIXTURE(Fixture, "sealed_table_indexers_must_unify") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( function f(a: {number}): {string} @@ -960,7 +960,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_type_when_indexing_from_a_table_indexer") LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->stringType, *requireType("s")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("s")); } TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_from_a_table_should_prefer_properties_when_possible") @@ -986,13 +986,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "indexing_from_a_table_should_prefer_properti LUAU_REQUIRE_ERROR_COUNT(1, result); - CHECK_EQ(*builtinTypes->stringType, *requireType("a1")); - CHECK_EQ(*builtinTypes->stringType, *requireType("a2")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("a1")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("a2")); - CHECK_EQ(*builtinTypes->numberType, *requireType("b1")); - CHECK_EQ(*builtinTypes->numberType, *requireType("b2")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("b1")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("b2")); - CHECK_EQ(*builtinTypes->numberType, *requireType("c")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("c")); CHECK_MESSAGE(nullptr != get(result.errors[0]), "Expected a TypeMismatch but got " << result.errors[0]); } @@ -1055,7 +1055,7 @@ TEST_CASE_FIXTURE(Fixture, "assigning_to_an_unsealed_table_with_string_literal_s LUAU_REQUIRE_NO_ERRORS(result); - CHECK("string" == toString(*builtinTypes->stringType)); + CHECK("string" == toString(*getBuiltins()->stringType)); TypeId tType = requireType("t"); TableType* tableType = getMutable(tType); @@ -1065,7 +1065,7 @@ TEST_CASE_FIXTURE(Fixture, "assigning_to_an_unsealed_table_with_string_literal_s TypeId propertyA = tableType->props["a"].type(); REQUIRE(propertyA != nullptr); - CHECK_EQ(*builtinTypes->stringType, *propertyA); + CHECK_EQ(*getBuiltins()->stringType, *propertyA); } TEST_CASE_FIXTURE(BuiltinsFixture, "oop_indexer_works") @@ -1088,7 +1088,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oop_indexer_works") LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->stringType, *requireType("words")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("words")); } TEST_CASE_FIXTURE(BuiltinsFixture, "indexer_table") @@ -1101,7 +1101,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "indexer_table") LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->stringType, *requireType("b")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("b")); } TEST_CASE_FIXTURE(BuiltinsFixture, "indexer_fn") @@ -1112,7 +1112,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "indexer_fn") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->numberType, *requireType("b")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("b")); } TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add") @@ -1254,10 +1254,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oop_polymorphic") LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(*builtinTypes->booleanType, *requireType("alive")); - CHECK_EQ(*builtinTypes->stringType, *requireType("movement")); - CHECK_EQ(*builtinTypes->stringType, *requireType("name")); - CHECK_EQ(*builtinTypes->numberType, *requireType("speed")); + CHECK_EQ(*getBuiltins()->booleanType, *requireType("alive")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("movement")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("name")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("speed")); } TEST_CASE_FIXTURE(Fixture, "user_defined_table_types_are_named") @@ -1691,7 +1691,7 @@ TEST_CASE_FIXTURE(Fixture, "right_table_missing_key2") TEST_CASE_FIXTURE(Fixture, "casting_unsealed_tables_with_props_into_table_with_indexer") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type StringToStringMap = { [string]: string } @@ -1795,7 +1795,7 @@ TEST_CASE_FIXTURE(Fixture, "casting_tables_with_props_into_table_with_indexer4") TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multiple_errors") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( function f(vec1: {x: number}): {x: number, y: number, z: number} @@ -1829,7 +1829,7 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multi TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multiple_errors2") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type MixedTable = {[number]: number, x: number} @@ -2042,7 +2042,7 @@ TEST_CASE_FIXTURE(Fixture, "explicit_nil_indexer") ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; auto result = check(R"( @@ -2375,7 +2375,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table local c : string = t.m("hi") )"); - if (FFlag::LuauEagerGeneralization3 && FFlag::LuauSolverV2) + if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2) { // FIXME CLI-151985 LUAU_CHECK_ERROR_COUNT(2, result); @@ -2961,7 +2961,7 @@ TEST_CASE_FIXTURE(Fixture, "table_length") LUAU_REQUIRE_NO_ERRORS(result); CHECK(nullptr != get(requireType("t"))); - CHECK_EQ(*builtinTypes->numberType, *requireType("s")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("s")); } TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_indexer") @@ -2985,7 +2985,7 @@ TEST_CASE_FIXTURE(Fixture, "wrong_assign_does_hit_indexer") TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); CHECK_EQ("number?", toString(tm->wantedType)); - CHECK(tm->givenType == builtinTypes->stringType); + CHECK(tm->givenType == getBuiltins()->stringType); } TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_no_indexer") @@ -3000,8 +3000,8 @@ TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_no_indexer") (TypeError{ Location{Position{2, 17}, Position{2, 20}}, TypeMismatch{ - builtinTypes->numberType, - builtinTypes->nilType, + getBuiltins()->numberType, + getBuiltins()->nilType, } }) ); @@ -3180,9 +3180,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "instantiate_tables_at_scope_level") --!strict local Option = {} Option.__index = Option + function Option.Is(obj) - return (type(obj) == "table" and getmetatable(obj) == Option) + return (type(obj) == "table" and getmetatable(obj) == Option) end + return Option )"); @@ -3223,7 +3225,7 @@ TEST_CASE_FIXTURE(Fixture, "setmetatable_cant_be_used_to_mutate_global_types") Fixture fix; // inherit env from parent fixture checker - fix.frontend.globals.globalScope = frontend.globals.globalScope; + fix.getFrontend().globals.globalScope = getFrontend().globals.globalScope; fix.check(R"( --!nonstrict @@ -3237,7 +3239,7 @@ end // validate sharedEnv post-typecheck; valuable for debugging some typeck crashes but slows fuzzing down // note: it's important for typeck to be destroyed at this point! { - for (auto& p : frontend.globals.globalScope->bindings) + for (auto& p : getFrontend().globals.globalScope->bindings) { toString(p.second.typeId); // toString walks the entire type, making sure ASAN catches access to destroyed type arenas } @@ -3386,7 +3388,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_basic") } else LUAU_REQUIRE_NO_ERRORS(result); - CHECK(requireType("foo") == builtinTypes->numberType); + CHECK(requireType("foo") == getBuiltins()->numberType); } TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_must_be_callable") @@ -3409,7 +3411,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_must_be_callable") { TypeError e{ Location{{5, 20}, {5, 21}}, - CannotCallNonFunction{builtinTypes->numberType}, + CannotCallNonFunction{getBuiltins()->numberType}, }; CHECK(result.errors[0] == e); } @@ -3429,8 +3431,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_call_metamethod_generic") )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK(requireType("foo") == builtinTypes->numberType); - CHECK(requireType("bar") == builtinTypes->stringType); + CHECK(requireType("foo") == getBuiltins()->numberType); + CHECK(requireType("bar") == getBuiltins()->stringType); } TEST_CASE_FIXTURE(BuiltinsFixture, "table_simple_call") @@ -3510,7 +3512,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_invalidate_the_properties_iterator_of_free_tabl return culprit )"; - CheckResult result = frontend.check("Module/Backend"); + CheckResult result = getFrontend().check("Module/Backend"); } TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early") @@ -3637,7 +3639,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_leak_free_table_props") TEST_CASE_FIXTURE(Fixture, "mixed_tables_with_implicit_numbered_keys") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( local t: { [string]: number } = { 5, 6, 7 } )"); @@ -3746,7 +3748,7 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_ { ScopedFastFlag sff[] = { {FFlag::LuauReportSubtypingErrors, true}, - {FFlag::LuauEagerGeneralization3, true}, + {FFlag::LuauEagerGeneralization4, true}, }; CheckResult result = check(R"( @@ -4274,7 +4276,7 @@ TEST_CASE_FIXTURE(Fixture, "cli_84607_missing_prop_in_array_or_dict") { ScopedFastFlag sffs[] = { {FFlag::LuauFixIndexerSubtypingOrdering, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -4345,7 +4347,7 @@ TEST_CASE_FIXTURE(Fixture, "identify_all_problematic_table_fields") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -4474,8 +4476,8 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression") // honestly not sure which of these is a better developer experience. if (FFlag::LuauSolverV2) { - CHECK_EQ(*tm->wantedType, *builtinTypes->stringType); - CHECK_EQ(*tm->givenType, *builtinTypes->numberType); + CHECK_EQ(*tm->wantedType, *getBuiltins()->stringType); + CHECK_EQ(*tm->givenType, *getBuiltins()->numberType); } else { @@ -4639,7 +4641,7 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties") return; ScopedFastFlag sff[] = { - {FFlag::LuauEagerGeneralization3, true}, + {FFlag::LuauEagerGeneralization4, true}, }; CheckResult result = check(R"( @@ -4689,7 +4691,7 @@ TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array") end )"); - if (FFlag::LuauSolverV2 && !FFlag::LuauEagerGeneralization3) + if (FFlag::LuauSolverV2 && !FFlag::LuauEagerGeneralization4) { LUAU_CHECK_ERROR_COUNT(1, result); LUAU_CHECK_ERROR(result, NotATable); @@ -4737,7 +4739,7 @@ TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_another_ LUAU_REQUIRE_NO_ERRORS(result); // FIXME CLI-114134. We need to simplify types more consistently. - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) CHECK("({number} & {number}, unknown) -> ()" == toString(requireType("f"))); else CHECK_EQ("(unknown & {number} & {number}, unknown) -> ()", toString(requireType("f"))); @@ -4760,7 +4762,7 @@ TEST_CASE_FIXTURE(Fixture, "write_to_union_property_not_all_present") CannotAssignToNever* tm = get(result.errors[0]); REQUIRE(tm); - CHECK(builtinTypes->stringType == tm->rhsType); + CHECK(getBuiltins()->stringType == tm->rhsType); CHECK(CannotAssignToNever::Reason::PropertyNarrowed == tm->reason); REQUIRE(tm->cause.size() == 2); CHECK("\"Cat\"" == toString(tm->cause[0])); @@ -4828,7 +4830,7 @@ export type Worker = Worker.Worker return {} )"; - CheckResult result = frontend.check("game/library"); + CheckResult result = getFrontend().check("game/library"); LUAU_REQUIRE_NO_ERRORS(result); } @@ -5174,7 +5176,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "magic_functions_bidirectionally_inferred") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -5370,7 +5372,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; auto result = check(R"( @@ -5390,7 +5392,7 @@ TEST_CASE_FIXTURE(Fixture, "returning_mismatched_optional_in_table") TEST_CASE_FIXTURE(Fixture, "optional_function_in_table") { - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}}; + ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}}; LUAU_CHECK_NO_ERRORS(check(R"( local t: { (() -> ())? } = { @@ -5446,7 +5448,7 @@ TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; auto result = check(R"( @@ -5476,7 +5478,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_index_syntax_bidirectional_infer_with_tables { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; auto result = check((R"( @@ -5539,7 +5541,7 @@ TEST_CASE_FIXTURE(Fixture, "deeply_nested_classish_inference") TEST_CASE_FIXTURE(Fixture, "bigger_nested_table_causes_big_type_error") { - ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}}; + ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}}; auto result = check(R"( type File = { @@ -5648,7 +5650,7 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -5668,7 +5670,7 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( --!strict @@ -5687,7 +5689,7 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_regression") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( --!strict @@ -5707,7 +5709,7 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, }; CheckResult result = check(R"( @@ -5729,7 +5731,7 @@ TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, }; @@ -5748,7 +5750,7 @@ TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_nested_tables") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, }; @@ -5765,7 +5767,7 @@ TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, {FFlag::LuauDisablePrimitiveInferenceInLargeTables, true}, }; @@ -5799,4 +5801,50 @@ TEST_CASE_FIXTURE(Fixture, "extremely_large_table" * doctest::timeout(2.0)) #endif +TEST_CASE_FIXTURE(Fixture, "oss_1838") +{ + LUAU_REQUIRE_NO_ERRORS(check(R"( + local myTable = {} + myTable.foo = {} + myTable.foo.bar = {} + )")); +} + +TEST_CASE_FIXTURE(Fixture, "oss_1859") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}, + }; + + CheckResult result = check(R"( + --!strict + + type Cat = { + name: string, + age: number, + actions: { + otherfield: string, + meow: () -> string, + } + } + + local function new(): Cat + local self = {} + self.name = "Taz" + self.age = 12 + self.actions = {} + self.actions.meow = function() return "meow" end + -- We're missing `otherfield` here so we should complain. + return self + end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + auto err = get(result.errors[0]); + REQUIRE(err); + CHECK_EQ("Cat", toString(err->wantedType)); + CHECK_EQ("{ actions: { meow: (...any) -> string }, age: number, name: string }", toString(err->givenType)); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index 576fed67..c3a12e68 100644 --- a/tests/TypeInfer.test.cpp +++ b/tests/TypeInfer.test.cpp @@ -25,7 +25,7 @@ LUAU_FASTINT(LuauRecursionLimit) LUAU_FASTINT(LuauTypeInferTypePackLoopLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauAvoidDoubleNegation) LUAU_FASTFLAG(LuauInsertErrorTypesIntoIndexerResult) @@ -34,6 +34,9 @@ LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) LUAU_FASTFLAG(LuauSkipLvalueForCompoundAssignment) LUAU_FASTFLAG(LuauMissingFollowInAssignIndexConstraint) +LUAU_FASTFLAG(LuauOccursCheckForRefinement) +LUAU_FASTFLAG(LuauInferPolarityOfReadWriteProperties) +LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) using namespace Luau; @@ -71,7 +74,7 @@ TEST_CASE_FIXTURE(Fixture, "tc_error") CHECK_EQ( result.errors[0], - (TypeError{Location{Position{0, 35}, Position{0, 36}}, TypeMismatch{builtinTypes->numberType, builtinTypes->stringType}}) + (TypeError{Location{Position{0, 35}, Position{0, 36}}, TypeMismatch{getBuiltins()->numberType, getBuiltins()->stringType}}) ); } } @@ -95,7 +98,7 @@ TEST_CASE_FIXTURE(Fixture, "tc_error_2") Location{Position{0, 18}, Position{0, 22}}, TypeMismatch{ requireType("a"), - builtinTypes->stringType, + getBuiltins()->stringType, } }) ); @@ -210,8 +213,8 @@ TEST_CASE_FIXTURE(Fixture, "if_statement") } else { - CHECK_EQ(*builtinTypes->stringType, *requireType("a")); - CHECK_EQ(*builtinTypes->numberType, *requireType("b")); + CHECK_EQ(*getBuiltins()->stringType, *requireType("a")); + CHECK_EQ(*getBuiltins()->numberType, *requireType("b")); } } @@ -441,7 +444,7 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit") #endif ScopedFastInt luauRecursionLimit{FInt::LuauRecursionLimit, limit + 100}; ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit - 100}; - ScopedFastFlag _{FFlag::LuauEagerGeneralization3, false}; + ScopedFastFlag _{FFlag::LuauEagerGeneralization4, false}; CheckResult result = check(R"(("foo"))" + rep(":lower()", limit)); @@ -691,7 +694,7 @@ TEST_CASE_FIXTURE(Fixture, "stringify_nested_unions_with_optionals") LUAU_REQUIRE_ERROR_COUNT(1, result); TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK_EQ(builtinTypes->numberType, tm->wantedType); + CHECK_EQ(getBuiltins()->numberType, tm->wantedType); CHECK_EQ("(boolean | number | string)?", toString(tm->givenType)); } @@ -1614,7 +1617,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "be_sure_to_use_active_txnlog_when_evaluating */ TEST_CASE_FIXTURE(Fixture, "typeof_cannot_refine_builtin_alias") { - GlobalTypes& globals = frontend.globals; + GlobalTypes& globals = getFrontend().globals; TypeArena& arena = globals.globalTypes; unfreeze(arena); @@ -2024,7 +2027,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauEagerGeneralization3, true}, + {FFlag::LuauEagerGeneralization4, true}, }; auto result = check(R"( @@ -2059,7 +2062,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauEagerGeneralization3, true}, + {FFlag::LuauEagerGeneralization4, true}, }; CheckResult result = check(R"( @@ -2092,7 +2095,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion") { ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, - {FFlag::LuauEagerGeneralization2, true}, + {FFlag::LuauEagerGeneralization4, true}, }; LUAU_REQUIRE_ERRORS(check(R"( @@ -2302,7 +2305,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "config_reader_example") // test suite starts, which will cause an assert if we try to eagerly // generalize _after_ the test is set up. Additionally, this code block // crashes under the new solver without flags. - if (!FFlag::LuauEagerGeneralization3) + if (!FFlag::LuauEagerGeneralization4) return; fileResolver.source["game/ConfigReader"] = R"( @@ -2344,12 +2347,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "config_reader_example") local _ = ConfigReader:read("foobar")() )"; - LUAU_REQUIRE_ERRORS(frontend.check("game/Util")); + LUAU_REQUIRE_ERRORS(getFrontend().check("game/Util")); } TEST_CASE_FIXTURE(BuiltinsFixture, "is_safe_integer_example") { - if (!FFlag::LuauEagerGeneralization3) + if (!FFlag::LuauEagerGeneralization4) return; fileResolver.source["game/isInteger"] = R"( @@ -2373,7 +2376,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "is_safe_integer_example") end )"; - LUAU_REQUIRE_NO_ERRORS(frontend.check("game/Util")); + LUAU_REQUIRE_NO_ERRORS(getFrontend().check("game/Util")); } TEST_CASE_FIXTURE(BuiltinsFixture, "type_remover_heap_use_after_free") @@ -2414,4 +2417,30 @@ TEST_CASE_FIXTURE(Fixture, "fuzzer_missing_follow_in_assign_index_constraint") )")); } +TEST_CASE_FIXTURE(Fixture, "fuzzer_occurs_check_stack_overflow") +{ + ScopedFastFlag _{FFlag::LuauOccursCheckForRefinement, true}; + // We just want this to not stack overflow, it's ok for it to barf errors. + LUAU_REQUIRE_ERRORS(check(R"( + _ = if _ then _ + for l0 in ... do + type t0 = (()->((t0)->())|(any))|(typeof(_)) + end + )")); +} + +TEST_CASE_FIXTURE(Fixture, "fuzzer_infer_divergent_rw_props") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauEnableWriteOnlyProperties, true}, + {FFlag::LuauInferPolarityOfReadWriteProperties, true}, + }; + + LUAU_REQUIRE_NO_ERRORS(check(R"( + return function(l0:{_:(any)&(any),write _:any,}) + end + )")); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.tryUnify.test.cpp b/tests/TypeInfer.tryUnify.test.cpp index 66bf034e..a191d1e3 100644 --- a/tests/TypeInfer.tryUnify.test.cpp +++ b/tests/TypeInfer.tryUnify.test.cpp @@ -23,7 +23,7 @@ struct TryUnifyFixture : Fixture ScopePtr globalScope{new Scope{arena.addTypePack({TypeId{}})}}; InternalErrorReporter iceHandler; UnifierSharedState unifierState{&iceHandler}; - Normalizer normalizer{&arena, builtinTypes, NotNull{&unifierState}}; + Normalizer normalizer{&arena, getBuiltins(), NotNull{&unifierState}}; Unifier state{NotNull{&normalizer}, NotNull{globalScope.get()}, Location{}, Variance::Covariant}; }; @@ -43,11 +43,11 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "primitives_unify") TEST_CASE_FIXTURE(TryUnifyFixture, "compatible_functions_are_unified") { Type functionOne{TypeVariant{ - FunctionType(arena.addTypePack({arena.freshType(builtinTypes, globalScope->level)}), arena.addTypePack({builtinTypes->numberType})) + FunctionType(arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}), arena.addTypePack({getBuiltins()->numberType})) }}; Type functionTwo{TypeVariant{FunctionType( - arena.addTypePack({arena.freshType(builtinTypes, globalScope->level)}), arena.addTypePack({arena.freshType(builtinTypes, globalScope->level)}) + arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}), arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}) )}}; state.tryUnify(&functionTwo, &functionOne); @@ -61,16 +61,16 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "compatible_functions_are_unified") TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_functions_are_preserved") { - TypePackVar argPackOne{TypePack{{arena.freshType(builtinTypes, globalScope->level)}, std::nullopt}}; + TypePackVar argPackOne{TypePack{{arena.freshType(getBuiltins(), globalScope->level)}, std::nullopt}}; Type functionOne{TypeVariant{ - FunctionType(arena.addTypePack({arena.freshType(builtinTypes, globalScope->level)}), arena.addTypePack({builtinTypes->numberType})) + FunctionType(arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}), arena.addTypePack({getBuiltins()->numberType})) }}; Type functionOneSaved = functionOne.clone(); - TypePackVar argPackTwo{TypePack{{arena.freshType(builtinTypes, globalScope->level)}, std::nullopt}}; + TypePackVar argPackTwo{TypePack{{arena.freshType(getBuiltins(), globalScope->level)}, std::nullopt}}; Type functionTwo{TypeVariant{ - FunctionType(arena.addTypePack({arena.freshType(builtinTypes, globalScope->level)}), arena.addTypePack({builtinTypes->stringType})) + FunctionType(arena.addTypePack({arena.freshType(getBuiltins(), globalScope->level)}), arena.addTypePack({getBuiltins()->stringType})) }}; Type functionTwoSaved = functionTwo.clone(); @@ -86,11 +86,11 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_functions_are_preserved") TEST_CASE_FIXTURE(TryUnifyFixture, "tables_can_be_unified") { Type tableOne{TypeVariant{ - TableType{{{"foo", {arena.freshType(builtinTypes, globalScope->level)}}}, std::nullopt, globalScope->level, TableState::Unsealed}, + TableType{{{"foo", {arena.freshType(getBuiltins(), globalScope->level)}}}, std::nullopt, globalScope->level, TableState::Unsealed}, }}; Type tableTwo{TypeVariant{ - TableType{{{"foo", {arena.freshType(builtinTypes, globalScope->level)}}}, std::nullopt, globalScope->level, TableState::Unsealed}, + TableType{{{"foo", {arena.freshType(getBuiltins(), globalScope->level)}}}, std::nullopt, globalScope->level, TableState::Unsealed}, }}; CHECK_NE(*getMutable(&tableOne)->props["foo"].type(), *getMutable(&tableTwo)->props["foo"].type()); @@ -109,7 +109,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_tables_are_preserved") { Type tableOne{TypeVariant{ TableType{ - {{"foo", {arena.freshType(builtinTypes, globalScope->level)}}, {"bar", {builtinTypes->numberType}}}, + {{"foo", {arena.freshType(getBuiltins(), globalScope->level)}}, {"bar", {getBuiltins()->numberType}}}, std::nullopt, globalScope->level, TableState::Unsealed @@ -118,7 +118,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_tables_are_preserved") Type tableTwo{TypeVariant{ TableType{ - {{"foo", {arena.freshType(builtinTypes, globalScope->level)}}, {"bar", {builtinTypes->stringType}}}, + {{"foo", {arena.freshType(getBuiltins(), globalScope->level)}}, {"bar", {getBuiltins()->stringType}}}, std::nullopt, globalScope->level, TableState::Unsealed @@ -235,8 +235,8 @@ TEST_CASE_FIXTURE(Fixture, "typepack_unification_should_trim_free_tails") TEST_CASE_FIXTURE(TryUnifyFixture, "variadic_type_pack_unification") { - TypePackVar testPack{TypePack{{builtinTypes->numberType, builtinTypes->stringType}, std::nullopt}}; - TypePackVar variadicPack{VariadicTypePack{builtinTypes->numberType}}; + TypePackVar testPack{TypePack{{getBuiltins()->numberType, getBuiltins()->stringType}, std::nullopt}}; + TypePackVar variadicPack{VariadicTypePack{getBuiltins()->numberType}}; state.tryUnify(&testPack, &variadicPack); CHECK(state.failure); @@ -245,9 +245,9 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "variadic_type_pack_unification") TEST_CASE_FIXTURE(TryUnifyFixture, "variadic_tails_respect_progress") { - TypePackVar variadicPack{VariadicTypePack{builtinTypes->booleanType}}; - TypePackVar a{TypePack{{builtinTypes->numberType, builtinTypes->stringType, builtinTypes->booleanType, builtinTypes->booleanType}}}; - TypePackVar b{TypePack{{builtinTypes->numberType, builtinTypes->stringType}, &variadicPack}}; + TypePackVar variadicPack{VariadicTypePack{getBuiltins()->booleanType}}; + TypePackVar a{TypePack{{getBuiltins()->numberType, getBuiltins()->stringType, getBuiltins()->booleanType, getBuiltins()->booleanType}}}; + TypePackVar b{TypePack{{getBuiltins()->numberType, getBuiltins()->stringType}, &variadicPack}}; state.tryUnify(&b, &a); CHECK(!state.failure); @@ -290,19 +290,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cli_41095_concat_log_in_sealed_table_unifica TEST_CASE_FIXTURE(TryUnifyFixture, "free_tail_is_grown_properly") { TypePackId threeNumbers = - arena.addTypePack(TypePack{{builtinTypes->numberType, builtinTypes->numberType, builtinTypes->numberType}, std::nullopt}); - TypePackId numberAndFreeTail = arena.addTypePack(TypePack{{builtinTypes->numberType}, arena.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}})}); + arena.addTypePack(TypePack{{getBuiltins()->numberType, getBuiltins()->numberType, getBuiltins()->numberType}, std::nullopt}); + TypePackId numberAndFreeTail = arena.addTypePack(TypePack{{getBuiltins()->numberType}, arena.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}})}); CHECK(state.canUnify(numberAndFreeTail, threeNumbers).empty()); } TEST_CASE_FIXTURE(TryUnifyFixture, "recursive_metatable_getmatchtag") { - Type redirect{FreeType{TypeLevel{}, builtinTypes->neverType, builtinTypes->unknownType}}; + Type redirect{FreeType{TypeLevel{}, getBuiltins()->neverType, getBuiltins()->unknownType}}; Type table{TableType{}}; Type metatable{MetatableType{&redirect, &table}}; redirect = BoundType{&metatable}; // Now we have a metatable that is recursive on the table type - Type variant{UnionType{{&metatable, builtinTypes->numberType}}}; + Type variant{UnionType{{&metatable, getBuiltins()->numberType}}}; state.tryUnify(&metatable, &variant); } @@ -316,13 +316,13 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "cli_50320_follow_in_any_unification") state.tryUnify(&free, &target); // Shouldn't assert or error. - state.tryUnify(&func, builtinTypes->anyType); + state.tryUnify(&func, getBuiltins()->anyType); } TEST_CASE_FIXTURE(TryUnifyFixture, "txnlog_preserves_type_owner") { - TypeId a = arena.freshType(builtinTypes, TypeLevel{}); - TypeId b = builtinTypes->numberType; + TypeId a = arena.freshType(getBuiltins(), TypeLevel{}); + TypeId b = getBuiltins()->numberType; state.tryUnify(a, b); state.log.commit(); @@ -333,7 +333,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "txnlog_preserves_type_owner") TEST_CASE_FIXTURE(TryUnifyFixture, "txnlog_preserves_pack_owner") { TypePackId a = arena.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}); - TypePackId b = builtinTypes->anyTypePack; + TypePackId b = getBuiltins()->anyTypePack; state.tryUnify(a, b); state.log.commit(); @@ -343,11 +343,11 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "txnlog_preserves_pack_owner") TEST_CASE_FIXTURE(TryUnifyFixture, "fuzz_tail_unification_issue") { - TypePackVar variadicAny{VariadicTypePack{builtinTypes->anyType}}; - TypePackVar packTmp{TypePack{{builtinTypes->anyType}, &variadicAny}}; - TypePackVar packSub{TypePack{{builtinTypes->anyType, builtinTypes->anyType}, &packTmp}}; + TypePackVar variadicAny{VariadicTypePack{getBuiltins()->anyType}}; + TypePackVar packTmp{TypePack{{getBuiltins()->anyType}, &variadicAny}}; + TypePackVar packSub{TypePack{{getBuiltins()->anyType, getBuiltins()->anyType}, &packTmp}}; - Type freeTy{FreeType{TypeLevel{}, builtinTypes->neverType, builtinTypes->unknownType}}; + Type freeTy{FreeType{TypeLevel{}, getBuiltins()->neverType, getBuiltins()->unknownType}}; TypePackVar freeTp{FreeTypePack{TypeLevel{}}}; TypePackVar packSuper{TypePack{{&freeTy}, &freeTp}}; diff --git a/tests/TypeInfer.typePacks.test.cpp b/tests/TypeInfer.typePacks.test.cpp index 1c4c8100..80f50f99 100644 --- a/tests/TypeInfer.typePacks.test.cpp +++ b/tests/TypeInfer.typePacks.test.cpp @@ -12,9 +12,9 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauInstantiateInSubtyping) -LUAU_FASTFLAG(LuauEagerGeneralization3) +LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauReportSubtypingErrors) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauFixEmptyTypePackStringification) TEST_SUITE_BEGIN("TypePackTests"); @@ -35,8 +35,8 @@ TEST_CASE_FIXTURE(Fixture, "infer_multi_return") const auto& [returns, tail] = flatten(takeTwoType->retTypes); CHECK_EQ(2, returns.size()); - CHECK_EQ(builtinTypes->numberType, follow(returns[0])); - CHECK_EQ(builtinTypes->numberType, follow(returns[1])); + CHECK_EQ(getBuiltins()->numberType, follow(returns[0])); + CHECK_EQ(getBuiltins()->numberType, follow(returns[1])); CHECK(!tail); } @@ -82,9 +82,9 @@ TEST_CASE_FIXTURE(Fixture, "last_element_of_return_statement_can_itself_be_a_pac const auto& [rets, tail] = flatten(takeOneMoreType->retTypes); REQUIRE_EQ(3, rets.size()); - CHECK_EQ(builtinTypes->numberType, follow(rets[0])); - CHECK_EQ(builtinTypes->numberType, follow(rets[1])); - CHECK_EQ(builtinTypes->numberType, follow(rets[2])); + CHECK_EQ(getBuiltins()->numberType, follow(rets[0])); + CHECK_EQ(getBuiltins()->numberType, follow(rets[1])); + CHECK_EQ(getBuiltins()->numberType, follow(rets[2])); CHECK(!tail); } @@ -99,7 +99,7 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) CHECK_EQ("((c...) -> (b...), (a) -> (c...), a) -> (b...)", toString(requireType("apply"))); else CHECK_EQ("((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply"))); @@ -195,28 +195,28 @@ TEST_CASE_FIXTURE(Fixture, "parenthesized_varargs_returns_any") TEST_CASE_FIXTURE(Fixture, "variadic_packs") { - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); - TypePackId listOfNumbers = arena.addTypePack(TypePackVar{VariadicTypePack{builtinTypes->numberType}}); - TypePackId listOfStrings = arena.addTypePack(TypePackVar{VariadicTypePack{builtinTypes->stringType}}); + TypePackId listOfNumbers = arena.addTypePack(TypePackVar{VariadicTypePack{getBuiltins()->numberType}}); + TypePackId listOfStrings = arena.addTypePack(TypePackVar{VariadicTypePack{getBuiltins()->stringType}}); // clang-format off - addGlobalBinding(frontend.globals, "foo", + addGlobalBinding(getFrontend().globals, "foo", arena.addType( FunctionType{ listOfNumbers, - arena.addTypePack({builtinTypes->numberType}) + arena.addTypePack({getBuiltins()->numberType}) } ), "@test" ); - addGlobalBinding(frontend.globals, "bar", + addGlobalBinding(getFrontend().globals, "bar", arena.addType( FunctionType{ - arena.addTypePack({{builtinTypes->numberType}, listOfStrings}), - arena.addTypePack({builtinTypes->numberType}) + arena.addTypePack({{getBuiltins()->numberType}, listOfStrings}), + arena.addTypePack({getBuiltins()->numberType}) } ), "@test" @@ -238,11 +238,11 @@ TEST_CASE_FIXTURE(Fixture, "variadic_packs") CHECK(Location{Position{4, 29}, Position{4, 30}} == result.errors[1].location); CHECK_EQ( - result.errors[0], (TypeError{Location(Position{3, 21}, Position{3, 26}), TypeMismatch{builtinTypes->numberType, builtinTypes->stringType}}) + result.errors[0], (TypeError{Location(Position{3, 21}, Position{3, 26}), TypeMismatch{getBuiltins()->numberType, getBuiltins()->stringType}}) ); CHECK_EQ( - result.errors[1], (TypeError{Location(Position{4, 29}, Position{4, 30}), TypeMismatch{builtinTypes->stringType, builtinTypes->numberType}}) + result.errors[1], (TypeError{Location(Position{4, 29}, Position{4, 30}), TypeMismatch{getBuiltins()->stringType, getBuiltins()->numberType}}) ); } @@ -376,7 +376,7 @@ export type Packed = { a: T, b: (U...) -> () } return {} )"; - CheckResult aResult = frontend.check("game/A"); + CheckResult aResult = getFrontend().check("game/A"); LUAU_REQUIRE_NO_ERRORS(aResult); CheckResult bResult = check(R"( @@ -880,7 +880,7 @@ export type H = { b: (T...) -> T... } return {} )"; - CheckResult resultTypes = frontend.check("Module/Types"); + CheckResult resultTypes = getFrontend().check("Module/Types"); LUAU_REQUIRE_NO_ERRORS(resultTypes); fileResolver.source["Module/Users"] = R"( @@ -897,7 +897,7 @@ local g: Types.G<...number> local h: Types.H<> )"; - CheckResult resultUsers = frontend.check("Module/Users"); + CheckResult resultUsers = getFrontend().check("Module/Users"); LUAU_REQUIRE_NO_ERRORS(resultUsers); CHECK_EQ(toString(requireType("Module/Users", "a")), "A"); @@ -1098,7 +1098,7 @@ TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments") TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments_free") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( function foo(...: T...): T... diff --git a/tests/TypeInfer.typestates.test.cpp b/tests/TypeInfer.typestates.test.cpp index 00a6d919..ff5c34f0 100644 --- a/tests/TypeInfer.typestates.test.cpp +++ b/tests/TypeInfer.typestates.test.cpp @@ -7,8 +7,8 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow) LUAU_FASTFLAG(LuauReportSubtypingErrors) -LUAU_FASTFLAG(LuauEagerGeneralization3) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) using namespace Luau; @@ -67,7 +67,7 @@ TEST_CASE_FIXTURE(TypeStateFixture, "assign_different_values_to_x") TEST_CASE_FIXTURE(TypeStateFixture, "parameter_x_was_constrained_by_two_types") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; // Parameter `x` has a fresh type `'x` bounded by `never` and `unknown`. // The first use of `x` constrains `x`'s upper bound by `string | number`. @@ -414,7 +414,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_futur ScopedFastFlag sffs[] = { {FFlag::LuauSolverV2, true}, {FFlag::LuauReportSubtypingErrors, true}, - {FFlag::LuauEagerGeneralization3, true}, + {FFlag::LuauEagerGeneralization4, true}, }; CheckResult result = check(R"( diff --git a/tests/TypeInfer.unionTypes.test.cpp b/tests/TypeInfer.unionTypes.test.cpp index 994b0a3d..de7971c6 100644 --- a/tests/TypeInfer.unionTypes.test.cpp +++ b/tests/TypeInfer.unionTypes.test.cpp @@ -10,8 +10,8 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauEagerGeneralization3) -LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) +LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) TEST_SUITE_BEGIN("UnionTypes"); @@ -583,7 +583,7 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_union_all") TEST_CASE_FIXTURE(Fixture, "error_detailed_optional") { - ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true}; CheckResult result = check(R"( type X = { x: number } @@ -625,20 +625,20 @@ TEST_CASE_FIXTURE(Fixture, "indexing_into_a_cyclic_union_doesnt_crash") // It shouldn't be possible to craft a cyclic union, but even if we do, we // shouldn't blow up. - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); - TypeId badCyclicUnionTy = arena.freshType(builtinTypes, frontend.globals.globalScope.get()); + TypeId badCyclicUnionTy = arena.freshType(getBuiltins(), getFrontend().globals.globalScope.get()); UnionType u; u.options.push_back(badCyclicUnionTy); u.options.push_back(arena.addType(TableType{ - {}, TableIndexer{builtinTypes->numberType, builtinTypes->numberType}, TypeLevel{}, frontend.globals.globalScope.get(), TableState::Sealed + {}, TableIndexer{getBuiltins()->numberType, getBuiltins()->numberType}, TypeLevel{}, getFrontend().globals.globalScope.get(), TableState::Sealed })); asMutable(badCyclicUnionTy)->ty.emplace(std::move(u)); - frontend.globals.globalScope->exportedTypeBindings["BadCyclicUnion"] = TypeFun{{}, badCyclicUnionTy}; + getFrontend().globals.globalScope->exportedTypeBindings["BadCyclicUnion"] = TypeFun{{}, badCyclicUnionTy}; freeze(arena); @@ -895,7 +895,7 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types") LUAU_REQUIRE_NO_ERRORS(result); - if (FFlag::LuauEagerGeneralization3) + if (FFlag::LuauEagerGeneralization4) CHECK_EQ( "(({ read x: a } & { x: number }) | ({ read x: a } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f")) ); @@ -1000,7 +1000,7 @@ TEST_CASE_FIXTURE(Fixture, "suppress_errors_for_prop_lookup_of_a_union_that_incl { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - registerHiddenTypes(&frontend); + registerHiddenTypes(getFrontend()); CheckResult result = check(R"( function f(a: err | Not) diff --git a/tests/TypePath.test.cpp b/tests/TypePath.test.cpp index d54506af..39eeb356 100644 --- a/tests/TypePath.test.cpp +++ b/tests/TypePath.test.cpp @@ -108,7 +108,7 @@ TEST_SUITE_BEGIN("TypePathTraversal"); TEST_CASE_FIXTURE(TypePathFixture, "empty_traversal") { - CHECK(traverseForType(builtinTypes->numberType, kEmpty, builtinTypes) == builtinTypes->numberType); + CHECK(traverseForType(getBuiltins()->numberType, kEmpty, getBuiltins()) == getBuiltins()->numberType); } TEST_CASE_FIXTURE(TypePathFixture, "table_property") @@ -117,12 +117,14 @@ TEST_CASE_FIXTURE(TypePathFixture, "table_property") local x = { y = 123 } )"); - CHECK(traverseForType(requireType("x"), Path(TypePath::Property{"y", true}), builtinTypes) == builtinTypes->numberType); + CHECK(traverseForType(requireType("x"), Path(TypePath::Property{"y", true}), getBuiltins()) == getBuiltins()->numberType); } TEST_CASE_FIXTURE(ExternTypeFixture, "class_property") { - CHECK(traverseForType(vector2InstanceType, Path(TypePath::Property{"X", true}), builtinTypes) == builtinTypes->numberType); + // Force this here because vector2InstanceType won't get initialized until the frontend has been forced + getFrontend(); + CHECK(traverseForType(vector2InstanceType, Path(TypePath::Property{"X", true}), getBuiltins()) == getBuiltins()->numberType); } TEST_CASE_FIXTURE(TypePathBuiltinsFixture, "metatable_property") @@ -149,7 +151,7 @@ TEST_CASE_FIXTURE(TypePathBuiltinsFixture, "metatable_property") )"); } - CHECK(traverseForType(requireType("x"), Path(TypePath::Property::read("x")), builtinTypes) == builtinTypes->numberType); + CHECK(traverseForType(requireType("x"), Path(TypePath::Property::read("x")), getBuiltins()) == getBuiltins()->numberType); } TEST_CASE_FIXTURE(TypePathFixture, "index") @@ -162,12 +164,12 @@ TEST_CASE_FIXTURE(TypePathFixture, "index") SUBCASE("in_bounds") { - CHECK(traverseForType(requireTypeAlias("T"), Path(TypePath::Index{1}), builtinTypes) == builtinTypes->stringType); + CHECK(traverseForType(requireTypeAlias("T"), Path(TypePath::Index{1}), getBuiltins()) == getBuiltins()->stringType); } SUBCASE("out_of_bounds") { - CHECK(traverseForType(requireTypeAlias("T"), Path(TypePath::Index{97}), builtinTypes) == std::nullopt); + CHECK(traverseForType(requireTypeAlias("T"), Path(TypePath::Index{97}), getBuiltins()) == std::nullopt); } } @@ -180,7 +182,7 @@ TEST_CASE_FIXTURE(TypePathFixture, "index") SUBCASE("in_bounds") { - auto result = traverseForType(requireTypeAlias("T"), Path(TypePath::Index{1}), builtinTypes); + auto result = traverseForType(requireTypeAlias("T"), Path(TypePath::Index{1}), getBuiltins()); CHECK(result); if (result) @@ -189,7 +191,7 @@ TEST_CASE_FIXTURE(TypePathFixture, "index") SUBCASE("out_of_bounds") { - CHECK(traverseForType(requireTypeAlias("T"), Path(TypePath::Index{97}), builtinTypes) == std::nullopt); + CHECK(traverseForType(requireTypeAlias("T"), Path(TypePath::Index{97}), getBuiltins()) == std::nullopt); } } @@ -203,14 +205,14 @@ TEST_CASE_FIXTURE(TypePathFixture, "index") SUBCASE("in_bounds") { Path path = Path({TypePath::PackField::Arguments, TypePath::Index{1}}); - auto result = traverseForType(requireTypeAlias("T"), path, builtinTypes); - CHECK(result == builtinTypes->stringType); + auto result = traverseForType(requireTypeAlias("T"), path, getBuiltins()); + CHECK(result == getBuiltins()->stringType); } SUBCASE("out_of_bounds") { Path path = Path({TypePath::PackField::Arguments, TypePath::Index{72}}); - auto result = traverseForType(requireTypeAlias("T"), path, builtinTypes); + auto result = traverseForType(requireTypeAlias("T"), path, getBuiltins()); CHECK(result == std::nullopt); } } @@ -218,10 +220,11 @@ TEST_CASE_FIXTURE(TypePathFixture, "index") TEST_CASE_FIXTURE(ExternTypeFixture, "metatables") { + getFrontend(); SUBCASE("string") { - auto result = traverseForType(builtinTypes->stringType, Path(TypeField::Metatable), builtinTypes); - CHECK(result == getMetatable(builtinTypes->stringType, builtinTypes)); + auto result = traverseForType(getBuiltins()->stringType, Path(TypeField::Metatable), getBuiltins()); + CHECK(result == getMetatable(getBuiltins()->stringType, getBuiltins())); } SUBCASE("string_singleton") @@ -230,8 +233,8 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "metatables") type T = "foo" )"); - auto result = traverseForType(requireTypeAlias("T"), Path(TypeField::Metatable), builtinTypes); - CHECK(result == getMetatable(builtinTypes->stringType, builtinTypes)); + auto result = traverseForType(requireTypeAlias("T"), Path(TypeField::Metatable), getBuiltins()); + CHECK(result == getMetatable(getBuiltins()->stringType, getBuiltins())); } SUBCASE("table") @@ -245,7 +248,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "metatables") )"); // Tricky test setup because 'setmetatable' mutates the argument 'tbl' type - auto result = traverseForType(requireType("res"), Path(TypeField::Table), builtinTypes); + auto result = traverseForType(requireType("res"), Path(TypeField::Table), getBuiltins()); auto expected = lookupType("Table"); REQUIRE(expected); CHECK(result == follow(*expected)); @@ -258,13 +261,13 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "metatables") local tbl = setmetatable({}, mt) )"); - auto result = traverseForType(requireType("tbl"), Path(TypeField::Metatable), builtinTypes); + auto result = traverseForType(requireType("tbl"), Path(TypeField::Metatable), getBuiltins()); CHECK(result == requireType("mt")); } SUBCASE("class") { - auto result = traverseForType(vector2InstanceType, Path(TypeField::Metatable), builtinTypes); + auto result = traverseForType(vector2InstanceType, Path(TypeField::Metatable), getBuiltins()); // ExternTypeFixture's Vector2 metatable is just an empty table, but it's there. CHECK(result); } @@ -274,31 +277,31 @@ TEST_CASE_FIXTURE(TypePathFixture, "bounds") { SUBCASE("free_type") { - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); - TypeId ty = arena.freshType(frontend.builtinTypes, frontend.globals.globalScope.get()); + TypeId ty = arena.freshType(getBuiltins(), getFrontend().globals.globalScope.get()); FreeType* ft = getMutable(ty); SUBCASE("upper") { - ft->upperBound = builtinTypes->numberType; - auto result = traverseForType(ty, Path(TypeField::UpperBound), builtinTypes); - CHECK(result == builtinTypes->numberType); + ft->upperBound = getBuiltins()->numberType; + auto result = traverseForType(ty, Path(TypeField::UpperBound), getBuiltins()); + CHECK(result == getBuiltins()->numberType); } SUBCASE("lower") { - ft->lowerBound = builtinTypes->booleanType; - auto result = traverseForType(ty, Path(TypeField::LowerBound), builtinTypes); - CHECK(result == builtinTypes->booleanType); + ft->lowerBound = getBuiltins()->booleanType; + auto result = traverseForType(ty, Path(TypeField::LowerBound), getBuiltins()); + CHECK(result == getBuiltins()->booleanType); } } SUBCASE("unbounded_type") { - CHECK(traverseForType(builtinTypes->numberType, Path(TypeField::UpperBound), builtinTypes) == std::nullopt); - CHECK(traverseForType(builtinTypes->numberType, Path(TypeField::LowerBound), builtinTypes) == std::nullopt); + CHECK(traverseForType(getBuiltins()->numberType, Path(TypeField::UpperBound), getBuiltins()) == std::nullopt); + CHECK(traverseForType(getBuiltins()->numberType, Path(TypeField::LowerBound), getBuiltins()) == std::nullopt); } } @@ -312,11 +315,11 @@ TEST_CASE_FIXTURE(TypePathFixture, "indexers") type T = { [string]: boolean } )"); - auto lookupResult = traverseForType(requireTypeAlias("T"), Path(TypeField::IndexLookup), builtinTypes); - auto resultResult = traverseForType(requireTypeAlias("T"), Path(TypeField::IndexResult), builtinTypes); + auto lookupResult = traverseForType(requireTypeAlias("T"), Path(TypeField::IndexLookup), getBuiltins()); + auto resultResult = traverseForType(requireTypeAlias("T"), Path(TypeField::IndexResult), getBuiltins()); - CHECK(lookupResult == builtinTypes->stringType); - CHECK(resultResult == builtinTypes->booleanType); + CHECK(lookupResult == getBuiltins()->stringType); + CHECK(resultResult == getBuiltins()->booleanType); } SUBCASE("no_indexer") @@ -325,8 +328,8 @@ TEST_CASE_FIXTURE(TypePathFixture, "indexers") type T = { y: number } )"); - auto lookupResult = traverseForType(requireTypeAlias("T"), Path(TypeField::IndexLookup), builtinTypes); - auto resultResult = traverseForType(requireTypeAlias("T"), Path(TypeField::IndexResult), builtinTypes); + auto lookupResult = traverseForType(requireTypeAlias("T"), Path(TypeField::IndexLookup), getBuiltins()); + auto resultResult = traverseForType(requireTypeAlias("T"), Path(TypeField::IndexResult), getBuiltins()); CHECK(lookupResult == std::nullopt); CHECK(resultResult == std::nullopt); @@ -340,17 +343,17 @@ TEST_CASE_FIXTURE(TypePathFixture, "negated") { SUBCASE("valid") { - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); - TypeId ty = arena.addType(NegationType{builtinTypes->numberType}); - auto result = traverseForType(ty, Path(TypeField::Negated), builtinTypes); - CHECK(result == builtinTypes->numberType); + TypeId ty = arena.addType(NegationType{getBuiltins()->numberType}); + auto result = traverseForType(ty, Path(TypeField::Negated), getBuiltins()); + CHECK(result == getBuiltins()->numberType); } SUBCASE("not_negation") { - auto result = traverseForType(builtinTypes->numberType, Path(TypeField::Negated), builtinTypes); + auto result = traverseForType(getBuiltins()->numberType, Path(TypeField::Negated), getBuiltins()); CHECK(result == std::nullopt); } } @@ -359,17 +362,17 @@ TEST_CASE_FIXTURE(TypePathFixture, "variadic") { SUBCASE("valid") { - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); - TypePackId tp = arena.addTypePack(VariadicTypePack{builtinTypes->numberType}); - auto result = traverseForType(tp, Path(TypeField::Variadic), builtinTypes); - CHECK(result == builtinTypes->numberType); + TypePackId tp = arena.addTypePack(VariadicTypePack{getBuiltins()->numberType}); + auto result = traverseForType(tp, Path(TypeField::Variadic), getBuiltins()); + CHECK(result == getBuiltins()->numberType); } SUBCASE("not_variadic") { - auto result = traverseForType(builtinTypes->numberType, Path(TypeField::Variadic), builtinTypes); + auto result = traverseForType(getBuiltins()->numberType, Path(TypeField::Variadic), getBuiltins()); CHECK(result == std::nullopt); } } @@ -383,7 +386,7 @@ TEST_CASE_FIXTURE(TypePathFixture, "arguments") end )"); - auto result = traverseForPack(requireType("f"), Path(PackField::Arguments), builtinTypes); + auto result = traverseForPack(requireType("f"), Path(PackField::Arguments), getBuiltins()); CHECK(result); if (result) CHECK(toString(*result) == "number, string"); @@ -391,7 +394,7 @@ TEST_CASE_FIXTURE(TypePathFixture, "arguments") SUBCASE("not_function") { - auto result = traverseForPack(builtinTypes->booleanType, Path(PackField::Arguments), builtinTypes); + auto result = traverseForPack(getBuiltins()->booleanType, Path(PackField::Arguments), getBuiltins()); CHECK(result == std::nullopt); } } @@ -406,7 +409,7 @@ TEST_CASE_FIXTURE(TypePathFixture, "returns") end )"); - auto result = traverseForPack(requireType("f"), Path(PackField::Returns), builtinTypes); + auto result = traverseForPack(requireType("f"), Path(PackField::Returns), getBuiltins()); CHECK(result); if (result) CHECK(toString(*result) == "number, string"); @@ -414,7 +417,7 @@ TEST_CASE_FIXTURE(TypePathFixture, "returns") SUBCASE("not_function") { - auto result = traverseForPack(builtinTypes->booleanType, Path(PackField::Returns), builtinTypes); + auto result = traverseForPack(getBuiltins()->booleanType, Path(PackField::Returns), getBuiltins()); CHECK(result == std::nullopt); } } @@ -427,7 +430,7 @@ TEST_CASE_FIXTURE(TypePathFixture, "tail") type T = (number, string, ...boolean) -> () )"); - auto result = traverseForPack(requireTypeAlias("T"), Path({PackField::Arguments, PackField::Tail}), builtinTypes); + auto result = traverseForPack(requireTypeAlias("T"), Path({PackField::Arguments, PackField::Tail}), getBuiltins()); CHECK(result); if (result) CHECK(toString(*result) == "...boolean"); @@ -439,13 +442,13 @@ TEST_CASE_FIXTURE(TypePathFixture, "tail") type T = (number, string) -> () )"); - auto result = traverseForPack(requireTypeAlias("T"), Path({PackField::Arguments, PackField::Tail}), builtinTypes); + auto result = traverseForPack(requireTypeAlias("T"), Path({PackField::Arguments, PackField::Tail}), getBuiltins()); CHECK(result == std::nullopt); } SUBCASE("type") { - auto result = traverseForPack(builtinTypes->stringType, Path({PackField::Arguments, PackField::Tail}), builtinTypes); + auto result = traverseForPack(getBuiltins()->stringType, Path({PackField::Arguments, PackField::Tail}), getBuiltins()); CHECK(result == std::nullopt); } } @@ -456,25 +459,25 @@ TEST_CASE_FIXTURE(TypePathFixture, "cycles" * doctest::timeout(0.5)) // where there _is_ no traversal. SUBCASE("bound_cycle") { - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); TypeId a = arena.addType(BlockedType{}); TypeId b = arena.addType(BoundType{a}); asMutable(a)->ty.emplace(b); - CHECK_THROWS(traverseForType(a, Path(TypeField::IndexResult), builtinTypes)); + CHECK_THROWS(traverseForType(a, Path(TypeField::IndexResult), getBuiltins())); } SUBCASE("table_contains_itself") { - TypeArena& arena = frontend.globals.globalTypes; + TypeArena& arena = getFrontend().globals.globalTypes; unfreeze(arena); TypeId tbl = arena.addType(TableType{}); getMutable(tbl)->props["a"] = Luau::Property(tbl); - auto result = traverseForType(tbl, Path(TypePath::Property{"a", true}), builtinTypes); + auto result = traverseForType(tbl, Path(TypePath::Property{"a", true}), getBuiltins()); CHECK(result == tbl); } } @@ -495,7 +498,7 @@ TEST_CASE_FIXTURE(TypePathFixture, "step_limit") TypeId root = requireTypeAlias("T"); Path path = PathBuilder().readProp("x").readProp("y").readProp("z").build(); - auto result = traverseForType(root, path, builtinTypes); + auto result = traverseForType(root, path, getBuiltins()); CHECK(!result); } @@ -513,8 +516,8 @@ TEST_CASE_FIXTURE(TypePathBuiltinsFixture, "complex_chains") TypeId root = requireTypeAlias("Tab"); Path path = PathBuilder().mt().readProp("__add").rets().index(0).build(); - auto result = traverseForType(root, path, builtinTypes); - CHECK(result == builtinTypes->numberType); + auto result = traverseForType(root, path, getBuiltins()); + CHECK(result == getBuiltins()->numberType); } SUBCASE("overloaded_fn_overload_one_argument_two") @@ -527,8 +530,8 @@ TEST_CASE_FIXTURE(TypePathBuiltinsFixture, "complex_chains") TypeId root = requireTypeAlias("Obj"); Path path = PathBuilder().readProp("method").index(0).args().index(1).build(); - auto result = traverseForType(root, path, builtinTypes); - CHECK(*result == builtinTypes->falseType); + auto result = traverseForType(root, path, getBuiltins()); + CHECK(*result == getBuiltins()->falseType); } } diff --git a/tests/TypeVar.test.cpp b/tests/TypeVar.test.cpp index 50064706..b71fee79 100644 --- a/tests/TypeVar.test.cpp +++ b/tests/TypeVar.test.cpp @@ -15,13 +15,13 @@ TEST_SUITE_BEGIN("TypeTests"); TEST_CASE_FIXTURE(Fixture, "primitives_are_equal") { - REQUIRE_EQ(builtinTypes->booleanType, builtinTypes->booleanType); + REQUIRE_EQ(getBuiltins()->booleanType, getBuiltins()->booleanType); } TEST_CASE_FIXTURE(Fixture, "bound_type_is_equal_to_that_which_it_is_bound") { - Type bound(BoundType(builtinTypes->booleanType)); - REQUIRE_EQ(bound, *builtinTypes->booleanType); + Type bound(BoundType(getBuiltins()->booleanType)); + REQUIRE_EQ(bound, *getBuiltins()->booleanType); } TEST_CASE_FIXTURE(Fixture, "equivalent_cyclic_tables_are_equal") @@ -53,8 +53,8 @@ TEST_CASE_FIXTURE(Fixture, "different_cyclic_tables_are_not_equal") TEST_CASE_FIXTURE(Fixture, "return_type_of_function_is_not_parenthesized_if_just_one_value") { auto emptyArgumentPack = TypePackVar{TypePack{}}; - auto returnPack = TypePackVar{TypePack{{builtinTypes->numberType}}}; - auto returnsTwo = Type(FunctionType(frontend.globals.globalScope->level, &emptyArgumentPack, &returnPack)); + auto returnPack = TypePackVar{TypePack{{getBuiltins()->numberType}}}; + auto returnsTwo = Type(FunctionType(getFrontend().globals.globalScope->level, &emptyArgumentPack, &returnPack)); std::string res = toString(&returnsTwo); CHECK_EQ("() -> number", res); @@ -63,8 +63,8 @@ TEST_CASE_FIXTURE(Fixture, "return_type_of_function_is_not_parenthesized_if_just TEST_CASE_FIXTURE(Fixture, "return_type_of_function_is_parenthesized_if_not_just_one_value") { auto emptyArgumentPack = TypePackVar{TypePack{}}; - auto returnPack = TypePackVar{TypePack{{builtinTypes->numberType, builtinTypes->numberType}}}; - auto returnsTwo = Type(FunctionType(frontend.globals.globalScope->level, &emptyArgumentPack, &returnPack)); + auto returnPack = TypePackVar{TypePack{{getBuiltins()->numberType, getBuiltins()->numberType}}}; + auto returnsTwo = Type(FunctionType(getFrontend().globals.globalScope->level, &emptyArgumentPack, &returnPack)); std::string res = toString(&returnsTwo); CHECK_EQ("() -> (number, number)", res); @@ -75,8 +75,8 @@ TEST_CASE_FIXTURE(Fixture, "return_type_of_function_is_parenthesized_if_tail_is_ auto emptyArgumentPack = TypePackVar{TypePack{}}; auto free = FreeTypePack(TypeLevel()); auto freePack = TypePackVar{TypePackVariant{free}}; - auto returnPack = TypePackVar{TypePack{{builtinTypes->numberType}, &freePack}}; - auto returnsTwo = Type(FunctionType(frontend.globals.globalScope->level, &emptyArgumentPack, &returnPack)); + auto returnPack = TypePackVar{TypePack{{getBuiltins()->numberType}, &freePack}}; + auto returnsTwo = Type(FunctionType(getFrontend().globals.globalScope->level, &emptyArgumentPack, &returnPack)); std::string res = toString(&returnsTwo); CHECK_EQ(res, "() -> (number, a...)"); @@ -85,9 +85,9 @@ TEST_CASE_FIXTURE(Fixture, "return_type_of_function_is_parenthesized_if_tail_is_ TEST_CASE_FIXTURE(Fixture, "subset_check") { UnionType super, sub, notSub; - super.options = {builtinTypes->numberType, builtinTypes->stringType, builtinTypes->booleanType}; - sub.options = {builtinTypes->numberType, builtinTypes->stringType}; - notSub.options = {builtinTypes->numberType, builtinTypes->nilType}; + super.options = {getBuiltins()->numberType, getBuiltins()->stringType, getBuiltins()->booleanType}; + sub.options = {getBuiltins()->numberType, getBuiltins()->stringType}; + notSub.options = {getBuiltins()->numberType, getBuiltins()->nilType}; CHECK(isSubset(super, sub)); CHECK(!isSubset(super, notSub)); @@ -96,7 +96,7 @@ TEST_CASE_FIXTURE(Fixture, "subset_check") TEST_CASE_FIXTURE(Fixture, "iterate_over_UnionType") { UnionType utv; - utv.options = {builtinTypes->numberType, builtinTypes->stringType, builtinTypes->anyType}; + utv.options = {getBuiltins()->numberType, getBuiltins()->stringType, getBuiltins()->anyType}; std::vector result; for (TypeId ty : &utv) @@ -109,38 +109,38 @@ TEST_CASE_FIXTURE(Fixture, "iterating_over_nested_UnionTypes") { Type subunion{UnionType{}}; UnionType* innerUtv = getMutable(&subunion); - innerUtv->options = {builtinTypes->numberType, builtinTypes->stringType}; + innerUtv->options = {getBuiltins()->numberType, getBuiltins()->stringType}; UnionType utv; - utv.options = {builtinTypes->anyType, &subunion}; + utv.options = {getBuiltins()->anyType, &subunion}; std::vector result; for (TypeId ty : &utv) result.push_back(ty); REQUIRE_EQ(result.size(), 3); - CHECK_EQ(result[0], builtinTypes->anyType); - CHECK_EQ(result[2], builtinTypes->stringType); - CHECK_EQ(result[1], builtinTypes->numberType); + CHECK_EQ(result[0], getBuiltins()->anyType); + CHECK_EQ(result[2], getBuiltins()->stringType); + CHECK_EQ(result[1], getBuiltins()->numberType); } TEST_CASE_FIXTURE(Fixture, "iterating_over_nested_UnionTypes_postfix_operator_plus_plus") { Type subunion{UnionType{}}; UnionType* innerUtv = getMutable(&subunion); - innerUtv->options = {builtinTypes->numberType, builtinTypes->stringType}; + innerUtv->options = {getBuiltins()->numberType, getBuiltins()->stringType}; UnionType utv; - utv.options = {builtinTypes->anyType, &subunion}; + utv.options = {getBuiltins()->anyType, &subunion}; std::vector result; for (auto it = begin(&utv); it != end(&utv); it++) result.push_back(*it); REQUIRE_EQ(result.size(), 3); - CHECK_EQ(result[0], builtinTypes->anyType); - CHECK_EQ(result[2], builtinTypes->stringType); - CHECK_EQ(result[1], builtinTypes->numberType); + CHECK_EQ(result[0], getBuiltins()->anyType); + CHECK_EQ(result[2], getBuiltins()->stringType); + CHECK_EQ(result[1], getBuiltins()->numberType); } TEST_CASE_FIXTURE(Fixture, "iterator_detects_cyclic_UnionTypes_and_skips_over_them") @@ -150,8 +150,8 @@ TEST_CASE_FIXTURE(Fixture, "iterator_detects_cyclic_UnionTypes_and_skips_over_th Type btv{UnionType{}}; UnionType* utv2 = getMutable(&btv); - utv2->options.push_back(builtinTypes->numberType); - utv2->options.push_back(builtinTypes->stringType); + utv2->options.push_back(getBuiltins()->numberType); + utv2->options.push_back(getBuiltins()->stringType); utv2->options.push_back(&atv); utv1->options.push_back(&btv); @@ -161,14 +161,14 @@ TEST_CASE_FIXTURE(Fixture, "iterator_detects_cyclic_UnionTypes_and_skips_over_th result.push_back(ty); REQUIRE_EQ(result.size(), 2); - CHECK_EQ(result[0], builtinTypes->numberType); - CHECK_EQ(result[1], builtinTypes->stringType); + CHECK_EQ(result[0], getBuiltins()->numberType); + CHECK_EQ(result[1], getBuiltins()->stringType); } TEST_CASE_FIXTURE(Fixture, "iterator_descends_on_nested_in_first_operator*") { - Type tv1{UnionType{{builtinTypes->stringType, builtinTypes->numberType}}}; - Type tv2{UnionType{{&tv1, builtinTypes->booleanType}}}; + Type tv1{UnionType{{getBuiltins()->stringType, getBuiltins()->numberType}}}; + Type tv2{UnionType{{&tv1, getBuiltins()->booleanType}}}; auto utv = get(&tv2); std::vector result; @@ -176,19 +176,19 @@ TEST_CASE_FIXTURE(Fixture, "iterator_descends_on_nested_in_first_operator*") result.push_back(ty); REQUIRE_EQ(result.size(), 3); - CHECK_EQ(result[0], builtinTypes->stringType); - CHECK_EQ(result[1], builtinTypes->numberType); - CHECK_EQ(result[2], builtinTypes->booleanType); + CHECK_EQ(result[0], getBuiltins()->stringType); + CHECK_EQ(result[1], getBuiltins()->numberType); + CHECK_EQ(result[2], getBuiltins()->booleanType); } TEST_CASE_FIXTURE(Fixture, "UnionTypeIterator_with_vector_iter_ctor") { - Type tv1{UnionType{{builtinTypes->stringType, builtinTypes->numberType}}}; - Type tv2{UnionType{{&tv1, builtinTypes->booleanType}}}; + Type tv1{UnionType{{getBuiltins()->stringType, getBuiltins()->numberType}}}; + Type tv2{UnionType{{&tv1, getBuiltins()->booleanType}}}; auto utv = get(&tv2); std::vector actual(begin(utv), end(utv)); - std::vector expected{builtinTypes->stringType, builtinTypes->numberType, builtinTypes->booleanType}; + std::vector expected{getBuiltins()->stringType, getBuiltins()->numberType, getBuiltins()->booleanType}; CHECK_EQ(actual, expected); } @@ -219,7 +219,7 @@ TEST_CASE_FIXTURE(Fixture, "UnionTypeIterator_with_only_cyclic_union") */ TEST_CASE_FIXTURE(Fixture, "substitution_skip_failure") { - Type ftv11{FreeType{TypeLevel{}, builtinTypes->neverType, builtinTypes->unknownType}}; + Type ftv11{FreeType{TypeLevel{}, getBuiltins()->neverType, getBuiltins()->unknownType}}; TypePackVar tp24{TypePack{{&ftv11}}}; TypePackVar tp17{TypePack{}}; @@ -294,11 +294,11 @@ TEST_CASE_FIXTURE(Fixture, "substitution_skip_failure") ModulePtr currentModule = std::make_shared(); Anyification anyification( ¤tModule->internalTypes, - frontend.globals.globalScope, - builtinTypes, - &frontend.iceHandler, - builtinTypes->anyType, - builtinTypes->anyTypePack + getFrontend().globals.globalScope, + getBuiltins(), + &getFrontend().iceHandler, + getBuiltins()->anyType, + getBuiltins()->anyTypePack ); std::optional any = anyification.substitute(root); diff --git a/tests/VisitType.test.cpp b/tests/VisitType.test.cpp index 86063ae8..4b604f50 100644 --- a/tests/VisitType.test.cpp +++ b/tests/VisitType.test.cpp @@ -55,7 +55,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_throw_when_limit_is_high_enough") TEST_CASE_FIXTURE(Fixture, "some_free_types_do_not_have_bounds") { - Type t{FreeType{TypeLevel{}, builtinTypes->neverType, builtinTypes->unknownType}}; + Type t{FreeType{TypeLevel{}, getBuiltins()->neverType, getBuiltins()->unknownType}}; (void)toString(&t); } @@ -64,8 +64,8 @@ TEST_CASE_FIXTURE(Fixture, "some_free_types_have_bounds") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; - Scope scope{builtinTypes->anyTypePack}; - Type t{FreeType{&scope, builtinTypes->neverType, builtinTypes->numberType}}; + Scope scope{getBuiltins()->anyTypePack}; + Type t{FreeType{&scope, getBuiltins()->neverType, getBuiltins()->numberType}}; CHECK("('a <: number)" == toString(&t)); }