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<T, t1, Y>`
* 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 <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
This commit is contained in:
Andy Friesen 2025-06-13 09:36:35 -07:00 committed by GitHub
parent 68cdcc4a3a
commit 713ee2ff8b
Signed by: DevComp
GPG key ID: B5690EEEBB952194
120 changed files with 3114 additions and 2075 deletions

View file

@ -439,9 +439,6 @@ public:
TypeId simplifyIntersection(NotNull<Scope> scope, Location location, std::set<TypeId> parts); TypeId simplifyIntersection(NotNull<Scope> scope, Location location, std::set<TypeId> parts);
TypeId simplifyUnion(NotNull<Scope> scope, Location location, TypeId left, TypeId right); TypeId simplifyUnion(NotNull<Scope> scope, Location location, TypeId left, TypeId right);
TypeId errorRecoveryType() const;
TypePackId errorRecoveryTypePack() const;
TypePackId anyifyModuleReturnTypePackGenerics(TypePackId tp); TypePackId anyifyModuleReturnTypePackGenerics(TypePackId tp);
void throwTimeLimitError() const; void throwTimeLimitError() const;

View file

@ -52,6 +52,10 @@ struct ExpectedTypeVisitor : public AstVisitor
// parameters. // parameters.
bool visit(AstExprCall* expr) override; 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: // If we have an expression of type:
// //
// return X :: Y // return X :: Y

View file

@ -970,8 +970,6 @@ struct BuiltinTypes
TypeId errorRecoveryType(TypeId guess) const; TypeId errorRecoveryType(TypeId guess) const;
TypePackId errorRecoveryTypePack(TypePackId guess) const; TypePackId errorRecoveryTypePack(TypePackId guess) const;
TypeId errorRecoveryType() const;
TypePackId errorRecoveryTypePack() const;
friend TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes); friend TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes);
friend struct GlobalTypes; friend struct GlobalTypes;

View file

@ -194,7 +194,9 @@ private:
void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result); void explainError(TypeId subTy, TypeId superTy, Location location, const SubtypingResult& result);
void explainError(TypePackId subTy, TypePackId 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(TypeId subTy, TypeId superTy, Location location);
bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location); bool testIsSubtype(TypePackId subTy, TypePackId superTy, Location location);

View file

@ -40,11 +40,11 @@ struct InConditionalContext
TypeContext* typeContext; TypeContext* typeContext;
TypeContext oldValue; TypeContext oldValue;
explicit InConditionalContext(TypeContext* c) explicit InConditionalContext(TypeContext* c, TypeContext newValue = TypeContext::Condition)
: typeContext(c) : typeContext(c)
, oldValue(*c) , oldValue(*c)
{ {
*typeContext = TypeContext::Condition; *typeContext = newValue;
} }
~InConditionalContext() ~InConditionalContext()

View file

@ -483,7 +483,7 @@ static std::optional<DocumentationSymbol> checkOverloadedDocumentationSymbol(
const Module& module, const Module& module,
const TypeId ty, const TypeId ty,
const AstExpr* parentExpr, const AstExpr* parentExpr,
const std::optional<DocumentationSymbol> documentationSymbol std::optional<DocumentationSymbol> documentationSymbol
) )
{ {
if (!documentationSymbol) if (!documentationSymbol)

View file

@ -46,7 +46,7 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
std::vector<AstNode*> ancestry = findAncestryAtPositionForAutocomplete(*sourceModule, position); std::vector<AstNode*> ancestry = findAncestryAtPositionForAutocomplete(*sourceModule, position);
LUAU_ASSERT(!ancestry.empty()); LUAU_ASSERT(!ancestry.empty());
ScopePtr startScope = findScopeAtPosition(*module, position); 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 } // namespace Luau

View file

@ -760,7 +760,7 @@ static bool tryAddTypeCorrectSuggestion(AutocompleteEntryMap& result, ScopePtr s
if (!ty) if (!ty)
return false; 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()) if (auto it = result.find(*name); it != result.end())
it->second.typeCorrect = TypeCorrectKind::Correct; it->second.typeCorrect = TypeCorrectKind::Correct;
@ -966,7 +966,7 @@ AutocompleteEntryMap autocompleteTypeNames(
} }
if (inferredType) if (inferredType)
tryAddTypeCorrectSuggestion(result, startScope, topType, inferredType, position); tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, inferredType, position);
break; break;
} }
@ -1066,7 +1066,7 @@ AutocompleteEntryMap autocompleteTypeNames(
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node)) if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
{ {
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) 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 (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
{ {
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) 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 (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
{ {
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) 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; AutocompleteEntryMap result;
AutocompleteContext context = autocompleteExpression(module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result); AutocompleteContext context = autocompleteExpression(module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result);
return {result, ancestry, context}; return {std::move(result), ancestry, context};
} }
static std::optional<const ExternType*> getMethodContainingExternType(const ModulePtr& module, AstExpr* funcExpr) static std::optional<const ExternType*> getMethodContainingExternType(const ModulePtr& module, AstExpr* funcExpr)
@ -2025,7 +2025,7 @@ AutocompleteResult autocomplete_(
if (!key) if (!key)
autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result); autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result);
return {result, ancestry, AutocompleteContext::Property}; return {std::move(result), ancestry, AutocompleteContext::Property};
} }
break; break;
@ -2061,12 +2061,12 @@ AutocompleteResult autocomplete_(
// Also offer general expression suggestions // Also offer general expression suggestions
autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result); 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<AstStatExpr>() || parent->is<AstStatError>())) else if (isIdentifier(node) && (parent->is<AstStatExpr>() || parent->is<AstStatError>()))
return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement}; return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement};
if (std::optional<AutocompleteEntryMap> ret = autocompleteStringParams(module, ancestry, position, fileResolver, callback)) if (std::optional<AutocompleteEntryMap> ret = autocompleteStringParams(module, ancestry, position, fileResolver, std::move(callback)))
{ {
return {*ret, ancestry, AutocompleteContext::String}; 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)) else if (stringPartOfInterpString(node, position))
{ {
// We're not a simple interpolated string, we're something like `a{"b"}@1`, and we // We're not a simple interpolated string, we're something like `a{"b"}@1`, and we
// can't know what to format to // can't know what to format to
AutocompleteEntryMap map; AutocompleteEntryMap map;
return {map, ancestry, AutocompleteContext::String}; return {std::move(map), ancestry, AutocompleteContext::String};
} }
if (node->is<AstExprConstantNumber>()) if (node->is<AstExprConstantNumber>())

View file

@ -30,7 +30,7 @@
*/ */
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3) LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements) LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements)
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2) 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) 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) 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; TypeArena& arena = globals.globalTypes;
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes; NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauEagerGeneralization2 Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauEagerGeneralization4
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
globalScope = globals.globalScope.get(); globalScope = globals.globalScope.get();
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2)
@ -376,7 +376,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
TypeId genericMT = arena.addType(GenericType{globalScope, "MT"}); TypeId genericMT = arena.addType(GenericType{globalScope, "MT"});
TableType tab{TableState::Generic, globals.globalScope->level}; 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}); 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; 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 // 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 // Libraries
"math", "math",
"table", "table",
@ -598,7 +598,7 @@ std::optional<WithPredicate<TypePackId>> MagicFormat::handleOldSolver(
WithPredicate<TypePackId> withPredicate WithPredicate<TypePackId> withPredicate
) )
{ {
auto [paramPack, _predicates] = withPredicate; auto [paramPack, _predicates] = std::move(withPredicate);
TypeArena& arena = typechecker.currentModule->internalTypes; TypeArena& arena = typechecker.currentModule->internalTypes;
@ -959,7 +959,7 @@ std::optional<WithPredicate<TypePackId>> MagicGmatch::handleOldSolver(
WithPredicate<TypePackId> withPredicate WithPredicate<TypePackId> withPredicate
) )
{ {
auto [paramPack, _predicates] = withPredicate; auto [paramPack, _predicates] = std::move(withPredicate);
const auto& [params, tail] = flatten(paramPack); const auto& [params, tail] = flatten(paramPack);
if (params.size() != 2) if (params.size() != 2)
@ -983,7 +983,7 @@ std::optional<WithPredicate<TypePackId>> MagicGmatch::handleOldSolver(
typechecker.unify(params[0], typechecker.stringType, scope, expr.args.data[0]->location); typechecker.unify(params[0], typechecker.stringType, scope, expr.args.data[0]->location);
const TypePackId emptyPack = arena.addTypePack({}); 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 TypeId iteratorType = arena.addType(FunctionType{emptyPack, returnList});
return WithPredicate<TypePackId>{arena.addTypePack({iteratorType})}; return WithPredicate<TypePackId>{arena.addTypePack({iteratorType})};
} }
@ -1013,7 +1013,7 @@ bool MagicGmatch::infer(const MagicFunctionCallContext& context)
context.solver->unify(context.constraint, params[0], context.solver->builtinTypes->stringType); context.solver->unify(context.constraint, params[0], context.solver->builtinTypes->stringType);
const TypePackId emptyPack = arena->addTypePack({}); 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 TypeId iteratorType = arena->addType(FunctionType{emptyPack, returnList});
const TypePackId resTypePack = arena->addTypePack({iteratorType}); const TypePackId resTypePack = arena->addTypePack({iteratorType});
asMutable(context.result)->ty.emplace<BoundTypePack>(resTypePack); asMutable(context.result)->ty.emplace<BoundTypePack>(resTypePack);
@ -1028,7 +1028,7 @@ std::optional<WithPredicate<TypePackId>> MagicMatch::handleOldSolver(
WithPredicate<TypePackId> withPredicate WithPredicate<TypePackId> withPredicate
) )
{ {
auto [paramPack, _predicates] = withPredicate; auto [paramPack, _predicates] = std::move(withPredicate);
const auto& [params, tail] = flatten(paramPack); const auto& [params, tail] = flatten(paramPack);
if (params.size() < 2 || params.size() > 3) if (params.size() < 2 || params.size() > 3)
@ -1057,7 +1057,7 @@ std::optional<WithPredicate<TypePackId>> MagicMatch::handleOldSolver(
if (params.size() == 3 && expr.args.size > initIndex) if (params.size() == 3 && expr.args.size > initIndex)
typechecker.unify(params[2], optionalNumber, scope, expr.args.data[initIndex]->location); 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<TypePackId>{returnList}; return WithPredicate<TypePackId>{returnList};
} }
@ -1091,7 +1091,7 @@ bool MagicMatch::infer(const MagicFunctionCallContext& context)
if (params.size() == 3 && context.callSite->args.size > initIndex) if (params.size() == 3 && context.callSite->args.size > initIndex)
context.solver->unify(context.constraint, params[2], optionalNumber); 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<BoundTypePack>(returnList); asMutable(context.result)->ty.emplace<BoundTypePack>(returnList);
return true; return true;
@ -1104,7 +1104,7 @@ std::optional<WithPredicate<TypePackId>> MagicFind::handleOldSolver(
WithPredicate<TypePackId> withPredicate WithPredicate<TypePackId> withPredicate
) )
{ {
auto [paramPack, _predicates] = withPredicate; auto [paramPack, _predicates] = std::move(withPredicate);
const auto& [params, tail] = flatten(paramPack); const auto& [params, tail] = flatten(paramPack);
if (params.size() < 2 || params.size() > 4) if (params.size() < 2 || params.size() > 4)
@ -1151,7 +1151,7 @@ std::optional<WithPredicate<TypePackId>> MagicFind::handleOldSolver(
returnTypes.insert(returnTypes.begin(), {optionalNumber, optionalNumber}); returnTypes.insert(returnTypes.begin(), {optionalNumber, optionalNumber});
const TypePackId returnList = arena.addTypePack(returnTypes); const TypePackId returnList = arena.addTypePack(std::move(returnTypes));
return WithPredicate<TypePackId>{returnList}; return WithPredicate<TypePackId>{returnList};
} }
@ -1204,7 +1204,7 @@ bool MagicFind::infer(const MagicFunctionCallContext& context)
returnTypes.insert(returnTypes.begin(), {optionalNumber, optionalNumber}); 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<BoundTypePack>(returnList); asMutable(context.result)->ty.emplace<BoundTypePack>(returnList);
return true; return true;
} }
@ -1233,7 +1233,7 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack}; FunctionType formatFTV{arena->addTypePack(TypePack{{stringType}, variadicTailPack}), oneStringPack};
formatFTV.isCheckedFunction = true; formatFTV.isCheckedFunction = true;
const TypeId formatFn = arena->addType(formatFTV); const TypeId formatFn = arena->addType(std::move(formatFTV));
attachMagicFunction(formatFn, std::make_shared<MagicFormat>()); attachMagicFunction(formatFn, std::make_shared<MagicFormat>());
@ -1254,7 +1254,7 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
arena->addTypePack({stringType, stringType, optionalNumber}), arena->addTypePack(TypePackVar{VariadicTypePack{stringType}}) arena->addTypePack({stringType, stringType, optionalNumber}), arena->addTypePack(TypePackVar{VariadicTypePack{stringType}})
}; };
matchFuncTy.isCheckedFunction = true; matchFuncTy.isCheckedFunction = true;
const TypeId matchFunc = arena->addType(matchFuncTy); const TypeId matchFunc = arena->addType(std::move(matchFuncTy));
attachMagicFunction(matchFunc, std::make_shared<MagicMatch>()); attachMagicFunction(matchFunc, std::make_shared<MagicMatch>());
FunctionType findFuncTy{ FunctionType findFuncTy{
@ -1262,7 +1262,7 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList}) arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})
}; };
findFuncTy.isCheckedFunction = true; findFuncTy.isCheckedFunction = true;
const TypeId findFunc = arena->addType(findFuncTy); const TypeId findFunc = arena->addType(std::move(findFuncTy));
attachMagicFunction(findFunc, std::make_shared<MagicFind>()); attachMagicFunction(findFunc, std::make_shared<MagicFind>());
// string.byte : string -> number? -> number? -> ...number // string.byte : string -> number? -> number? -> ...number
@ -1281,8 +1281,8 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
stringDotUnpack.isCheckedFunction = true; stringDotUnpack.isCheckedFunction = true;
TableType::Props stringLib = { TableType::Props stringLib = {
{"byte", {arena->addType(stringDotByte)}}, {"byte", {arena->addType(std::move(stringDotByte))}},
{"char", {arena->addType(stringDotChar)}}, {"char", {arena->addType(std::move(stringDotChar))}},
{"find", {findFunc}}, {"find", {findFunc}},
{"format", {formatFn}}, // FIXME {"format", {formatFn}}, // FIXME
{"gmatch", {gmatchFunc}}, {"gmatch", {gmatchFunc}},
@ -1311,7 +1311,7 @@ TypeId makeStringMetatable(NotNull<BuiltinTypes> builtinTypes)
oneStringPack, oneStringPack,
})}}, })}},
{"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}}, {"packsize", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType}, /* checked */ true)}},
{"unpack", {arena->addType(stringDotUnpack)}}, {"unpack", {arena->addType(std::move(stringDotUnpack))}},
}; };
assignPropDocumentationSymbols(stringLib, "@luau/global/string"); assignPropDocumentationSymbols(stringLib, "@luau/global/string");
@ -1331,7 +1331,7 @@ std::optional<WithPredicate<TypePackId>> MagicSelect::handleOldSolver(
WithPredicate<TypePackId> withPredicate WithPredicate<TypePackId> withPredicate
) )
{ {
auto [paramPack, _predicates] = withPredicate; auto [paramPack, _predicates] = std::move(withPredicate);
(void)scope; (void)scope;
@ -1421,7 +1421,7 @@ std::optional<WithPredicate<TypePackId>> MagicSetMetatable::handleOldSolver(
WithPredicate<TypePackId> withPredicate WithPredicate<TypePackId> withPredicate
) )
{ {
auto [paramPack, _predicates] = withPredicate; auto [paramPack, _predicates] = std::move(withPredicate);
if (size(paramPack) < 2 && finite(paramPack)) if (size(paramPack) < 2 && finite(paramPack))
return std::nullopt; return std::nullopt;
@ -1455,7 +1455,7 @@ std::optional<WithPredicate<TypePackId>> MagicSetMetatable::handleOldSolver(
mtv.syntheticName = tableName; mtv.syntheticName = tableName;
} }
TypeId mtTy = arena.addType(mtv); TypeId mtTy = arena.addType(std::move(mtv));
if (expr.args.size < 1) if (expr.args.size < 1)
return std::nullopt; return std::nullopt;
@ -1508,7 +1508,7 @@ std::optional<WithPredicate<TypePackId>> MagicAssert::handleOldSolver(
WithPredicate<TypePackId> withPredicate WithPredicate<TypePackId> withPredicate
) )
{ {
auto [paramPack, predicates] = withPredicate; auto [paramPack, predicates] = std::move(withPredicate);
TypeArena& arena = typechecker.currentModule->internalTypes; TypeArena& arena = typechecker.currentModule->internalTypes;
@ -1547,7 +1547,7 @@ std::optional<WithPredicate<TypePackId>> MagicPack::handleOldSolver(
WithPredicate<TypePackId> withPredicate WithPredicate<TypePackId> withPredicate
) )
{ {
auto [paramPack, _predicates] = withPredicate; auto [paramPack, _predicates] = std::move(withPredicate);
TypeArena& arena = typechecker.currentModule->internalTypes; TypeArena& arena = typechecker.currentModule->internalTypes;
@ -1632,7 +1632,7 @@ std::optional<WithPredicate<TypePackId>> MagicClone::handleOldSolver(
{ {
LUAU_ASSERT(FFlag::LuauTableCloneClonesType3); LUAU_ASSERT(FFlag::LuauTableCloneClonesType3);
auto [paramPack, _predicates] = withPredicate; auto [paramPack, _predicates] = std::move(withPredicate);
TypeArena& arena = typechecker.currentModule->internalTypes; TypeArena& arena = typechecker.currentModule->internalTypes;

View file

@ -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. // 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_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
LUAU_FASTFLAGVARIABLE(LuauSolverAgnosticClone)
namespace Luau namespace Luau
{ {
@ -73,12 +75,12 @@ public:
if (hasExceededIterationLimit()) if (hasExceededIterationLimit())
{ {
TypeId error = builtinTypes->errorRecoveryType(); TypeId error = builtinTypes->errorType;
(*types)[ty] = error; (*types)[ty] = error;
return error; return error;
} }
return find(ty).value_or(builtinTypes->errorRecoveryType()); return find(ty).value_or(builtinTypes->errorType);
} }
TypePackId clone(TypePackId tp) TypePackId clone(TypePackId tp)
@ -88,12 +90,12 @@ public:
if (hasExceededIterationLimit()) if (hasExceededIterationLimit())
{ {
TypePackId error = builtinTypes->errorRecoveryTypePack(); TypePackId error = builtinTypes->errorTypePack;
(*packs)[tp] = error; (*packs)[tp] = error;
return error; return error;
} }
return find(tp).value_or(builtinTypes->errorRecoveryTypePack()); return find(tp).value_or(builtinTypes->errorTypePack);
} }
private: private:
@ -208,7 +210,7 @@ public:
private: private:
Property shallowClone(const Property& p) Property shallowClone(const Property& p)
{ {
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone)
{ {
std::optional<TypeId> cloneReadTy; std::optional<TypeId> cloneReadTy;
if (auto ty = p.readTy) if (auto ty = p.readTy)

View file

@ -3,7 +3,7 @@
#include "Luau/Constraint.h" #include "Luau/Constraint.h"
#include "Luau/VisitType.h" #include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
namespace Luau namespace Luau
{ {
@ -43,6 +43,17 @@ struct ReferenceCountInitializer : TypeOnceVisitor
return false; 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 bool visit(TypeId ty, const ExternType&) override
{ {
// ExternTypes never contain free types. // ExternTypes never contain free types.
@ -51,12 +62,18 @@ struct ReferenceCountInitializer : TypeOnceVisitor
bool visit(TypeId, const TypeFunctionInstanceType&) override bool visit(TypeId, const TypeFunctionInstanceType&) override
{ {
return FFlag::LuauEagerGeneralization3 && traverseIntoTypeFunctions; return FFlag::LuauEagerGeneralization4 && traverseIntoTypeFunctions;
} }
}; };
bool isReferenceCountedType(const TypeId typ) bool isReferenceCountedType(const TypeId typ)
{ {
if (FFlag::LuauEagerGeneralization4)
{
if (auto tt = get<TableType>(typ))
return tt->state == TableState::Free || tt->state == TableState::Unsealed;
}
// n.b. this should match whatever `ReferenceCountInitializer` includes. // n.b. this should match whatever `ReferenceCountInitializer` includes.
return get<FreeType>(typ) || get<BlockedType>(typ) || get<PendingExpansionType>(typ); return get<FreeType>(typ) || get<BlockedType>(typ) || get<PendingExpansionType>(typ);
} }
@ -104,7 +121,7 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
{ {
rci.traverse(fchc->argsPack); rci.traverse(fchc->argsPack);
} }
else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::LuauEagerGeneralization3) else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::LuauEagerGeneralization4)
{ {
rci.traverseIntoTypeFunctions = false; rci.traverseIntoTypeFunctions = false;
rci.traverse(fcc->fn); rci.traverse(fcc->fn);
@ -118,12 +135,12 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
else if (auto hpc = get<HasPropConstraint>(*this)) else if (auto hpc = get<HasPropConstraint>(*this))
{ {
rci.traverse(hpc->resultType); rci.traverse(hpc->resultType);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
rci.traverse(hpc->subjectType); rci.traverse(hpc->subjectType);
} }
else if (auto hic = get<HasIndexerConstraint>(*this)) else if (auto hic = get<HasIndexerConstraint>(*this))
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
rci.traverse(hic->subjectType); rci.traverse(hic->subjectType);
rci.traverse(hic->resultType); rci.traverse(hic->resultType);
// `HasIndexerConstraint` should not mutate `indexType`. // `HasIndexerConstraint` should not mutate `indexType`.

View file

@ -34,8 +34,8 @@
LUAU_FASTINT(LuauCheckRecursionLimit) LUAU_FASTINT(LuauCheckRecursionLimit)
LUAU_FASTFLAG(DebugLuauLogSolverToJson) LUAU_FASTFLAG(DebugLuauLogSolverToJson)
LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation) LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties) LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
@ -43,12 +43,14 @@ LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation) LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation)
LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2) LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables) LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500) LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunctionAliases) LUAU_FASTFLAGVARIABLE(LuauUserTypeFunctionAliases)
LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment) LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment)
LUAU_FASTFLAGVARIABLE(LuauFollowTypeAlias)
LUAU_FASTFLAGVARIABLE(LuauFollowExistingTypeFunction)
namespace Luau namespace Luau
{ {
@ -256,7 +258,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
rootScope->location = block->location; rootScope->location = block->location;
module->astScopes[block] = NotNull{scope.get()}; module->astScopes[block] = NotNull{scope.get()};
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.emplace_back(); 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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); 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(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); 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 // We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block); prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
// Pre // Pre
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.emplace_back(); DEPRECATED_interiorTypes.emplace_back();
visitBlockWithoutChildScope(resumeScope, block); visitBlockWithoutChildScope(resumeScope, block);
// Post // Post
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
interiorFreeTypes.pop_back(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); DEPRECATED_interiorTypes.pop_back();
@ -385,12 +387,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity) TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity); auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
interiorFreeTypes.back().types.push_back(ft); interiorFreeTypes.back().types.push_back(ft);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
freeTypes.insert(ft); freeTypes.insert(ft);
return ft; return ft;
@ -407,7 +409,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po
{ {
FreeTypePack f{scope.get(), polarity}; FreeTypePack f{scope.get(), polarity};
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)}); TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
interiorFreeTypes.back().typePacks.push_back(result); interiorFreeTypes.back().typePacks.push_back(result);
return 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; const TypeFunction& func = kind == RefinementsOpKind::Intersect ? builtinTypeFunctions().intersectFunc : builtinTypeFunctions().refineFunc;
LUAU_ASSERT(!func.name.empty()); LUAU_ASSERT(!func.name.empty());
args.insert(args.end(), discriminants.begin(), discriminants.end()); 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(); discriminants.clear();
return resultType; return resultType;
}; };
@ -945,7 +947,8 @@ void ConstraintGenerator::checkAliases(const ScopePtr& scope, AstStatBlock* bloc
auto addToEnvironment = auto addToEnvironment =
[this, &globalNameCollector](UserDefinedFunctionData& userFuncData, ScopePtr scope, const Name& name, TypeFun tf, size_t level) [this, &globalNameCollector](UserDefinedFunctionData& userFuncData, ScopePtr scope, const Name& name, TypeFun tf, size_t level)
{ {
if (auto ty = get<TypeFunctionInstanceType>(tf.type); ty && ty->userFuncData.definition) if (auto ty = get<TypeFunctionInstanceType>(FFlag::LuauFollowTypeAlias ? follow(tf.type) : tf.type);
ty && ty->userFuncData.definition)
{ {
if (userFuncData.environmentFunction.find(name)) if (userFuncData.environmentFunction.find(name))
return; 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}; scope->bindings[ty->userFuncData.definition->name] = Binding{existing->typeId, ty->userFuncData.definition->location};
} }
} }
else if (FFlag::LuauUserTypeFunctionAliases && !get<TypeFunctionInstanceType>(tf.type)) else if (FFlag::LuauUserTypeFunctionAliases &&
!get<TypeFunctionInstanceType>(FFlag::LuauFollowTypeAlias ? follow(tf.type) : tf.type))
{ {
if (userFuncData.environmentAlias.find(name)) if (userFuncData.environmentAlias.find(name))
return; return;
@ -1417,7 +1421,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location); FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
sig.bodyScope->bindings[function->name] = Binding{sig.signature, 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) if (sigFullyDefined)
emplaceType<BoundType>(asMutable(functionType), sig.signature); emplaceType<BoundType>(asMutable(functionType), sig.signature);
@ -1477,7 +1481,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
Checkpoint start = checkpoint(this); Checkpoint start = checkpoint(this);
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location); 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); 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<AstExprError>()) else if (AstExprError* err = function->name->as<AstExprError>())
{ {
generalizedType = builtinTypes->errorRecoveryType(); generalizedType = builtinTypes->errorType;
} }
if (generalizedType == nullptr) 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 // Place this function as a child of the non-type function scope
scope->children.push_back(NotNull{sig.signatureScope.get()}); scope->children.push_back(NotNull{sig.signatureScope.get()});
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{}); DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
@ -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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); 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()); sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
getMutable<BlockedType>(generalizedTy)->setOwner(gc); getMutable<BlockedType>(generalizedTy)->setOwner(gc);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
interiorFreeTypes.pop_back(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); DEPRECATED_interiorTypes.pop_back();
@ -1850,8 +1854,18 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
if (!existingFunctionTy) if (!existingFunctionTy)
ice->ice("checkAliases did not populate type function name", function->nameLocation); ice->ice("checkAliases did not populate type function name", function->nameLocation);
if (FFlag::LuauFollowExistingTypeFunction)
{
TypeId unpackedTy = follow(*existingFunctionTy);
if (auto bt = get<BlockedType>(unpackedTy); bt && nullptr == bt->getOwner())
emplaceType<BoundType>(asMutable(unpackedTy), generalizedTy);
}
else
{
if (auto bt = get<BlockedType>(*existingFunctionTy); bt && nullptr == bt->getOwner()) if (auto bt = get<BlockedType>(*existingFunctionTy); bt && nullptr == bt->getOwner())
emplaceType<BoundType>(asMutable(*existingFunctionTy), generalizedTy); emplaceType<BoundType>(asMutable(*existingFunctionTy), generalizedTy);
}
return ControlFlow::None; return ControlFlow::None;
} }
@ -1896,7 +1910,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
if (!lookupType) if (!lookupType)
{ {
reportError(declaredExternType->location, UnknownSymbol{superName, UnknownSymbol::Type}); reportError(declaredExternType->location, UnknownSymbol{std::move(superName), UnknownSymbol::Type});
return ControlFlow::None; return ControlFlow::None;
} }
@ -1918,7 +1932,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
Name className(declaredExternType->name.value); 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<ExternType>(externTy); ExternType* etv = getMutable<ExternType>(externTy);
TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()}); TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()});
@ -2120,7 +2134,7 @@ InferencePack ConstraintGenerator::checkPack(
if (recursionCount >= FInt::LuauCheckRecursionLimit) if (recursionCount >= FInt::LuauCheckRecursionLimit)
{ {
reportCodeTooComplex(expr->location); reportCodeTooComplex(expr->location);
return InferencePack{builtinTypes->errorRecoveryTypePack()}; return InferencePack{builtinTypes->errorTypePack};
} }
InferencePack result; InferencePack result;
@ -2132,7 +2146,7 @@ InferencePack ConstraintGenerator::checkPack(
if (scope->varargPack) if (scope->varargPack)
result = InferencePack{*scope->varargPack}; result = InferencePack{*scope->varargPack};
else else
result = InferencePack{builtinTypes->errorRecoveryTypePack()}; result = InferencePack{builtinTypes->errorTypePack};
} }
else else
{ {
@ -2189,7 +2203,14 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
Checkpoint funcBeginCheckpoint = checkpoint(this); 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); Checkpoint funcEndCheckpoint = checkpoint(this);
@ -2273,7 +2294,7 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
mt = arena->addType(BlockedType{}); mt = arena->addType(BlockedType{});
unpackedTypes.emplace_back(mt); 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<BlockedType>(mt)->setOwner(c); getMutable<BlockedType>(mt)->setOwner(c);
if (auto b = getMutable<BlockedType>(target); b && b->getOwner() == nullptr) if (auto b = getMutable<BlockedType>(target); b && b->getOwner() == nullptr)
b->setOwner(c); b->setOwner(c);
@ -2402,7 +2423,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std::
if (recursionCount >= FInt::LuauCheckRecursionLimit) if (recursionCount >= FInt::LuauCheckRecursionLimit)
{ {
reportCodeTooComplex(expr->location); reportCodeTooComplex(expr->location);
return Inference{builtinTypes->errorRecoveryType()}; return Inference{builtinTypes->errorType};
} }
// We may recurse a given expression more than once when checking compound // 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) for (AstExpr* subExpr : err->expressions)
check(scope, subExpr); check(scope, subExpr);
result = Inference{builtinTypes->errorRecoveryType()}; result = Inference{builtinTypes->errorType};
} }
else else
{ {
@ -2491,7 +2512,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
return Inference{builtinTypes->stringType}; return Inference{builtinTypes->stringType};
TypeId freeTy = nullptr; TypeId freeTy = nullptr;
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
freeTy = freshType(scope, Polarity::Positive); freeTy = freshType(scope, Polarity::Positive);
FreeType* ft = getMutable<FreeType>(freeTy); FreeType* ft = getMutable<FreeType>(freeTy);
@ -2532,7 +2553,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
return Inference{builtinTypes->booleanType}; return Inference{builtinTypes->booleanType};
TypeId freeTy = nullptr; TypeId freeTy = nullptr;
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
freeTy = freshType(scope, Polarity::Positive); freeTy = freshType(scope, Polarity::Positive);
FreeType* ft = getMutable<FreeType>(freeTy); FreeType* ft = getMutable<FreeType>(freeTy);
@ -2591,7 +2612,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprGlobal* globa
return Inference{*ty, refinementArena.proposition(key, builtinTypes->truthyType)}; return Inference{*ty, refinementArena.proposition(key, builtinTypes->truthyType)};
} }
else else
return Inference{builtinTypes->errorRecoveryType()}; return Inference{builtinTypes->errorType};
} }
Inference ConstraintGenerator::checkIndexName( 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<TypeId> expectedType, bool generalize) Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* func, std::optional<TypeId> expectedType, bool generalize)
{ {
std::optional<InConditionalContext> inContext;
if (FFlag::LuauEagerGeneralization4)
inContext.emplace(&typeContext, TypeContext::Default);
Checkpoint startCheckpoint = checkpoint(this); Checkpoint startCheckpoint = checkpoint(this);
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType); FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{}); DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
@ -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->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); 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) Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprUnary* unary)
{ {
std::optional<InConditionalContext> inContext;
if (FFlag::LuauEagerGeneralization4 && unary->op != AstExprUnary::Op::Not)
inContext.emplace(&typeContext, TypeContext::Default);
auto [operandType, refinement] = check(scope, unary->expr); auto [operandType, refinement] = check(scope, unary->expr);
switch (unary->op) switch (unary->op)
@ -2916,6 +2945,10 @@ Inference ConstraintGenerator::checkAstExprBinary(
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType) Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType)
{ {
std::optional<InConditionalContext> inContext;
if (FFlag::LuauEagerGeneralization4)
inContext.emplace(&typeContext, TypeContext::Default);
RefinementId refinement = [&]() RefinementId refinement = [&]()
{ {
InConditionalContext flipper{&typeContext}; InConditionalContext flipper{&typeContext};
@ -2942,6 +2975,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTypeAssertion
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprInterpString* interpString) Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprInterpString* interpString)
{ {
std::optional<InConditionalContext> inContext;
if (FFlag::LuauEagerGeneralization4)
inContext.emplace(&typeContext, TypeContext::Default);
for (AstExpr* expr : interpString->expressions) for (AstExpr* expr : interpString->expressions)
check(scope, expr); check(scope, expr);
@ -2956,6 +2993,13 @@ std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
std::optional<TypeId> expectedType std::optional<TypeId> expectedType
) )
{ {
std::optional<InConditionalContext> 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) if (op == AstExprBinary::And)
{ {
std::optional<TypeId> relaxedExpectedLhs; std::optional<TypeId> relaxedExpectedLhs;
@ -3200,6 +3244,10 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprIndexExpr* e
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType) Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType)
{ {
std::optional<InConditionalContext> inContext;
if (FFlag::LuauEagerGeneralization4)
inContext.emplace(&typeContext, TypeContext::Default);
TypeId ty = arena->addType(TableType{}); TypeId ty = arena->addType(TableType{});
TableType* ttv = getMutable<TableType>(ty); TableType* ttv = getMutable<TableType>(ty);
LUAU_ASSERT(ttv); LUAU_ASSERT(ttv);
@ -3213,7 +3261,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit)) expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
largeTableDepth++; largeTableDepth++;
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
interiorFreeTypes.back().types.push_back(ty); interiorFreeTypes.back().types.push_back(ty);
else else
DEPRECATED_interiorTypes.back().push_back(ty); 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( addConstraint(
scope, scope,
@ -3474,7 +3522,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
LUAU_ASSERT(nullptr != varargPack); LUAU_ASSERT(nullptr != varargPack);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
// Some of the types in argTypes will eventually be generics, and some // Some of the types in argTypes will eventually be generics, and some
// will not. The ones that are not generic will be pruned when // 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. // 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.generics = std::move(genericTypes);
actualFunction.genericPacks = std::move(genericTypePacks); actualFunction.genericPacks = std::move(genericTypePacks);
actualFunction.argNames = std::move(argNames); actualFunction.argNames = std::move(argNames);
@ -3539,13 +3587,13 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
if (expectedType && get<FreeType>(*expectedType)) if (expectedType && get<FreeType>(*expectedType))
bindFreeType(*expectedType, actualFunctionType); bindFreeType(*expectedType, actualFunctionType);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
scopeToFunction[signatureScope.get()] = actualFunctionType; scopeToFunction[signatureScope.get()] = actualFunctionType;
return { return {
/* signature */ actualFunctionType, /* signature */ actualFunctionType,
/* signatureScope */ signatureScope, /* signatureScope */ std::move(signatureScope),
/* bodyScope */ bodyScope, /* bodyScope */ std::move(bodyScope),
}; };
} }
@ -3576,8 +3624,8 @@ TypeId ConstraintGenerator::resolveReferenceType(
if (ref->parameters.size != 1 || !ref->parameters.data[0].type) if (ref->parameters.size != 1 || !ref->parameters.data[0].type)
{ {
reportError(ty->location, GenericError{"_luau_print requires one generic parameter"}); reportError(ty->location, GenericError{"_luau_print requires one generic parameter"});
module->astResolvedTypes[ty] = builtinTypes->errorRecoveryType(); module->astResolvedTypes[ty] = builtinTypes->errorType;
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
} }
else else
return resolveType_(scope, ref->parameters.data[0].type, inTypeArguments); 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. // 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 // The dispatching of the above constraint will queue up additional constraints for nested
@ -3644,7 +3692,7 @@ TypeId ConstraintGenerator::resolveReferenceType(
} }
else else
{ {
result = builtinTypes->errorRecoveryType(); result = builtinTypes->errorType;
if (replaceErrorWithFresh) if (replaceErrorWithFresh)
result = freshType(scope, Polarity::Mixed); 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)); 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<AstTypeIntersection>()) else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
@ -3855,7 +3903,7 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
parts.push_back(resolveType_(scope, part, inTypeArguments)); 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<AstTypeGroup>()) else if (auto typeGroupAnnotation = ty->as<AstTypeGroup>())
@ -3875,14 +3923,14 @@ TypeId ConstraintGenerator::resolveType_(const ScopePtr& scope, AstType* ty, boo
} }
else if (ty->is<AstTypeError>()) else if (ty->is<AstTypeError>())
{ {
result = builtinTypes->errorRecoveryType(); result = builtinTypes->errorType;
if (replaceErrorWithFresh) if (replaceErrorWithFresh)
result = freshType(scope); result = freshType(scope);
} }
else else
{ {
LUAU_ASSERT(0); LUAU_ASSERT(0);
result = builtinTypes->errorRecoveryType(); result = builtinTypes->errorType;
} }
module->astResolvedTypes[ty] = result; module->astResolvedTypes[ty] = result;
@ -3917,13 +3965,13 @@ TypePackId ConstraintGenerator::resolveTypePack_(const ScopePtr& scope, AstTypeP
else else
{ {
reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type}); reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type});
result = builtinTypes->errorRecoveryTypePack(); result = builtinTypes->errorTypePack;
} }
} }
else else
{ {
LUAU_ASSERT(0); LUAU_ASSERT(0);
result = builtinTypes->errorRecoveryTypePack(); result = builtinTypes->errorTypePack;
} }
module->astResolvedTypePacks[tp] = result; module->astResolvedTypePacks[tp] = result;
@ -4391,7 +4439,7 @@ TypeId ConstraintGenerator::createTypeFunctionInstance(
Location location 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}); addConstraint(scope, location, ReduceConstraint{result});
return result; return result;
} }

View file

@ -32,13 +32,11 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings) LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500) LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauDeprecatedAttribute)
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2) LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2)
LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult) LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult)
LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck) LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint) LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint)
@ -187,7 +185,7 @@ std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
if (!defaultTy) if (!defaultTy)
break; 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; atf.typeArguments[fn.typeParams[i].ty] = instantiatedDefault;
saturatedTypeArguments.push_back(instantiatedDefault); saturatedTypeArguments.push_back(instantiatedDefault);
} }
@ -205,7 +203,7 @@ std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
if (!defaultTp) if (!defaultTp)
break; 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; atf.typePackArguments[fn.typePackParams[i].tp] = instantiatedDefault;
saturatedPackArguments.push_back(instantiatedDefault); saturatedPackArguments.push_back(instantiatedDefault);
} }
@ -223,12 +221,12 @@ std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
// even if they're missing, so we use the error type as a filler. // even if they're missing, so we use the error type as a filler.
for (size_t i = saturatedTypeArguments.size(); i < typesRequired; ++i) 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) for (size_t i = saturatedPackArguments.size(); i < packsRequired; ++i)
{ {
saturatedPackArguments.push_back(builtinTypes->errorRecoveryTypePack()); saturatedPackArguments.push_back(builtinTypes->errorTypePack);
} }
for (TypeId& arg : saturatedTypeArguments) for (TypeId& arg : saturatedTypeArguments)
@ -416,7 +414,7 @@ void ConstraintSolver::run()
} }
// Free types that have no constraints at all can be generalized right away. // Free types that have no constraints at all can be generalized right away.
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
for (TypeId ty : constraintSet.freeTypes) for (TypeId ty : constraintSet.freeTypes)
{ {
@ -477,7 +475,7 @@ void ConstraintSolver::run()
// expansion types, etc, so we need to follow it. // expansion types, etc, so we need to follow it.
ty = follow(ty); ty = follow(ty);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (seen.contains(ty)) if (seen.contains(ty))
continue; continue;
@ -496,7 +494,7 @@ void ConstraintSolver::run()
if (refCount <= 1) if (refCount <= 1)
unblock(ty, Location{}); unblock(ty, Location{});
if (FFlag::LuauEagerGeneralization3 && refCount == 0) if (FFlag::LuauEagerGeneralization4 && refCount == 0)
generalizeOneType(ty); generalizeOneType(ty);
} }
} }
@ -674,7 +672,7 @@ void ConstraintSolver::initFreeTypeTracking()
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0); auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
refCount += 1; refCount += 1;
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr); auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr);
it->second.insert(c.get()); it->second.insert(c.get());
@ -730,7 +728,7 @@ void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, Typ
constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed
); // FIXME? Is this the right polarity? ); // FIXME? Is this the right polarity?
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
trackInteriorFreeType(constraint->scope, ty); trackInteriorFreeType(constraint->scope, ty);
return; return;
@ -882,7 +880,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
else else
{ {
reportError(CodeTooComplex{}, constraint->location); reportError(CodeTooComplex{}, constraint->location);
bind(constraint, c.generalizedType, builtinTypes->errorRecoveryType()); bind(constraint, c.generalizedType, builtinTypes->errorType);
} }
// We check if this member is initialized and then access it, but // We check if this member is initialized and then access it, but
@ -891,7 +889,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
{ {
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access) for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
ty = follow(ty); ty = follow(ty);
if (auto freeTy = get<FreeType>(ty)) if (auto freeTy = get<FreeType>(ty))
@ -912,7 +910,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
} }
} }
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (constraint->scope->interiorFreeTypePacks) if (constraint->scope->interiorFreeTypePacks)
{ {
@ -932,7 +930,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
} }
} }
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (c.noGenerics) if (c.noGenerics)
{ {
@ -1013,7 +1011,7 @@ bool ConstraintSolver::tryDispatch(const IterableConstraint& c, NotNull<const Co
if (0 == iterator.head.size()) if (0 == iterator.head.size())
{ {
for (TypeId ty : c.variables) for (TypeId ty : c.variables)
unify(constraint, builtinTypes->errorRecoveryType(), ty); unify(constraint, builtinTypes->errorType, ty);
return true; return true;
} }
@ -1152,7 +1150,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2 && occursCheck(cTarget, result)) if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2 && occursCheck(cTarget, result))
{ {
reportError(OccursCheckFailed{}, constraint->location); reportError(OccursCheckFailed{}, constraint->location);
bind(constraint, cTarget, builtinTypes->errorRecoveryType()); bind(constraint, cTarget, builtinTypes->errorType);
} }
else else
{ {
@ -1167,7 +1165,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
if (!tf.has_value()) if (!tf.has_value())
{ {
reportError(UnknownSymbol{petv->name.value, UnknownSymbol::Context::Type}, constraint->location); reportError(UnknownSymbol{petv->name.value, UnknownSymbol::Context::Type}, constraint->location);
bindResult(errorRecoveryType()); bindResult(builtinTypes->errorType);
return true; return true;
} }
@ -1183,7 +1181,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
if (occursCheck(lhs, rhs)) if (occursCheck(lhs, rhs))
{ {
reportError(OccursCheckFailed{}, constraint->location); reportError(OccursCheckFailed{}, constraint->location);
bindResult(errorRecoveryType()); bindResult(builtinTypes->errorType);
return true; return true;
} }
@ -1254,7 +1252,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
if (itf.foundInfiniteType) if (itf.foundInfiniteType)
{ {
// TODO (CLI-56761): Report an error. // TODO (CLI-56761): Report an error.
bindResult(errorRecoveryType()); bindResult(builtinTypes->errorType);
reportError(GenericError{"Recursive type being used with different parameters"}, constraint->location); reportError(GenericError{"Recursive type being used with different parameters"}, constraint->location);
return true; return true;
} }
@ -1278,7 +1276,7 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
if (!maybeInstantiated.has_value()) if (!maybeInstantiated.has_value())
{ {
// TODO (CLI-56761): Report an error. // TODO (CLI-56761): Report an error.
bindResult(errorRecoveryType()); bindResult(builtinTypes->errorType);
return true; return true;
} }
@ -1377,7 +1375,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
TypePackId argsPack = follow(c.argsPack); TypePackId argsPack = follow(c.argsPack);
TypePackId result = follow(c.result); TypePackId result = follow(c.result);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (isBlocked(fn)) if (isBlocked(fn))
return block(c.fn, constraint); return block(c.fn, constraint);
@ -1401,7 +1399,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
// if we're calling an error type, the result is an error type, and that's that. // if we're calling an error type, the result is an error type, and that's that.
if (get<ErrorType>(fn)) if (get<ErrorType>(fn))
{ {
bind(constraint, c.result, builtinTypes->errorRecoveryTypePack()); bind(constraint, c.result, builtinTypes->errorTypePack);
fillInDiscriminantTypes(constraint, c.discriminantTypes); fillInDiscriminantTypes(constraint, c.discriminantTypes);
return true; return true;
} }
@ -1517,7 +1515,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy); const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
for (TypeId freeTy : u2.newFreshTypes) for (TypeId freeTy : u2.newFreshTypes)
trackInteriorFreeType(constraint->scope, freeTy); trackInteriorFreeType(constraint->scope, freeTy);
@ -2084,7 +2082,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
TypeId upperBound = TypeId upperBound =
arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed}); 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)); 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}; FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
emplace<FreeType>(constraint, resultType, freeResult); emplace<FreeType>(constraint, resultType, freeResult);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
trackInteriorFreeType(constraint->scope, resultType); trackInteriorFreeType(constraint->scope, resultType);
tt->indexer = TableIndexer{indexType, resultType}; tt->indexer = TableIndexer{indexType, resultType};
@ -2304,7 +2302,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
{ {
auto lhsFreeUpperBound = follow(lhsFree->upperBound); auto lhsFreeUpperBound = follow(lhsFree->upperBound);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue); const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
if (!blocked.empty()) if (!blocked.empty())
@ -2424,6 +2422,10 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
if (lhsTable->state == TableState::Unsealed || lhsTable->state == TableState::Free) if (lhsTable->state == 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); bind(constraint, c.propType, rhsType);
Property& newProp = lhsTable->props[propName]; Property& newProp = lhsTable->props[propName];
newProp.readTy = rhsType; newProp.readTy = rhsType;
@ -2907,7 +2909,7 @@ bool ConstraintSolver::tryDispatchIterableTable(TypeId iteratorTy, const Iterabl
{ {
std::vector<TypeId> expectedVariables{iteratorTable->indexer->indexType, iteratorTable->indexer->indexResultType}; std::vector<TypeId> expectedVariables{iteratorTable->indexer->indexType, iteratorTable->indexer->indexResultType};
while (c.variables.size() >= expectedVariables.size()) 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) for (size_t i = 0; i < c.variables.size(); ++i)
{ {
@ -3216,7 +3218,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
{ {
const TypeId upperBound = follow(ft->upperBound); const TypeId upperBound = follow(ft->upperBound);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound)) if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
{ {
@ -3273,7 +3275,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
} }
if (!blocked.empty()) if (!blocked.empty())
return {blocked, std::nullopt}; return {std::move(blocked), std::nullopt};
if (options.empty()) if (options.empty())
return {{}, std::nullopt}; return {{}, std::nullopt};
@ -3310,7 +3312,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
} }
if (!blocked.empty()) if (!blocked.empty())
return {blocked, std::nullopt}; return {std::move(blocked), std::nullopt};
if (options.empty()) if (options.empty())
return {{}, std::nullopt}; return {{}, std::nullopt};
@ -3613,7 +3615,7 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l
if (info.name.empty()) if (info.name.empty())
{ {
reportError(UnknownRequire{}, location); reportError(UnknownRequire{}, location);
return errorRecoveryType(); return builtinTypes->errorType;
} }
for (const auto& [location, path] : requireCycles) 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) if (!moduleResolver->moduleExists(info.name) && !info.optional)
reportError(UnknownRequire{moduleResolver->getHumanReadableModuleName(info.name)}, location); reportError(UnknownRequire{moduleResolver->getHumanReadableModuleName(info.name)}, location);
return errorRecoveryType(); return builtinTypes->errorType;
} }
if (module->type != SourceCode::Type::Module) if (module->type != SourceCode::Type::Module)
{ {
reportError(IllegalRequire{module->humanReadableName, "Module is not a ModuleScript. It cannot be required."}, location); reportError(IllegalRequire{module->humanReadableName, "Module is not a ModuleScript. It cannot be required."}, location);
return errorRecoveryType(); return builtinTypes->errorType;
} }
TypePackId modulePack = module->returnType; TypePackId modulePack = module->returnType;
if (get<ErrorTypePack>(modulePack)) if (get<ErrorTypePack>(modulePack))
return errorRecoveryType(); return builtinTypes->errorType;
std::optional<TypeId> moduleType = first(modulePack); std::optional<TypeId> moduleType = first(modulePack);
if (!moduleType) if (!moduleType)
{ {
reportError(IllegalRequire{module->humanReadableName, "Module does not return exactly 1 value. It cannot be required."}, location); reportError(IllegalRequire{module->humanReadableName, "Module does not return exactly 1 value. It cannot be required."}, location);
return errorRecoveryType(); return builtinTypes->errorType;
} }
return *moduleType; return *moduleType;
@ -3673,28 +3675,33 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
return; return;
auto sourceRefs = unresolvedConstraints.find(source); auto sourceRefs = unresolvedConstraints.find(source);
if (!sourceRefs) if (sourceRefs)
return; {
// we read out the count before proceeding to avoid hash invalidation issues. // we read out the count before proceeding to avoid hash invalidation issues.
size_t count = *sourceRefs; size_t count = *sourceRefs;
auto [targetRefs, _] = unresolvedConstraints.try_insert(target, 0); auto [targetRefs, _] = unresolvedConstraints.try_insert(target, 0);
targetRefs += count; targetRefs += count;
}
// Any constraint that might have mutated source may now mutate target // Any constraint that might have mutated source may now mutate target
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
auto it = mutatedFreeTypeToConstraint.find(source); auto it = mutatedFreeTypeToConstraint.find(source);
if (it != mutatedFreeTypeToConstraint.end()) if (it != mutatedFreeTypeToConstraint.end())
{ {
auto [it2, fresh] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet<const Constraint*>{nullptr}); const DenseHashSet<const Constraint*>& constraintsAffectedBySource = it->second;
for (const Constraint* constraint : it->second)
{
it2->second.insert(constraint);
auto [it3, fresh2] = maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, DenseHashSet<TypeId>{nullptr}); auto [it2, fresh2] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet<const Constraint*>{nullptr});
DenseHashSet<const Constraint*>& constraintsAffectedByTarget = it2->second;
// auto [it2, fresh] = mutatedFreeTypeToConstraint.try_emplace(target, DenseHashSet<const Constraint*>{nullptr});
for (const Constraint* constraint : constraintsAffectedBySource)
{
constraintsAffectedByTarget.insert(constraint);
auto [it3, fresh3] = maybeMutatedFreeTypes.try_emplace(NotNull{constraint}, DenseHashSet<TypeId>{nullptr});
it3->second.insert(target); it3->second.insert(target);
} }
} }
@ -3784,16 +3791,6 @@ TypeId ConstraintSolver::simplifyUnion(NotNull<Scope> scope, Location location,
return ::Luau::simplifyUnion(builtinTypes, arena, left, right).result; 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) TypePackId ConstraintSolver::anyifyModuleReturnTypePackGenerics(TypePackId tp)
{ {
tp = follow(tp); tp = follow(tp);
@ -3821,7 +3818,7 @@ TypePackId ConstraintSolver::anyifyModuleReturnTypePackGenerics(TypePackId tp)
if (std::optional<TypePackId> tail = it.tail()) if (std::optional<TypePackId> tail = it.tail())
resultTail = anyifyModuleReturnTypePackGenerics(*tail); resultTail = anyifyModuleReturnTypePackGenerics(*tail);
return arena->addTypePack(resultTypes, resultTail); return arena->addTypePack(std::move(resultTypes), resultTail);
} }
LUAU_NOINLINE void ConstraintSolver::throwTimeLimitError() const LUAU_NOINLINE void ConstraintSolver::throwTimeLimitError() const

View file

@ -253,10 +253,10 @@ static ScopeSnapshot snapshotScope(const Scope* scope, ToStringOptions& opts)
} }
return ScopeSnapshot{ return ScopeSnapshot{
bindings, std::move(bindings),
typeBindings, std::move(typeBindings),
typePackBindings, std::move(typePackBindings),
children, std::move(children),
}; };
} }
@ -307,7 +307,7 @@ void DcrLogger::captureGenerationError(const TypeError& error)
{ {
std::string stringifiedError = toString(error); std::string stringifiedError = toString(error);
generationLog.errors.push_back(ErrorSnapshot{ generationLog.errors.push_back(ErrorSnapshot{
/* message */ stringifiedError, /* message */ std::move(stringifiedError),
/* location */ error.location, /* location */ error.location,
}); });
} }
@ -424,7 +424,7 @@ StepSnapshot DcrLogger::prepareStepSnapshot(
current, current,
force, force,
std::move(constraints), std::move(constraints),
scopeSnapshot, std::move(scopeSnapshot),
std::move(typeStrings), std::move(typeStrings),
}; };
} }
@ -443,7 +443,7 @@ void DcrLogger::captureTypeCheckError(const TypeError& error)
{ {
std::string stringifiedError = toString(error); std::string stringifiedError = toString(error);
checkLog.errors.push_back(ErrorSnapshot{ checkLog.errors.push_back(ErrorSnapshot{
/* message */ stringifiedError, /* message */ std::move(stringifiedError),
/* location */ error.location, /* location */ error.location,
}); });
} }

View file

@ -18,7 +18,7 @@
#include <unordered_set> #include <unordered_set>
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10) LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive) LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive)
@ -159,7 +159,7 @@ struct ErrorConverter
} }
if (result.empty()) 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) if (tm.error)
@ -663,7 +663,7 @@ struct ErrorConverter
} }
// binary operators // 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()) 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 "; 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" + 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"; "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) TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason)
: wantedType(wantedType) : wantedType(wantedType)
, givenType(givenType) , givenType(givenType)
, reason(reason) , reason(std::move(reason))
{ {
} }
TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional<TypeError> error) TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, std::optional<TypeError> error)
: wantedType(wantedType) : wantedType(wantedType)
, givenType(givenType) , givenType(givenType)
, reason(reason) , reason(std::move(reason))
, error(error ? std::make_shared<TypeError>(std::move(*error)) : nullptr) , error(error ? std::make_shared<TypeError>(std::move(*error)) : nullptr)
{ {
} }
@ -929,7 +929,7 @@ TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reas
: wantedType(wantedType) : wantedType(wantedType)
, givenType(givenType) , givenType(givenType)
, context(context) , context(context)
, reason(reason) , reason(std::move(reason))
{ {
} }
@ -937,7 +937,7 @@ TypeMismatch::TypeMismatch(TypeId wantedType, TypeId givenType, std::string reas
: wantedType(wantedType) : wantedType(wantedType)
, givenType(givenType) , givenType(givenType)
, context(context) , context(context)
, reason(reason) , reason(std::move(reason))
, error(error ? std::make_shared<TypeError>(std::move(*error)) : nullptr) , error(error ? std::make_shared<TypeError>(std::move(*error)) : nullptr)
{ {
} }

View file

@ -3,8 +3,12 @@
#include "Luau/Scope.h" #include "Luau/Scope.h"
#include "Luau/TypeArena.h" #include "Luau/TypeArena.h"
#include "Luau/TypeIds.h"
#include "Luau/TypePack.h" #include "Luau/TypePack.h"
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
#include "Luau/VisitType.h"
LUAU_FASTFLAGVARIABLE(LuauImplicitTableIndexerKeys)
namespace Luau namespace Luau
{ {
@ -70,6 +74,106 @@ bool ExpectedTypeVisitor::visit(AstStatReturn* stat)
return true; return true;
} }
namespace
{
struct IndexerIndexCollector : public TypeOnceVisitor
{
NotNull<TypeIds> indexes;
explicit IndexerIndexCollector(NotNull<TypeIds> 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<TypeArena> arena;
TypeIds indexes;
explicit IndexCollector(NotNull<TypeArena> 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) bool ExpectedTypeVisitor::visit(AstExprCall* expr)
{ {
auto ty = astTypes->find(expr->func); auto ty = astTypes->find(expr->func);

View file

@ -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<FragmentParseResult> parseFragment( std::optional<FragmentParseResult> parseFragment(
@ -512,7 +512,7 @@ std::optional<FragmentParseResult> parseFragment(
opts.allowDeclarationSyntax = false; opts.allowDeclarationSyntax = false;
opts.captureComments = true; opts.captureComments = true;
opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack), startPos}; 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. // This means we threw a ParseError and we should decline to offer autocomplete here.
if (p.root == nullptr) if (p.root == nullptr)
return std::nullopt; return std::nullopt;
@ -1037,7 +1037,7 @@ std::optional<FragmentParseResult> parseFragment_DEPRECATED(
opts.allowDeclarationSyntax = false; opts.allowDeclarationSyntax = false;
opts.captureComments = true; opts.captureComments = true;
opts.parseFragment = FragmentParseResumeSettings{std::move(result.localMap), std::move(result.localStack), startPos}; 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. // This means we threw a ParseError and we should decline to offer autocomplete here.
if (p.root == nullptr) if (p.root == nullptr)
return std::nullopt; return std::nullopt;
@ -1192,7 +1192,7 @@ FragmentTypeCheckResult typecheckFragment_(
{}, {},
nullptr, nullptr,
NotNull{&dfg}, NotNull{&dfg},
limits std::move(limits)
}; };
try try
@ -1351,7 +1351,7 @@ FragmentAutocompleteResult fragmentAutocomplete(
tcResult.freshScope, tcResult.freshScope,
cursorPosition, cursorPosition,
frontend.fileResolver, frontend.fileResolver,
callback std::move(callback)
); );
if (FFlag::LuauFragmentAcMemoryLeak) if (FFlag::LuauFragmentAcMemoryLeak)
freeze(tcResult.incrementalModule->internalTypes); freeze(tcResult.incrementalModule->internalTypes);

View file

@ -40,7 +40,7 @@ LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(LuauInferInNoCheckMode) LUAU_FASTFLAG(LuauInferInNoCheckMode)
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3) LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes) LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
@ -207,16 +207,16 @@ LoadDefinitionFileResult Frontend::loadDefinitionFile(
Luau::ParseResult parseResult = parseSourceForModule(source, sourceModule, captureComments); Luau::ParseResult parseResult = parseSourceForModule(source, sourceModule, captureComments);
if (parseResult.errors.size() > 0) 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, {}); ModulePtr checkedModule = check(sourceModule, Mode::Definition, {}, std::nullopt, /*forAutocomplete*/ false, /*recordJsonLog*/ false, {});
if (checkedModule->errors.size() > 0) 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 namespace
@ -970,7 +970,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
environmentScope, environmentScope,
/*forAutocomplete*/ true, /*forAutocomplete*/ true,
/*recordJsonLog*/ false, /*recordJsonLog*/ false,
typeCheckLimits std::move(typeCheckLimits)
); );
double duration = getTimestamp() - timestamp; double duration = getTimestamp() - timestamp;
@ -990,7 +990,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
return; 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; double duration = getTimestamp() - timestamp;
@ -1172,7 +1172,7 @@ void Frontend::sendQueueCycleItemTask(std::shared_ptr<BuildQueueWorkState> state
if (!item.processing) if (!item.processing)
{ {
sendQueueItemTask(state, i); sendQueueItemTask(std::move(state), i);
break; break;
} }
} }
@ -1314,10 +1314,10 @@ ModulePtr check(
parentScope, parentScope,
typeFunctionScope, typeFunctionScope,
std::move(prepareModuleScope), std::move(prepareModuleScope),
options, std::move(options),
limits, std::move(limits),
recordJsonLog, recordJsonLog,
writeJsonLog std::move(writeJsonLog)
); );
} }
@ -1438,13 +1438,13 @@ ModulePtr check(
requireCycles 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 // This optional<> only exists so that we can run one constructor when the flag
// is set, and another when it is unset. // is set, and another when it is unset.
std::optional<ConstraintSolver> cs; std::optional<ConstraintSolver> cs;
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
ConstraintSet constraintSet = cg.run(sourceModule.root); ConstraintSet constraintSet = cg.run(sourceModule.root);
result->errors = std::move(constraintSet.errors); 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 // If solver was interrupted, skip typechecking and replace all module results with error-supressing types to avoid leaking blocked/pending
// types // types
ScopePtr moduleScope = result->getModuleScope(); ScopePtr moduleScope = result->getModuleScope();
moduleScope->returnType = builtinTypes->errorRecoveryTypePack(); moduleScope->returnType = builtinTypes->errorTypePack;
for (auto& [name, ty] : result->declaredGlobals) for (auto& [name, ty] : result->declaredGlobals)
ty = builtinTypes->errorRecoveryType(); ty = builtinTypes->errorType;
for (auto& [name, tf] : result->exportedTypeBindings) for (auto& [name, tf] : result->exportedTypeBindings)
tf.type = builtinTypes->errorRecoveryType(); tf.type = builtinTypes->errorType;
} }
else if (FFlag::LuauNewSolverTypecheckCatchTimeouts) else if (FFlag::LuauNewSolverTypecheckCatchTimeouts)
{ {
@ -1705,7 +1705,7 @@ ModulePtr Frontend::check(
globals.globalTypeFunctionScope, globals.globalTypeFunctionScope,
prepareModuleScopeWrap, prepareModuleScopeWrap,
options, options,
typeCheckLimits, std::move(typeCheckLimits),
recordJsonLog, recordJsonLog,
writeJsonLog writeJsonLog
); );
@ -1740,7 +1740,7 @@ ModulePtr Frontend::check(
typeChecker.unifierIterationLimit = typeCheckLimits.unifierIterationLimit; typeChecker.unifierIterationLimit = typeCheckLimits.unifierIterationLimit;
typeChecker.cancellationToken = typeCheckLimits.cancellationToken; typeChecker.cancellationToken = typeCheckLimits.cancellationToken;
return typeChecker.check(sourceModule, mode, environmentScope); return typeChecker.check(sourceModule, mode, std::move(environmentScope));
} }
} }

View file

@ -16,7 +16,7 @@
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization3) LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(LuauGeneralizationCannotMutateAcrossModules) LUAU_FASTFLAGVARIABLE(LuauGeneralizationCannotMutateAcrossModules)
namespace Luau namespace Luau
@ -470,7 +470,7 @@ struct FreeTypeSearcher : TypeVisitor
bool visit(TypeId ty, const FreeType& ft) override bool visit(TypeId ty, const FreeType& ft) override
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (!subsumes(scope, ft.scope)) if (!subsumes(scope, ft.scope))
return true; return true;
@ -521,7 +521,7 @@ struct FreeTypeSearcher : TypeVisitor
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope)) if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
unsealedTables.insert(ty); unsealedTables.insert(ty);
else else
{ {
@ -594,7 +594,7 @@ struct FreeTypeSearcher : TypeVisitor
if (tt.indexer) if (tt.indexer)
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
// {[K]: V} is equivalent to three functions: get, set, and iterate // {[K]: V} is equivalent to three functions: get, set, and iterate
// //
@ -652,7 +652,7 @@ struct FreeTypeSearcher : TypeVisitor
if (!subsumes(scope, ftp.scope)) if (!subsumes(scope, ftp.scope))
return true; return true;
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
GeneralizationParams<TypePackId>& params = typePacks[tp]; GeneralizationParams<TypePackId>& params = typePacks[tp];
++params.useCount; ++params.useCount;
@ -1223,7 +1223,7 @@ GeneralizationResult<TypeId> generalizeType(
if (!hasLowerBound && !hasUpperBound) 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<BoundType>(asMutable(freeTy), builtinTypes->unknownType); emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
else else
{ {
@ -1247,7 +1247,7 @@ GeneralizationResult<TypeId> generalizeType(
if (follow(lb) != freeTy) if (follow(lb) != freeTy)
emplaceType<BoundType>(asMutable(freeTy), lb); emplaceType<BoundType>(asMutable(freeTy), lb);
else if (!isWithinFunction || (!FFlag::LuauEagerGeneralization3 && params.useCount == 1)) else if (!isWithinFunction || (!FFlag::LuauEagerGeneralization4 && params.useCount == 1))
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType); emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
else else
{ {
@ -1353,7 +1353,7 @@ std::optional<TypeId> generalize(
FreeTypeSearcher fts{scope, cachedTypes}; FreeTypeSearcher fts{scope, cachedTypes};
fts.traverse(ty); fts.traverse(ty);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
FunctionType* functionTy = getMutable<FunctionType>(ty); FunctionType* functionTy = getMutable<FunctionType>(ty);
auto pushGeneric = [&](TypeId t) auto pushGeneric = [&](TypeId t)
@ -1597,7 +1597,7 @@ void pruneUnnecessaryGenerics(
TypeId ty TypeId ty
) )
{ {
if (!FFlag::LuauEagerGeneralization3) if (!FFlag::LuauEagerGeneralization4)
return; return;
ty = follow(ty); ty = follow(ty);

View file

@ -5,7 +5,8 @@
#include "Luau/Scope.h" #include "Luau/Scope.h"
#include "Luau/VisitType.h" #include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(LuauInferPolarityOfReadWriteProperties)
namespace Luau namespace Luau
{ {
@ -48,6 +49,32 @@ struct InferPolarity : TypeVisitor
return false; return false;
const Polarity p = polarity; const Polarity p = polarity;
if (FFlag::LuauInferPolarityOfReadWriteProperties)
{
for (const auto& [name, prop] : tt.props)
{
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
{
for (const auto& [name, prop] : tt.props) for (const auto& [name, prop] : tt.props)
{ {
if (prop.isShared()) if (prop.isShared())
@ -68,6 +95,7 @@ struct InferPolarity : TypeVisitor
else else
LUAU_ASSERT(!"Unreachable"); LUAU_ASSERT(!"Unreachable");
} }
}
if (tt.indexer) if (tt.indexer)
{ {
@ -133,7 +161,7 @@ struct InferPolarity : TypeVisitor
template<typename TID> template<typename TID>
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty) static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
{ {
if (!FFlag::LuauEagerGeneralization3) if (!FFlag::LuauEagerGeneralization4)
return; return;
InferPolarity infer{arena, scope}; InferPolarity infer{arena, scope};

View file

@ -16,9 +16,6 @@ LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAttribute)
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
namespace Luau namespace Luau
@ -107,7 +104,7 @@ static void emitWarning(LintContext& context, LintWarning::Code code, const Loca
std::string message = vformat(format, args); std::string message = vformat(format, args);
va_end(args); va_end(args);
LintWarning warning = {code, location, message}; LintWarning warning = {code, location, std::move(message)};
context.result.push_back(warning); context.result.push_back(warning);
} }
@ -3392,8 +3389,6 @@ static void lintComments(LintContext& context, const std::vector<HotComment>& ho
static bool hasNativeCommentDirective(const std::vector<HotComment>& hotcomments) static bool hasNativeCommentDirective(const std::vector<HotComment>& hotcomments)
{ {
LUAU_ASSERT(FFlag::LintRedundantNativeAttribute);
for (const HotComment& hc : hotcomments) for (const HotComment& hc : hotcomments)
{ {
if (hc.content.empty() || hc.content[0] == ' ' || hc.content[0] == '\t') if (hc.content.empty() || hc.content[0] == ' ' || hc.content[0] == '\t')
@ -3417,8 +3412,6 @@ struct LintRedundantNativeAttribute : AstVisitor
public: public:
LUAU_NOINLINE static void process(LintContext& context) LUAU_NOINLINE static void process(LintContext& context)
{ {
LUAU_ASSERT(FFlag::LintRedundantNativeAttribute);
LintRedundantNativeAttribute pass; LintRedundantNativeAttribute pass;
pass.context = &context; pass.context = &context;
context.root->visit(&pass); context.root->visit(&pass);
@ -3540,7 +3533,7 @@ std::vector<LintWarning> lint(
if (context.warningEnabled(LintWarning::Code_ComparisonPrecedence)) if (context.warningEnabled(LintWarning::Code_ComparisonPrecedence))
LintComparisonPrecedence::process(context); LintComparisonPrecedence::process(context);
if (FFlag::LintRedundantNativeAttribute && context.warningEnabled(LintWarning::Code_RedundantNativeAttribute)) if (context.warningEnabled(LintWarning::Code_RedundantNativeAttribute))
{ {
if (hasNativeCommentDirective(hotcomments)) if (hasNativeCommentDirective(hotcomments))
LintRedundantNativeAttribute::process(context); LintRedundantNativeAttribute::process(context);

View file

@ -172,7 +172,7 @@ struct ClonePublicInterface : Substitution
InternalError{"Free type is escaping its module; please report this bug at " InternalError{"Free type is escaping its module; please report this bug at "
"https://github.com/luau-lang/luau/issues"} "https://github.com/luau-lang/luau/issues"}
); );
result = builtinTypes->errorRecoveryType(); result = builtinTypes->errorType;
} }
else if (auto genericty = getMutable<GenericType>(result)) else if (auto genericty = getMutable<GenericType>(result))
{ {
@ -196,7 +196,7 @@ struct ClonePublicInterface : Substitution
InternalError{"Free type pack is escaping its module; please report this bug at " InternalError{"Free type pack is escaping its module; please report this bug at "
"https://github.com/luau-lang/luau/issues"} "https://github.com/luau-lang/luau/issues"}
); );
clonedTp = builtinTypes->errorRecoveryTypePack(); clonedTp = builtinTypes->errorTypePack;
} }
else if (auto gtp = getMutable<GenericTypePack>(clonedTp)) else if (auto gtp = getMutable<GenericTypePack>(clonedTp))
gtp->scope = nullptr; gtp->scope = nullptr;
@ -218,7 +218,7 @@ struct ClonePublicInterface : Substitution
else else
{ {
module->errors.push_back(TypeError{module->scopes[0].first, UnificationTooComplex{}}); module->errors.push_back(TypeError{module->scopes[0].first, UnificationTooComplex{}});
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
} }
} }
@ -232,7 +232,7 @@ struct ClonePublicInterface : Substitution
else else
{ {
module->errors.push_back(TypeError{module->scopes[0].first, UnificationTooComplex{}}); 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); TypeId type = cloneType(tf.type);
return TypeFun{typeParams, typePackParams, type, tf.definitionLocation}; return TypeFun{std::move(typeParams), std::move(typePackParams), type, tf.definitionLocation};
} }
}; };

View file

@ -226,7 +226,7 @@ struct NonStrictTypeChecker
return result; return result;
} }
else if (get<ErrorTypePack>(pack)) else if (get<ErrorTypePack>(pack))
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
else if (finite(pack) && size(pack) == 0) else if (finite(pack) && size(pack) == 0)
return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil`
else else
@ -682,7 +682,7 @@ struct NonStrictTypeChecker
if (arguments.size() > argTypes.size()) if (arguments.size() > argTypes.size())
{ {
// We are passing more arguments than we expect, so we should error // 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; return fresh;
} }
@ -729,7 +729,7 @@ struct NonStrictTypeChecker
if (!remainingArgsOptional) if (!remainingArgsOptional)
{ {
reportError(CheckedFunctionIncorrectArgs{functionName, argTypes.size(), arguments.size()}, call->location); reportError(CheckedFunctionIncorrectArgs{std::move(functionName), argTypes.size(), arguments.size()}, call->location);
return fresh; return fresh;
} }
} }
@ -1009,7 +1009,7 @@ struct NonStrictTypeChecker
} }
symbol += ty->name.value; symbol += ty->name.value;
reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location); reportError(UnknownSymbol{std::move(symbol), UnknownSymbol::Context::Type}, ty->location);
} }
} }
} }

View file

@ -1363,7 +1363,7 @@ std::optional<TypePackId> Normalizer::unionOfTypePacks(TypePackId here, TypePack
else if (thereSubHere) else if (thereSubHere)
return here; return here;
if (!head.empty()) if (!head.empty())
return arena->addTypePack(TypePack{head, tail}); return arena->addTypePack(TypePack{std::move(head), tail});
else if (tail) else if (tail)
return *tail; return *tail;
else else
@ -1822,7 +1822,7 @@ std::optional<NormalizedType> Normalizer::negateNormal(const NormalizedType& her
} }
if (!rootNegations.empty()) if (!rootNegations.empty())
result.externTypes.pushPair(builtinTypes->externType, rootNegations); result.externTypes.pushPair(builtinTypes->externType, std::move(rootNegations));
} }
result.nils = get<NeverType>(here.nils) ? builtinTypes->nilType : builtinTypes->neverType; result.nils = get<NeverType>(here.nils) ? builtinTypes->nilType : builtinTypes->neverType;
@ -2375,7 +2375,7 @@ std::optional<TypePackId> Normalizer::intersectionOfTypePacks(TypePackId here, T
else if (thereSubHere) else if (thereSubHere)
return there; return there;
if (!head.empty()) if (!head.empty())
return arena->addTypePack(TypePack{head, tail}); return arena->addTypePack(TypePack{std::move(head), tail});
else if (tail) else if (tail)
return *tail; return *tail;
else else

View file

@ -256,7 +256,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
return {Analysis::ArityMismatch, {error}}; return {Analysis::ArityMismatch, {std::move(error)}};
} }
// If any of the unsatisfied arguments are not supertypes of // If any of the unsatisfied arguments are not supertypes of
@ -274,7 +274,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
return {Analysis::ArityMismatch, {error}}; return {Analysis::ArityMismatch, {std::move(error)}};
} }
} }
else else
@ -284,7 +284,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes); auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}}; TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
return {Analysis::ArityMismatch, {error}}; return {Analysis::ArityMismatch, {std::move(error)}};
} }
} }
} }
@ -305,7 +305,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes); auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg}}; TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg}};
return {Analysis::ArityMismatch, {error}}; return {Analysis::ArityMismatch, {std::move(error)}};
} }
} }
} }

View file

@ -10,6 +10,7 @@
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256) LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
LUAU_FASTFLAG(LuauSolverAgnosticClone)
namespace Luau namespace Luau
{ {
@ -189,7 +190,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
LUAU_ASSERT(!ttv->boundTo); LUAU_ASSERT(!ttv->boundTo);
for (const auto& [name, prop] : ttv->props) for (const auto& [name, prop] : ttv->props)
{ {
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone)
{ {
visitChild(prop.readTy); visitChild(prop.readTy);
visitChild(prop.writeTy); visitChild(prop.writeTy);
@ -773,7 +774,7 @@ void Substitution::replaceChildren(TypeId ty)
LUAU_ASSERT(!ttv->boundTo); LUAU_ASSERT(!ttv->boundTo);
for (auto& [name, prop] : ttv->props) for (auto& [name, prop] : ttv->props)
{ {
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2 || FFlag::LuauSolverAgnosticClone)
{ {
if (prop.readTy) if (prop.readTy)
prop.readTy = replace(prop.readTy); prop.readTy = replace(prop.readTy);

View file

@ -20,7 +20,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100) LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
namespace Luau namespace Luau
{ {
@ -165,13 +165,13 @@ SubtypingResult& SubtypingResult::orElse(const SubtypingResult& other)
SubtypingResult& SubtypingResult::withBothComponent(TypePath::Component component) SubtypingResult& SubtypingResult::withBothComponent(TypePath::Component component)
{ {
return withSubComponent(component).withSuperComponent(component); return withSubComponent(component).withSuperComponent(std::move(component));
} }
SubtypingResult& SubtypingResult::withSubComponent(TypePath::Component component) SubtypingResult& SubtypingResult::withSubComponent(TypePath::Component component)
{ {
if (reasoning.empty()) if (reasoning.empty())
reasoning.insert(SubtypingReasoning{Path(component), TypePath::kEmpty}); reasoning.insert(SubtypingReasoning{Path(std::move(component)), TypePath::kEmpty});
else else
{ {
for (auto& r : reasoning) for (auto& r : reasoning)
@ -184,7 +184,7 @@ SubtypingResult& SubtypingResult::withSubComponent(TypePath::Component component
SubtypingResult& SubtypingResult::withSuperComponent(TypePath::Component component) SubtypingResult& SubtypingResult::withSuperComponent(TypePath::Component component)
{ {
if (reasoning.empty()) if (reasoning.empty())
reasoning.insert(SubtypingReasoning{TypePath::kEmpty, Path(component)}); reasoning.insert(SubtypingReasoning{TypePath::kEmpty, Path(std::move(component))});
else else
{ {
for (auto& r : reasoning) for (auto& r : reasoning)
@ -196,13 +196,13 @@ SubtypingResult& SubtypingResult::withSuperComponent(TypePath::Component compone
SubtypingResult& SubtypingResult::withBothPath(TypePath::Path path) SubtypingResult& SubtypingResult::withBothPath(TypePath::Path path)
{ {
return withSubPath(path).withSuperPath(path); return withSubPath(path).withSuperPath(std::move(path));
} }
SubtypingResult& SubtypingResult::withSubPath(TypePath::Path path) SubtypingResult& SubtypingResult::withSubPath(TypePath::Path path)
{ {
if (reasoning.empty()) if (reasoning.empty())
reasoning.insert(SubtypingReasoning{path, TypePath::kEmpty}); reasoning.insert(SubtypingReasoning{std::move(path), TypePath::kEmpty});
else else
{ {
for (auto& r : reasoning) for (auto& r : reasoning)
@ -215,7 +215,7 @@ SubtypingResult& SubtypingResult::withSubPath(TypePath::Path path)
SubtypingResult& SubtypingResult::withSuperPath(TypePath::Path path) SubtypingResult& SubtypingResult::withSuperPath(TypePath::Path path)
{ {
if (reasoning.empty()) if (reasoning.empty())
reasoning.insert(SubtypingReasoning{TypePath::kEmpty, path}); reasoning.insert(SubtypingReasoning{TypePath::kEmpty, std::move(path)});
else else
{ {
for (auto& r : reasoning) for (auto& r : reasoning)
@ -680,14 +680,14 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
result.isSubtype = ok; result.isSubtype = ok;
result.isCacheable = false; result.isCacheable = false;
} }
else if (auto pair = get2<FreeType, FreeType>(subTy, superTy); FFlag::LuauEagerGeneralization3 && pair) else if (auto pair = get2<FreeType, FreeType>(subTy, superTy); FFlag::LuauEagerGeneralization4 && pair)
{ {
// Any two free types are potentially subtypes of one another because // Any two free types are potentially subtypes of one another because
// both of them could be narrowed to never. // both of them could be narrowed to never.
result = {true}; result = {true};
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy}); result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
} }
else if (auto superFree = get<FreeType>(superTy); superFree && FFlag::LuauEagerGeneralization3) else if (auto superFree = get<FreeType>(superTy); superFree && FFlag::LuauEagerGeneralization4)
{ {
// Given SubTy <: (LB <: SuperTy <: UB) // Given SubTy <: (LB <: SuperTy <: UB)
// //
@ -702,7 +702,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
if (result.isSubtype) if (result.isSubtype)
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy}); result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
} }
else if (auto subFree = get<FreeType>(subTy); subFree && FFlag::LuauEagerGeneralization3) else if (auto subFree = get<FreeType>(subTy); subFree && FFlag::LuauEagerGeneralization4)
{ {
// Given (LB <: SubTy <: UB) <: SuperTy // Given (LB <: SubTy <: UB) <: SuperTy
// //
@ -792,7 +792,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
assertReasoningValid(subTy, superTy, result, builtinTypes); 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> scope) SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId subTp, TypePackId superTp, NotNull<Scope> scope)
@ -1437,7 +1437,7 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
{ {
SubtypingResult result{true}; SubtypingResult result{true};
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (subTable->props.empty() && !subTable->indexer && subTable->state == TableState::Sealed && superTable->indexer) 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) if (subTable->indexer)
result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope)); 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 // As above, we assume that {| |} <: {T} because the unsealed table
// on the left will eventually gain the necessary indexer. // on the left will eventually gain the necessary indexer.

View file

@ -13,7 +13,7 @@
#include "Luau/TypeUtils.h" #include "Luau/TypeUtils.h"
#include "Luau/Unifier2.h" #include "Luau/Unifier2.h"
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAGVARIABLE(LuauWriteOnlyPropertyMangling)
namespace Luau namespace Luau
{ {
@ -195,11 +195,20 @@ TypeId matchLiteralType(
Property& prop = it->second; Property& prop = it->second;
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 // If we encounter a duplcate property, we may have already
// set it to be read-only. If that's the case, the only thing // set it to be read-only. If that's the case, the only thing
// that will definitely crash is trying to access a write // that will definitely crash is trying to access a write
// only property. // only property.
LUAU_ASSERT(!prop.isWriteOnly()); LUAU_ASSERT(!prop.isWriteOnly());
}
TypeId propTy = *prop.readTy; TypeId propTy = *prop.readTy;
auto it2 = expectedTableTy->props.find(keyStr); auto it2 = expectedTableTy->props.find(keyStr);

View file

@ -148,7 +148,7 @@ void findCyclicTypes(std::set<TypeId>& cycles, std::set<TypePackId>& cycleTPs, T
static std::pair<bool, std::optional<Luau::Name>> canUseTypeNameInScope(ScopePtr scope, const std::string& name) static std::pair<bool, std::optional<Luau::Name>> 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) 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); return tos(c.resultType) + " ~ hasIndexer " + tos(c.subjectType) + " " + tos(c.indexType);
} }
else if constexpr (std::is_same_v<T, AssignPropConstraint>) else if constexpr (std::is_same_v<T, AssignPropConstraint>)
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<T, AssignIndexConstraint>) else if constexpr (std::is_same_v<T, AssignIndexConstraint>)
return "assignIndex " + tos(c.lhsType) + " " + tos(c.indexType) + " " + tos(c.rhsType); return "assignIndex " + tos(c.lhsType) + " " + tos(c.indexType) + " " + tos(c.rhsType);
else if constexpr (std::is_same_v<T, UnpackConstraint>) else if constexpr (std::is_same_v<T, UnpackConstraint>)

View file

@ -2960,7 +2960,7 @@ TranspileResult transpile(std::string_view source, ParseOptions options, bool wi
auto allocator = Allocator{}; auto allocator = Allocator{};
auto names = AstNameTable{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()) if (!parseResult.errors.empty())
{ {

View file

@ -585,8 +585,8 @@ PendingExpansionType::PendingExpansionType(
) )
: prefix(prefix) : prefix(prefix)
, name(name) , name(name)
, typeArguments(typeArguments) , typeArguments(std::move(typeArguments))
, packArguments(packArguments) , packArguments(std::move(packArguments))
, index(++nextIndex) , index(++nextIndex)
{ {
} }
@ -619,8 +619,8 @@ FunctionType::FunctionType(
bool hasSelf bool hasSelf
) )
: definition(std::move(defn)) : definition(std::move(defn))
, generics(generics) , generics(std::move(generics))
, genericPacks(genericPacks) , genericPacks(std::move(genericPacks))
, argTypes(argTypes) , argTypes(argTypes)
, retTypes(retTypes) , retTypes(retTypes)
, hasSelf(hasSelf) , hasSelf(hasSelf)
@ -637,8 +637,8 @@ FunctionType::FunctionType(
bool hasSelf bool hasSelf
) )
: definition(std::move(defn)) : definition(std::move(defn))
, generics(generics) , generics(std::move(generics))
, genericPacks(genericPacks) , genericPacks(std::move(genericPacks))
, level(level) , level(level)
, argTypes(argTypes) , argTypes(argTypes)
, retTypes(retTypes) , retTypes(retTypes)
@ -1046,16 +1046,6 @@ BuiltinTypes::~BuiltinTypes()
FFlag::DebugLuauFreezeArena.value = prevFlag; FFlag::DebugLuauFreezeArena.value = prevFlag;
} }
TypeId BuiltinTypes::errorRecoveryType() const
{
return errorType;
}
TypePackId BuiltinTypes::errorRecoveryTypePack() const
{
return errorTypePack;
}
TypeId BuiltinTypes::errorRecoveryType(TypeId guess) const TypeId BuiltinTypes::errorRecoveryType(TypeId guess) const
{ {
return guess; return guess;

View file

@ -36,7 +36,7 @@ LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks) LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors) LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases) LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases)
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts)
namespace Luau namespace Luau
@ -598,7 +598,7 @@ TypePackId TypeChecker2::reconstructPack(AstArray<AstExpr*> exprs, TypeArena& ar
} }
TypePackId tail = lookupPack(exprs.data[exprs.size - 1]); 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 Scope* TypeChecker2::findInnermostScope(Location location) const
@ -713,7 +713,7 @@ void TypeChecker2::visit(AstStatReturn* ret)
{ {
Scope* scope = findInnermostScope(ret->location); Scope* scope = findInnermostScope(ret->location);
TypePackId expectedRetType = scope->returnType; TypePackId expectedRetType = scope->returnType;
if (FFlag::LuauTableLiteralSubtypeSpecificCheck) if (FFlag::LuauTableLiteralSubtypeSpecificCheck2)
{ {
if (ret->list.size == 0) if (ret->list.size == 0)
{ {
@ -811,7 +811,7 @@ void TypeChecker2::visit(AstStatLocal* local)
TypeId valueType = value ? lookupType(value) : nullptr; TypeId valueType = value ? lookupType(value) : nullptr;
if (valueType) if (valueType)
{ {
if (FFlag::LuauTableLiteralSubtypeSpecificCheck) if (FFlag::LuauTableLiteralSubtypeSpecificCheck2)
testPotentialLiteralIsSubtype(value, annotationType); testPotentialLiteralIsSubtype(value, annotationType);
else else
testIsSubtype(valueType, annotationType, value->location); 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. // 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) // ... and then expand it out to 3 values (if possible)
TypePack iteratorTypes = extendTypePack(arena, builtinTypes, iteratorPack, 3); TypePack iteratorTypes = extendTypePack(arena, builtinTypes, iteratorPack, 3);
@ -1120,7 +1120,7 @@ void TypeChecker2::visit(AstStatForIn* forInStatement)
if (const FunctionType* nextFtv = get<FunctionType>(*instantiatedNextFn)) if (const FunctionType* nextFtv = get<FunctionType>(*instantiatedNextFn))
{ {
checkFunction(nextFtv, instantiatedIteratorTypes, true); checkFunction(nextFtv, std::move(instantiatedIteratorTypes), true);
} }
else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *instantiatedNextFn)) else if (!isErrorSuppressing(forInStatement->values.data[0]->location, *instantiatedNextFn))
{ {
@ -1224,13 +1224,20 @@ void TypeChecker2::visit(AstStatAssign* assign)
continue; continue;
} }
if (FFlag::LuauTableLiteralSubtypeSpecificCheck) if (FFlag::LuauTableLiteralSubtypeSpecificCheck2)
{ {
// If rhsType </: lhsType, then it's not useful to also report that rhsType </: bindingType // FIXME CLI-142462: Due to the fact that we do not type state
if (testPotentialLiteralIsSubtype(rhs, lhsType)) // tables properly, table types "time travel." We can take
// advantage of this for the specific code pattern of:
//
// local t = {}
// t.foo = {} -- Type of the RHS gets time warped to `{ bar: {} }`
// t.foo.bar = {}
//
if (testLiteralOrAstTypeIsSubtype(rhs, lhsType))
{ {
if (std::optional<TypeId> bindingType = getBindingType(lhs)) if (std::optional<TypeId> bindingType = getBindingType(lhs))
testPotentialLiteralIsSubtype(rhs, *bindingType); testLiteralOrAstTypeIsSubtype(rhs, *bindingType);
} }
} }
else else
@ -2137,7 +2144,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
auto name = getIdentifierOfBaseVar(expr->left); auto name = getIdentifierOfBaseVar(expr->left);
reportError( reportError(
CannotInferBinaryOperation{ 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 expr->location
); );
@ -2197,7 +2204,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
expr->location expr->location
); );
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
} }
std::optional<TypeId> mm; std::optional<TypeId> 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); reportError(GenericError{format("Metamethod '%s' must return a value", it->second)}, expr->location);
} }
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
} }
} }
else else
@ -2288,7 +2295,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
reportError(CannotCallNonFunction{*mm}, expr->location); reportError(CannotCallNonFunction{*mm}, expr->location);
} }
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
} }
// If this is a string comparison, or a concatenation of strings, we // If this is a string comparison, or a concatenation of strings, we
// want to fall through to primitive behavior. // 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<TableType>(leftType) || get<TableType>(rightType))) else if (!leftMt && !rightMt && (get<TableType>(leftType) || get<TableType>(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 expr->location
); );
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
} }
case AstExprBinary::Op::And: case AstExprBinary::Op::And:
@ -2423,7 +2430,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
default: default:
// Unhandled AstExprBinary::Op possibility. // Unhandled AstExprBinary::Op possibility.
LUAU_ASSERT(false); LUAU_ASSERT(false);
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
} }
} }
@ -2509,7 +2516,7 @@ TypeId TypeChecker2::flattenPack(TypePackId pack)
return result; return result;
} }
else if (get<ErrorTypePack>(pack)) else if (get<ErrorTypePack>(pack))
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
else if (finite(pack) && size(pack) == 0) else if (finite(pack) && size(pack) == 0)
return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil`
else else
@ -2721,7 +2728,7 @@ void TypeChecker2::visit(AstTypeReference* ty)
} }
symbol += ty->name.value; 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); reportError(TypePackMismatch{superTy, subTy, reasonings.toString()}, location);
} }
bool TypeChecker2::testLiteralOrAstTypeIsSubtype(AstExpr* expr, TypeId expectedType)
{
NotNull<Scope> 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) bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedType)
{ {
auto exprType = follow(lookupType(expr)); 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; TypeId propTy;
if (context == ValueContext::LValue) if (context == ValueContext::LValue)
propTy = module->internalTypes.addType(IntersectionType{parts}); propTy = module->internalTypes.addType(IntersectionType{std::move(parts)});
else else
propTy = module->internalTypes.addType(UnionType{parts}); propTy = module->internalTypes.addType(UnionType{std::move(parts)});
return {NormalizationResult::True, propTy}; return {NormalizationResult::True, propTy};
} }
@ -3501,7 +3519,7 @@ void TypeChecker2::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData&
} }
if (!candidates.empty()) 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) bool TypeChecker2::isErrorSuppressing(Location loc, TypeId ty)

View file

@ -48,14 +48,15 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1); LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies) LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers) LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults) LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
LUAU_FASTFLAG(LuauUserTypeFunctionAliases) LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature)
LUAU_FASTFLAGVARIABLE(LuauOccursCheckForRefinement)
namespace Luau namespace Luau
{ {
@ -264,7 +265,7 @@ struct TypeFunctionReducer
Okay, Okay,
}; };
SkipTestResult testForSkippability(TypeId ty) SkipTestResult DEPRECATED_testForSkippability(TypeId ty)
{ {
ty = follow(ty); ty = follow(ty);
@ -283,7 +284,7 @@ struct TypeFunctionReducer
} }
else if (is<GenericType>(ty)) else if (is<GenericType>(ty))
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
return SkipTestResult::Generic; return SkipTestResult::Generic;
else else
return SkipTestResult::Irreducible; return SkipTestResult::Irreducible;
@ -292,6 +293,51 @@ struct TypeFunctionReducer
return SkipTestResult::Okay; return SkipTestResult::Okay;
} }
SkipTestResult testForSkippability(TypeId ty)
{
if (!FFlag::LuauEagerGeneralization4)
return DEPRECATED_testForSkippability(ty);
VecDeque<TypeId> queue;
DenseHashSet<TypeId> seen{nullptr};
queue.push_back(follow(ty));
while (!queue.empty())
{
TypeId t = queue.front();
queue.pop_front();
if (seen.contains(t))
continue;
if (is<TypeFunctionInstanceType>(t))
{
for (auto cyclicTy : cyclicTypeFunctions)
{
if (t == cyclicTy)
return SkipTestResult::CyclicTypeFunction;
}
if (!irreducible.contains(t))
return SkipTestResult::Defer;
return SkipTestResult::Irreducible;
}
else if (is<GenericType>(t))
return SkipTestResult::Generic;
else if (auto it = get<IntersectionType>(t))
{
for (TypeId part : it->parts)
queue.push_back(follow(part));
}
seen.insert(t);
}
return SkipTestResult::Okay;
}
SkipTestResult testForSkippability(TypePackId ty) const SkipTestResult testForSkippability(TypePackId ty) const
{ {
ty = follow(ty); ty = follow(ty);
@ -305,7 +351,7 @@ struct TypeFunctionReducer
} }
else if (is<GenericTypePack>(ty)) else if (is<GenericTypePack>(ty))
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
return SkipTestResult::Generic; return SkipTestResult::Generic;
else else
return SkipTestResult::Irreducible; return SkipTestResult::Irreducible;
@ -517,7 +563,7 @@ struct TypeFunctionReducer
ctx.userFuncName = tfit->userFuncName; ctx.userFuncName = tfit->userFuncName;
TypeFunctionReductionResult<TypeId> result = tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); TypeFunctionReductionResult<TypeId> 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<TypePackId> result = TypeFunctionReductionResult<TypePackId> result =
tfit->function->reducer(subject, tfit->typeArguments, tfit->packArguments, NotNull{&ctx}); 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<TypeFunctionReductionResult<TypeId>> tryDistributeTypeFunct
} }
if (reductionStatus != Reduction::MaybeOk || !blockedTypes.empty()) if (reductionStatus != Reduction::MaybeOk || !blockedTypes.empty())
return {{std::nullopt, reductionStatus, blockedTypes, {}}}; return {{std::nullopt, reductionStatus, std::move(blockedTypes), {}}};
if (!results.empty()) if (!results.empty())
{ {
@ -924,7 +970,7 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones // 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) if (!ctx->typeFunctionRuntime->allowEvaluation || typeFunction->userFuncData.definition->hasErrors)
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
FindUserTypeFunctionBlockers check{ctx}; FindUserTypeFunctionBlockers check{ctx};
@ -949,7 +995,7 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
{ {
// Cannot evaluate if a potential dependency couldn't be parsed // Cannot evaluate if a potential dependency couldn't be parsed
if (definition.first->hasErrors) if (definition.first->hasErrors)
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first)) if (std::optional<std::string> error = ctx->typeFunctionRuntime->registerFunction(definition.first))
{ {
@ -1090,7 +1136,7 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
ctx->typeFunctionRuntime->messages.clear(); ctx->typeFunctionRuntime->messages.clear();
if (auto error = checkResultForError(L, name.value, lua_pcall(L, int(typeParams.size()), 1, 0))) 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 the return value is not a type userdata, return with error message
if (!isTypeUserData(L, 1)) if (!isTypeUserData(L, 1))
@ -1222,7 +1268,7 @@ TypeFunctionReductionResult<TypeId> lenTypeFunction(
const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType); const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType);
if (!instantiatedMmFtv) if (!instantiatedMmFtv)
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
TypePackId inferredArgPack = ctx->arena->addTypePack({operandTy}); TypePackId inferredArgPack = ctx->arena->addTypePack({operandTy});
Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice}; Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice};
@ -1259,7 +1305,7 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
if (isPending(operandTy, ctx->solver)) if (isPending(operandTy, ctx->solver))
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
operandTy = follow(operandTy); operandTy = follow(operandTy);
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy); std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
@ -1305,7 +1351,7 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType); const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType);
if (!instantiatedMmFtv) if (!instantiatedMmFtv)
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
TypePackId inferredArgPack = ctx->arena->addTypePack({operandTy}); TypePackId inferredArgPack = ctx->arena->addTypePack({operandTy});
Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice}; Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice};
@ -1772,7 +1818,7 @@ TypeFunctionReductionResult<TypeId> concatTypeFunction(
const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType); const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType);
if (!instantiatedMmFtv) if (!instantiatedMmFtv)
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
std::vector<TypeId> inferredArgs; std::vector<TypeId> inferredArgs;
if (!reversed) if (!reversed)
@ -1856,7 +1902,7 @@ TypeFunctionReductionResult<TypeId> orTypeFunction(
return {rhsTy, Reduction::MaybeOk, {}, {}}; return {rhsTy, Reduction::MaybeOk, {}, {}};
// check to see if both operand types are resolved enough, and wait to reduce if not // check to see if both operand types are resolved enough, and wait to reduce if not
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy)) if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
@ -1903,7 +1949,7 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
if (lhsTy == instance || rhsTy == instance) if (lhsTy == instance || rhsTy == instance)
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}}; return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy)) if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
@ -1997,7 +2043,7 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType); const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType);
if (!instantiatedMmFtv) if (!instantiatedMmFtv)
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
TypePackId inferredArgPack = ctx->arena->addTypePack({lhsTy, rhsTy}); TypePackId inferredArgPack = ctx->arena->addTypePack({lhsTy, rhsTy});
Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice}; Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice};
@ -2126,7 +2172,7 @@ TypeFunctionReductionResult<TypeId> eqTypeFunction(
const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType); const FunctionType* instantiatedMmFtv = get<FunctionType>(*instantiatedMmType);
if (!instantiatedMmFtv) if (!instantiatedMmFtv)
return {ctx->builtins->errorRecoveryType(), Reduction::MaybeOk, {}, {}}; return {ctx->builtins->errorType, Reduction::MaybeOk, {}, {}};
TypePackId inferredArgPack = ctx->arena->addTypePack({lhsTy, rhsTy}); TypePackId inferredArgPack = ctx->arena->addTypePack({lhsTy, rhsTy});
Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice}; Unifier2 u2{ctx->arena, ctx->builtins, ctx->scope, ctx->ice};
@ -2247,6 +2293,98 @@ bool isSimpleDiscriminant(TypeId ty)
return isApproximateTruthy(ty) || isApproximateFalsy(ty); return isApproximateTruthy(ty) || isApproximateFalsy(ty);
} }
struct RefineTypeScrubber : public Substitution
{
NotNull<TypeFunctionContext> ctx;
TypeId needle;
explicit RefineTypeScrubber(NotNull<TypeFunctionContext> 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<UnionType>(ty))
{
for (auto option : ut)
{
if (option == needle)
return true;
}
}
else if (auto it = get<IntersectionType>(ty))
{
for (auto part : it)
{
if (part == needle)
return true;
}
}
return false;
}
bool ignoreChildren(TypeId ty) override
{
return !is<UnionType, IntersectionType>(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<UnionType>(ty))
{
TypeIds newOptions;
for (auto option : ut)
{
if (option != needle && !is<NeverType>(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<IntersectionType>(ty))
{
TypeIds newParts;
for (auto part : it)
{
if (part != needle && !is<UnknownType>(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 } // namespace
TypeFunctionReductionResult<TypeId> refineTypeFunction( TypeFunctionReductionResult<TypeId> refineTypeFunction(
@ -2263,11 +2401,30 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
} }
TypeId targetTy = follow(typeParams.at(0)); TypeId targetTy = follow(typeParams.at(0));
if (FFlag::LuauOccursCheckForRefinement)
{
// If we end up minting a refine type like:
//
// t1 where t1 = refine<T | t1, Y>
//
// 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<T | t1, Y> => refine<T, Y>
RefineTypeScrubber rts{ctx, instance};
if (auto result = rts.substitute(targetTy))
targetTy = *result;
}
std::vector<TypeId> discriminantTypes; std::vector<TypeId> discriminantTypes;
for (size_t i = 1; i < typeParams.size(); i++) for (size_t i = 1; i < typeParams.size(); i++)
discriminantTypes.push_back(follow(typeParams.at(i))); discriminantTypes.push_back(follow(typeParams.at(i)));
const bool targetIsPending = FFlag::LuauEagerGeneralization3 ? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy) const bool targetIsPending = FFlag::LuauEagerGeneralization4 ? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
: isPending(targetTy, ctx->solver); : isPending(targetTy, ctx->solver);
// check to see if both operand types are resolved enough, and wait to reduce if not // check to see if both operand types are resolved enough, and wait to reduce if not
@ -2348,7 +2505,7 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
if (is<TableType>(target) || isSimpleDiscriminant(discriminant)) if (is<TableType>(target) || isSimpleDiscriminant(discriminant))
{ {
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant); SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
// Simplification considers free and generic types to be // Simplification considers free and generic types to be
// 'blocking', but that's not suitable for refine<>. // 'blocking', but that's not suitable for refine<>.
@ -2844,7 +3001,7 @@ TypeFunctionReductionResult<TypeId> keyofFunctionImpl(
if (singletons.size() == 1) if (singletons.size() == 1)
return {singletons.front(), Reduction::MaybeOk, {}, {}}; return {singletons.front(), Reduction::MaybeOk, {}, {}};
return {ctx->arena->addType(UnionType{singletons}), Reduction::MaybeOk, {}, {}}; return {ctx->arena->addType(UnionType{std::move(singletons)}), Reduction::MaybeOk, {}, {}};
} }
TypeFunctionReductionResult<TypeId> keyofTypeFunction( TypeFunctionReductionResult<TypeId> keyofTypeFunction(
@ -3289,7 +3446,7 @@ TypeFunctionReductionResult<TypeId> setmetatableTypeFunction(
blockedTypes.reserve(simplified.blockedTypes.size()); blockedTypes.reserve(simplified.blockedTypes.size());
for (auto ty : simplified.blockedTypes) for (auto ty : simplified.blockedTypes)
blockedTypes.push_back(ty); blockedTypes.push_back(ty);
return {std::nullopt, Reduction::MaybeOk, blockedTypes, {}}; return {std::nullopt, Reduction::MaybeOk, std::move(blockedTypes), {}};
} }
result = simplified.result; result = simplified.result;
@ -3489,7 +3646,7 @@ BuiltinTypeFunctions::BuiltinTypeFunctions()
, ltFunc{"lt", ltTypeFunction} , ltFunc{"lt", ltTypeFunction}
, leFunc{"le", leTypeFunction} , leFunc{"le", leTypeFunction}
, eqFunc{"eq", eqTypeFunction} , eqFunc{"eq", eqTypeFunction}
, refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization3} , refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization4}
, singletonFunc{"singleton", singletonTypeFunction} , singletonFunc{"singleton", singletonTypeFunction}
, unionFunc{"union", unionTypeFunction} , unionFunc{"union", unionTypeFunction}
, intersectFunc{"intersect", intersectTypeFunction} , intersectFunc{"intersect", intersectTypeFunction}

View file

@ -125,7 +125,7 @@ std::optional<TypePackId> TypeFunctionReductionGuesser::guess(TypePackId tp)
guessedHead.push_back(*guessedType); guessedHead.push_back(*guessedType);
} }
return arena->addTypePack(TypePack{guessedHead, tail}); return arena->addTypePack(TypePack{std::move(guessedHead), tail});
} }
TypeFunctionReductionGuessResult TypeFunctionReductionGuesser::guessTypeFunctionReductionForFunctionExpr( TypeFunctionReductionGuessResult TypeFunctionReductionGuesser::guessTypeFunctionReductionForFunctionExpr(
@ -182,7 +182,7 @@ TypeFunctionReductionGuessResult TypeFunctionReductionGuesser::guessTypeFunction
functionReducesTo.clear(); functionReducesTo.clear();
substitutable.clear(); substitutable.clear();
return TypeFunctionReductionGuessResult{results, recommendedAnnotation}; return TypeFunctionReductionGuessResult{std::move(results), recommendedAnnotation};
} }
std::optional<TypeId> TypeFunctionReductionGuesser::guessType(TypeId arg) std::optional<TypeId> TypeFunctionReductionGuesser::guessType(TypeId arg)

View file

@ -342,7 +342,7 @@ static int createOptional(lua_State* L)
components.emplace_back(allocateTypeFunctionType(L, TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::NilType))); components.emplace_back(allocateTypeFunctionType(L, TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::NilType)));
allocTypeUserData(L, TypeFunctionUnionType{components}); allocTypeUserData(L, TypeFunctionUnionType{std::move(components)});
return 1; return 1;
} }
@ -362,7 +362,7 @@ static int createUnion(lua_State* L)
for (int i = 1; i <= argSize; i++) for (int i = 1; i <= argSize; i++)
components.push_back(getTypeUserData(L, i)); components.push_back(getTypeUserData(L, i));
allocTypeUserData(L, TypeFunctionUnionType{components}); allocTypeUserData(L, TypeFunctionUnionType{std::move(components)});
return 1; return 1;
} }
@ -382,7 +382,7 @@ static int createIntersection(lua_State* L)
for (int i = 1; i <= argSize; i++) for (int i = 1; i <= argSize; i++)
components.push_back(getTypeUserData(L, i)); components.push_back(getTypeUserData(L, i));
allocTypeUserData(L, TypeFunctionIntersectionType{components}); allocTypeUserData(L, TypeFunctionIntersectionType{std::move(components)});
return 1; return 1;
} }
@ -542,7 +542,7 @@ static int createTable(lua_State* L)
if (metatable && !get<TypeFunctionTableType>(*metatable)) if (metatable && !get<TypeFunctionTableType>(*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()); 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; return 1;
} }
@ -904,7 +904,7 @@ static TypeFunctionTypePackId getTypePack(lua_State* L, int headIdx, int tailIdx
if (head.size() == 0 && tail.has_value()) if (head.size() == 0 && tail.has_value())
result = *tail; result = *tail;
else else
result = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{head, tail}); result = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{std::move(head), tail});
return result; return result;
} }

View file

@ -218,7 +218,7 @@ private:
if (!g->explicitName) if (!g->explicitName)
name = format("g%d", g->index); 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 else
{ {
@ -251,7 +251,7 @@ private:
if (!gPack->explicitName) if (!gPack->explicitName)
name = format("g%d", gPack->index); 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 else
{ {
@ -527,12 +527,12 @@ public:
if (hasExceededIterationLimit() || state->errors.size() != 0) if (hasExceededIterationLimit() || state->errors.size() != 0)
{ {
TypeId error = state->ctx->builtins->errorRecoveryType(); TypeId error = state->ctx->builtins->errorType;
types[ty] = error; types[ty] = error;
return 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) TypePackId deserialize(TypeFunctionTypePackId tp)
@ -542,12 +542,12 @@ public:
if (hasExceededIterationLimit() || state->errors.size() != 0) if (hasExceededIterationLimit() || state->errors.size() != 0)
{ {
TypePackId error = state->ctx->builtins->errorRecoveryTypePack(); TypePackId error = state->ctx->builtins->errorTypePack;
packs[tp] = error; packs[tp] = error;
return error; return error;
} }
return find(tp).value_or(state->ctx->builtins->errorRecoveryTypePack()); return find(tp).value_or(state->ctx->builtins->errorTypePack);
} }
private: private:

View file

@ -232,7 +232,7 @@ ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optiona
{ {
try try
{ {
return checkWithoutRecursionCheck(module, mode, environmentScope); return checkWithoutRecursionCheck(module, mode, std::move(environmentScope));
} }
catch (const RecursionLimitException&) catch (const RecursionLimitException&)
{ {
@ -688,7 +688,7 @@ static std::optional<Predicate> tryGetTypeGuardPredicate(const AstExprBinary& ex
if (!lvalue) if (!lvalue)
return std::nullopt; 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) if (expr.op == AstExprBinary::Op::CompareNe)
return NotPredicate{{std::move(predicate)}}; return NotPredicate{{std::move(predicate)}};
@ -1372,7 +1372,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
varTypes.front() = *fty; 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); unify(retPack, varPack, scope, forin.location);
@ -1681,7 +1681,7 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareExternTyp
if (!lookupType) if (!lookupType)
{ {
reportError(declaredExternType.location, UnknownSymbol{superName, UnknownSymbol::Type}); reportError(declaredExternType.location, UnknownSymbol{std::move(superName), UnknownSymbol::Type});
incorrectExternTypeDefinitions.insert(&declaredExternType); incorrectExternTypeDefinitions.insert(&declaredExternType);
return; return;
} }
@ -2166,7 +2166,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromTypeImpl(
if (goodOptions.empty()) if (goodOptions.empty())
reportError(location, UnknownProperty{type, name}); reportError(location, UnknownProperty{type, name});
else else
reportError(location, MissingUnionProperty{type, badOptions, name}); reportError(location, MissingUnionProperty{type, std::move(badOptions), name});
} }
return std::nullopt; return std::nullopt;
} }
@ -2615,7 +2615,7 @@ TypeId TypeChecker::unionOfTypes(TypeId a, TypeId b, const ScopePtr& scope, cons
if (types.size() == 1) if (types.size() == 1)
return types[0]; return types[0];
return addType(UnionType{types}); return addType(UnionType{std::move(types)});
} }
static std::optional<std::string> getIdentifierOfBaseVar(AstExpr* node) static std::optional<std::string> getIdentifierOfBaseVar(AstExpr* node)
@ -2923,7 +2923,7 @@ TypeId TypeChecker::checkRelationalOperation(
if (get<FreeType>(follow(lhsType)) && !isEquality) if (get<FreeType>(follow(lhsType)) && !isEquality)
{ {
auto name = getIdentifierOfBaseVar(expr.left); 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); return errorRecoveryType(booleanType);
} }
@ -3032,7 +3032,7 @@ TypeId TypeChecker::checkBinaryOperation(
if (!isNonstrictMode() && get<FreeType>(lhsType)) if (!isNonstrictMode() && get<FreeType>(lhsType))
{ {
auto name = getIdentifierOfBaseVar(expr.left); 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. // We will fall-through to the `return anyType` check below.
} }
@ -3102,7 +3102,7 @@ TypeId TypeChecker::checkBinaryOperation(
std::string op = opToMetaTableEntry(expr.op); std::string op = opToMetaTableEntry(expr.op);
if (auto fnt = findMetatableEntry(lhsType, op, expr.location, /* addErrors= */ true)) if (auto fnt = findMetatableEntry(lhsType, op, expr.location, /* addErrors= */ true))
return checkMetatableCall(*fnt, lhsType, rhsType); 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. // Note the intentionally reversed arguments here.
return checkMetatableCall(*fnt, rhsType, lhsType); 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, // 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. // but still add it to the bindings, so that autocomplete includes it in completions.
if (!isNonstrictMode()) if (!isNonstrictMode())
reportError(TypeError{expr.location, UnknownSymbol{name, UnknownSymbol::Binding}}); reportError(TypeError{expr.location, UnknownSymbol{std::move(name), UnknownSymbol::Binding}});
return result; return result;
} }
@ -3426,7 +3426,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
if (!state.errors.empty()) if (!state.errors.empty())
{ {
reportError(expr.location, UnknownProperty{lhs, name}); reportError(expr.location, UnknownProperty{lhs, std::move(name)});
retType = errorRecoveryType(retType); retType = errorRecoveryType(retType);
} }
else else
@ -3436,7 +3436,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
} }
else if (lhsTable->state == TableState::Sealed) 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); return errorRecoveryType(scope);
} }
else 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); return errorRecoveryType(scope);
} }
else if (get<IntersectionType>(lhs)) else if (get<IntersectionType>(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 intersection has a table part, report that it cannot be extended just as a sealed table
if (isTableIntersection(lhs)) 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); 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 intersection has a table part, report that it cannot be extended just as a sealed table
if (isTableIntersection(exprType)) 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); return errorRecoveryType(scope);
} }
} }
@ -3645,7 +3645,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
if (options.size() == 1) if (options.size() == 1)
return options[0]; return options[0];
return addType(UnionType{options}); return addType(UnionType{std::move(options)});
} }
return addType(IntersectionType{{propTypes.begin(), propTypes.end()}}); return addType(IntersectionType{{propTypes.begin(), propTypes.end()}});
@ -3700,7 +3700,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
if (options.size() == 1) if (options.size() == 1)
return options[0]; return options[0];
return addType(UnionType{options}); return addType(UnionType{std::move(options)});
} }
return addType(IntersectionType{{resultTypes.begin(), resultTypes.end()}}); return addType(IntersectionType{{resultTypes.begin(), resultTypes.end()}});
@ -3941,7 +3941,7 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
++expectedArgsCurr; ++expectedArgsCurr;
} }
TypePackId argPack = addTypePack(TypePackVar(TypePack{argTypes, funScope->varargPack})); TypePackId argPack = addTypePack(TypePackVar(TypePack{std::move(argTypes), funScope->varargPack}));
FunctionDefinition defn; FunctionDefinition defn;
defn.definitionModuleName = currentModule->name; defn.definitionModuleName = currentModule->name;
@ -4129,7 +4129,7 @@ void TypeChecker::checkArgumentList(
auto [minParams, optMaxParams] = getParameterExtents(&state.log, paramPack); auto [minParams, optMaxParams] = getParameterExtents(&state.log, paramPack);
state.reportError(TypeError{ state.reportError(TypeError{
location, 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; return;
} }
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}}); TypePackId varPack = addTypePack(TypePackVar{TypePack{std::move(rest), paramIter.tail()}});
state.tryUnify(tail, varPack); state.tryUnify(tail, varPack);
return; return;
} }
@ -4248,7 +4248,7 @@ void TypeChecker::checkArgumentList(
namePath = *path; namePath = *path;
state.reportError(TypeError{ 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; return;
} }
@ -4325,7 +4325,7 @@ void TypeChecker::checkArgumentList(
return; return;
} }
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}}); TypePackId varPack = addTypePack(TypePackVar{TypePack{std::move(rest), argIter.tail()}});
state.tryUnify(varPack, tail); state.tryUnify(varPack, tail);
return; return;
@ -4950,7 +4950,7 @@ WithPredicate<TypePackId> TypeChecker::checkExprList(
if (uninhabitable) if (uninhabitable)
return WithPredicate{uninhabitableTypePack}; return WithPredicate{uninhabitableTypePack};
return {pack, predicates}; return {pack, std::move(predicates)};
} }
std::optional<AstExpr*> TypeChecker::matchRequire(const AstExprCall& call) std::optional<AstExpr*> TypeChecker::matchRequire(const AstExprCall& call)
@ -5314,7 +5314,7 @@ void TypeChecker::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& d
} }
if (!candidates.empty()) 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) LUAU_NOINLINE void TypeChecker::reportErrorCodeTooComplex(const Location& location)
@ -5404,7 +5404,7 @@ TypeId TypeChecker::singletonType(std::string value)
TypeId TypeChecker::errorRecoveryType(const ScopePtr& scope) TypeId TypeChecker::errorRecoveryType(const ScopePtr& scope)
{ {
return builtinTypes->errorRecoveryType(); return builtinTypes->errorType;
} }
TypeId TypeChecker::errorRecoveryType(TypeId guess) TypeId TypeChecker::errorRecoveryType(TypeId guess)
@ -5414,7 +5414,7 @@ TypeId TypeChecker::errorRecoveryType(TypeId guess)
TypePackId TypeChecker::errorRecoveryTypePack(const ScopePtr& scope) TypePackId TypeChecker::errorRecoveryTypePack(const ScopePtr& scope)
{ {
return builtinTypes->errorRecoveryTypePack(); return builtinTypes->errorTypePack;
} }
TypePackId TypeChecker::errorRecoveryTypePack(TypePackId guess) TypePackId TypeChecker::errorRecoveryTypePack(TypePackId guess)
@ -5449,7 +5449,7 @@ TypeIdPredicate TypeChecker::mkTruthyPredicate(bool sense, TypeId emptySetTy)
std::optional<TypeId> TypeChecker::filterMapImpl(TypeId type, TypeIdPredicate predicate) std::optional<TypeId> TypeChecker::filterMapImpl(TypeId type, TypeIdPredicate predicate)
{ {
std::vector<TypeId> types = Luau::filterMap(type, predicate); std::vector<TypeId> types = Luau::filterMap(type, std::move(predicate));
if (!types.empty()) if (!types.empty())
return types.size() == 1 ? types[0] : addType(UnionType{std::move(types)}); return types.size() == 1 ? types[0] : addType(UnionType{std::move(types)});
return std::nullopt; return std::nullopt;
@ -5457,7 +5457,7 @@ std::optional<TypeId> TypeChecker::filterMapImpl(TypeId type, TypeIdPredicate pr
std::pair<std::optional<TypeId>, bool> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate) std::pair<std::optional<TypeId>, 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<NeverType>(ty))}; return {ty, !bool(get<NeverType>(ty))};
} }
@ -5556,9 +5556,9 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
typeName += lit->name.value; typeName += lit->name.value;
if (scope->lookupPack(typeName)) if (scope->lookupPack(typeName))
reportError(TypeError{annotation.location, SwappedGenericTypeParameter{typeName, SwappedGenericTypeParameter::Type}}); reportError(TypeError{annotation.location, SwappedGenericTypeParameter{std::move(typeName), SwappedGenericTypeParameter::Type}});
else else
reportError(TypeError{annotation.location, UnknownSymbol{typeName, UnknownSymbol::Type}}); reportError(TypeError{annotation.location, UnknownSymbol{std::move(typeName), UnknownSymbol::Type}});
return errorRecoveryType(scope); return errorRecoveryType(scope);
} }
@ -5844,7 +5844,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
for (AstType* ann : un->types) for (AstType* ann : un->types)
types.push_back(resolveType(scope, *ann)); types.push_back(resolveType(scope, *ann));
return addType(UnionType{types}); return addType(UnionType{std::move(types)});
} }
else if (const auto& un = annotation.as<AstTypeIntersection>()) else if (const auto& un = annotation.as<AstTypeIntersection>())
{ {
@ -5854,7 +5854,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
for (AstType* ann : un->types) for (AstType* ann : un->types)
types.push_back(resolveType(scope, *ann)); types.push_back(resolveType(scope, *ann));
return addType(IntersectionType{types}); return addType(IntersectionType{std::move(types)});
} }
else if (const auto& g = annotation.as<AstTypeGroup>()) else if (const auto& g = annotation.as<AstTypeGroup>())
{ {
@ -5890,7 +5890,7 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypeList
head.push_back(resolveType(scope, *ann)); head.push_back(resolveType(scope, *ann));
std::optional<TypePackId> tail = types.tailType ? std::optional<TypePackId>(resolveTypePack(scope, *types.tailType)) : std::nullopt; std::optional<TypePackId> tail = types.tailType ? std::optional<TypePackId>(resolveTypePack(scope, *types.tailType)) : std::nullopt;
return addTypePack(TypePack{head, tail}); return addTypePack(TypePack{std::move(head), tail});
} }
return addTypePack(TypePack{}); return addTypePack(TypePack{});
@ -5911,9 +5911,9 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypePack
if (!genericTy) if (!genericTy)
{ {
if (scope->lookupType(genericName)) if (scope->lookupType(genericName))
reportError(TypeError{generic->location, SwappedGenericTypeParameter{genericName, SwappedGenericTypeParameter::Pack}}); reportError(TypeError{generic->location, SwappedGenericTypeParameter{std::move(genericName), SwappedGenericTypeParameter::Pack}});
else else
reportError(TypeError{generic->location, UnknownSymbol{genericName, UnknownSymbol::Type}}); reportError(TypeError{generic->location, UnknownSymbol{std::move(genericName), UnknownSymbol::Type}});
result = errorRecoveryTypePack(scope); result = errorRecoveryTypePack(scope);
} }
@ -6089,7 +6089,7 @@ GenericTypeDefinitions TypeChecker::createGenericTypes(
scope->privateTypePackBindings[n] = cached; 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) 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 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) if (!key)
{ {
auto [result, ok] = filterMap(*ty, predicate); auto [result, ok] = filterMap(*ty, std::move(predicate));
addRefinement(refis, *target, *result); addRefinement(refis, *target, *result);
return; return;
} }
@ -6416,7 +6416,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r
return std::nullopt; 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. // Note: "vector" never happens here at this point, so we don't have to write something for it.

View file

@ -12,7 +12,7 @@
#include <algorithm> #include <algorithm>
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs) LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs)
namespace Luau namespace Luau
@ -306,7 +306,7 @@ TypePack extendTypePack(
TypePack newPack; TypePack newPack;
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity); newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
trackInteriorFreeTypePack(ftp->scope, *newPack.tail); trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2)
@ -343,7 +343,7 @@ TypePack extendTypePack(
else if (auto etp = getMutable<ErrorTypePack>(pack)) else if (auto etp = getMutable<ErrorTypePack>(pack))
{ {
while (result.head.size() < length) while (result.head.size() < length)
result.head.push_back(builtinTypes->errorRecoveryType()); result.head.push_back(builtinTypes->errorType);
result.tail = pack; result.tail = pack;
return result; return result;
@ -588,7 +588,7 @@ void trackInteriorFreeType(Scope* scope, TypeId ty)
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp) void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
{ {
LUAU_ASSERT(tp); LUAU_ASSERT(tp);
if (!FFlag::LuauEagerGeneralization3) if (!FFlag::LuauEagerGeneralization4)
return; return;
for (; scope; scope = scope->parent.get()) for (; scope; scope = scope->parent.get())

View file

@ -1078,33 +1078,33 @@ void Unifier::tryUnifyNormalizedTypes(
return; return;
if (get<UnknownType>(subNorm.tops)) if (get<UnknownType>(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<PrimitiveType>(subNorm.booleans)) if (get<PrimitiveType>(subNorm.booleans))
{ {
if (!get<PrimitiveType>(superNorm.booleans)) if (!get<PrimitiveType>(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<SingletonType>(subNorm.booleans)) else if (const SingletonType* stv = get<SingletonType>(subNorm.booleans))
{ {
if (!get<PrimitiveType>(superNorm.booleans) && stv != get<SingletonType>(superNorm.booleans)) if (!get<PrimitiveType>(superNorm.booleans) && stv != get<SingletonType>(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<PrimitiveType>(subNorm.nils)) if (get<PrimitiveType>(subNorm.nils))
if (!get<PrimitiveType>(superNorm.nils)) if (!get<PrimitiveType>(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<PrimitiveType>(subNorm.numbers)) if (get<PrimitiveType>(subNorm.numbers))
if (!get<PrimitiveType>(superNorm.numbers)) if (!get<PrimitiveType>(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)) 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<PrimitiveType>(subNorm.threads)) if (get<PrimitiveType>(subNorm.threads))
if (!get<PrimitiveType>(superNorm.errors)) if (!get<PrimitiveType>(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) for (const auto& [subExternType, _] : subNorm.externTypes.externTypes)
{ {
@ -1140,7 +1140,7 @@ void Unifier::tryUnifyNormalizedTypes(
if (!found) 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); return reportError(*e);
} }
if (!found) 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 (!subNorm.functions.isNever())
{ {
if (superNorm.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) for (TypeId superFun : superNorm.functions.parts)
{ {
std::unique_ptr<Unifier> innerState = makeChildUnifier(); std::unique_ptr<Unifier> innerState = makeChildUnifier();
const FunctionType* superFtv = get<FunctionType>(superFun); const FunctionType* superFtv = get<FunctionType>(superFun);
if (!superFtv) 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); TypePackId tgt = innerState->tryApplyOverloadedFunction(subTy, subNorm.functions, superFtv->argTypes);
innerState->tryUnify_(tgt, superFtv->retTypes); innerState->tryUnify_(tgt, superFtv->retTypes);
if (innerState->errors.empty()) if (innerState->errors.empty())
@ -1189,7 +1189,7 @@ void Unifier::tryUnifyNormalizedTypes(
else if (auto e = hasUnificationTooComplex(innerState->errors)) else if (auto e = hasUnificationTooComplex(innerState->errors))
return reportError(*e); return reportError(*e);
else 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()) if (overloads.isNever())
{ {
reportError(location, CannotCallNonFunction{function}); reportError(location, CannotCallNonFunction{function});
return builtinTypes->errorRecoveryTypePack(); return builtinTypes->errorTypePack;
} }
std::optional<TypePackId> result; std::optional<TypePackId> result;
@ -1266,7 +1266,7 @@ TypePackId Unifier::tryApplyOverloadedFunction(TypeId function, const Normalized
else else
{ {
reportError(location, CannotCallNonFunction{function}); 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()) while (superIter.good())
{ {
tryUnify_(*superIter, builtinTypes->errorRecoveryType()); tryUnify_(*superIter, builtinTypes->errorType);
superIter.advance(); superIter.advance();
} }
while (subIter.good()) while (subIter.good())
{ {
tryUnify_(*subIter, builtinTypes->errorRecoveryType()); tryUnify_(*subIter, builtinTypes->errorType);
subIter.advance(); 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."; std::string reason = "The former's metatable does not satisfy the requirements.";
if (e) if (e)
reportError(location, TypeMismatch{osuperTy, osubTy, reason, *e, mismatchContext()}); reportError(location, TypeMismatch{osuperTy, osubTy, std::move(reason), std::move(e), mismatchContext()});
else 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...) } // 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::Sealed:
case TableState::Unsealed: case TableState::Unsealed:
case TableState::Generic: case TableState::Generic:
reportError(mismatchError); reportError(std::move(mismatchError));
} }
} }
else if (log.getMutable<AnyType>(subTy) || log.getMutable<ErrorType>(subTy)) else if (log.getMutable<AnyType>(subTy) || log.getMutable<ErrorType>(subTy))
@ -2377,7 +2377,7 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
} }
else else
{ {
reportError(mismatchError); reportError(std::move(mismatchError));
} }
} }
@ -2462,7 +2462,7 @@ void Unifier::tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed
{ {
ok = false; ok = false;
std::string msg = "Extern type " + superExternType->name + " does not have an indexer"; std::string msg = "Extern type " + superExternType->name + " does not have an indexer";
reportError(location, GenericError{msg}); reportError(location, GenericError{std::move(msg)});
} }
if (!ok) if (!ok)
@ -2670,7 +2670,7 @@ void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
{ {
LUAU_ASSERT(get<ErrorTypePack>(anyTp)); LUAU_ASSERT(get<ErrorTypePack>(anyTp));
const TypeId anyTy = builtinTypes->errorRecoveryType(); const TypeId anyTy = builtinTypes->errorType;
std::vector<TypeId> queue; std::vector<TypeId> queue;
@ -2726,7 +2726,7 @@ bool Unifier::occursCheck(TypeId needle, TypeId haystack, bool reversed)
if (innerState->failure) if (innerState->failure)
{ {
reportError(location, OccursCheckFailed{}); 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) if (occurs)
{ {
reportError(location, OccursCheckFailed{}); reportError(location, OccursCheckFailed{});
log.replace(needle, BoundTypePack{builtinTypes->errorRecoveryTypePack()}); log.replace(needle, BoundTypePack{builtinTypes->errorTypePack});
} }
return occurs; return occurs;

View file

@ -19,7 +19,7 @@
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
namespace Luau namespace Luau
{ {
@ -329,12 +329,12 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
for (TypePackId genericPack : subFn->genericPacks) for (TypePackId genericPack : subFn->genericPacks)
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
genericPack = follow(genericPack); genericPack = follow(genericPack);
// TODO: Clip this follow() with LuauEagerGeneralization2 // TODO: Clip this follow() with LuauEagerGeneralization4
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack)); const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
if (gen) if (gen)
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity); 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->indexType, superTable->indexer->indexType);
result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType); result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType);
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
// FIXME: We can probably do something more efficient here. // FIXME: We can probably do something more efficient here.
result &= unify(superTable->indexer->indexType, subTable->indexer->indexType); result &= unify(superTable->indexer->indexType, subTable->indexer->indexType);

View file

@ -130,7 +130,8 @@ private:
// function funcname funcbody // function funcname funcbody
LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray<AstAttr*>& attributes = {nullptr, 0}); LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray<AstAttr*>& attributes = {nullptr, 0});
std::pair<bool, AstAttr::Type> validateAttribute(const char* attributeName, const TempVector<AstAttr*>& attributes); std::pair<bool, AstAttr::Type> validateAttribute_DEPRECATED(const char* attributeName, const TempVector<AstAttr*>& attributes);
std::optional<AstAttr::Type> validateAttribute(const char* attributeName, const TempVector<AstAttr*>& attributes);
// attribute ::= '@' NAME // attribute ::= '@' NAME
void parseAttribute(TempVector<AstAttr*>& attribute); void parseAttribute(TempVector<AstAttr*>& attribute);

View file

@ -24,6 +24,7 @@ LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
LUAU_FASTFLAGVARIABLE(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauStoreReturnTypesAsPackOnAst)
LUAU_FASTFLAGVARIABLE(LuauStoreLocalAnnotationColonPositions) LUAU_FASTFLAGVARIABLE(LuauStoreLocalAnnotationColonPositions)
LUAU_FASTFLAGVARIABLE(LuauCSTForReturnTypeFunctionTail) LUAU_FASTFLAGVARIABLE(LuauCSTForReturnTypeFunctionTail)
LUAU_FASTFLAGVARIABLE(LuauParseAttributeFixUninit)
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false) LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
@ -808,8 +809,10 @@ AstStat* Parser::parseFunctionStat(const AstArray<AstAttr*>& attributes)
} }
std::pair<bool, AstAttr::Type> Parser::validateAttribute(const char* attributeName, const TempVector<AstAttr*>& attributes) std::pair<bool, AstAttr::Type> Parser::validateAttribute_DEPRECATED(const char* attributeName, const TempVector<AstAttr*>& attributes)
{ {
LUAU_ASSERT(!FFlag::LuauParseAttributeFixUninit);
AstAttr::Type type; AstAttr::Type type;
// check if the attribute name is valid // check if the attribute name is valid
@ -848,6 +851,42 @@ std::pair<bool, AstAttr::Type> Parser::validateAttribute(const char* attributeNa
return {found, type}; return {found, type};
} }
std::optional<AstAttr::Type> Parser::validateAttribute(const char* attributeName, const TempVector<AstAttr*>& attributes)
{
LUAU_ASSERT(FFlag::LuauParseAttributeFixUninit);
// check if the attribute name is valid
std::optional<AstAttr::Type> 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 // attribute ::= '@' NAME
void Parser::parseAttribute(TempVector<AstAttr*>& attributes) void Parser::parseAttribute(TempVector<AstAttr*>& attributes)
{ {
@ -855,13 +894,26 @@ void Parser::parseAttribute(TempVector<AstAttr*>& attributes)
Location loc = lexer.current().location; Location loc = lexer.current().location;
if (FFlag::LuauParseAttributeFixUninit)
{
const char* name = lexer.current().name; const char* name = lexer.current().name;
const auto [found, type] = validateAttribute(name, attributes); std::optional<AstAttr::Type> type = validateAttribute(name, attributes);
nextLexeme();
if (type)
attributes.push_back(allocator.alloc<AstAttr>(loc, *type));
}
else
{
const char* name = lexer.current().name;
const auto [found, type] = validateAttribute_DEPRECATED(name, attributes);
nextLexeme(); nextLexeme();
if (found) if (found)
attributes.push_back(allocator.alloc<AstAttr>(loc, type)); attributes.push_back(allocator.alloc<AstAttr>(loc, type));
}
} }
// attributes ::= {attribute} // attributes ::= {attribute}

View file

@ -178,7 +178,7 @@ struct CliFileResolver : Luau::FileResolver
Luau::Require::ErrorHandler nullErrorHandler{}; Luau::Require::ErrorHandler nullErrorHandler{};
Luau::Require::Navigator navigator(navigationContext, 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; return std::nullopt;
if (!navigationContext.isModulePresent()) if (!navigationContext.isModulePresent())

View file

@ -66,7 +66,7 @@ int main(int argc, char** argv)
options.captureComments = true; options.captureComments = true;
options.allowDeclarationSyntax = 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) if (parseResult.errors.size() > 0)
{ {

View file

@ -490,8 +490,8 @@ int main(int argc, char** argv)
help(args); help(args);
} }
const std::string scriptName = argv[1]; std::string scriptName = argv[1];
const std::string appName = argv[2]; std::string appName = argv[2];
const std::string searchText = argv[3]; const std::string searchText = argv[3];
std::optional<std::string> source = readFile(scriptName); std::optional<std::string> source = readFile(scriptName);
@ -503,5 +503,5 @@ int main(int argc, char** argv)
} }
Reducer reducer; Reducer reducer;
reducer.run(scriptName, appName, *source, searchText); reducer.run(std::move(scriptName), std::move(appName), *source, searchText);
} }

View file

@ -41,6 +41,7 @@ struct IrBuilder
IrOp constInt(int value); IrOp constInt(int value);
IrOp constUint(unsigned value); IrOp constUint(unsigned value);
IrOp constImport(unsigned value);
IrOp constDouble(double value); IrOp constDouble(double value);
IrOp constTag(uint8_t value); IrOp constTag(uint8_t value);
IrOp constAny(IrConst constant, uint64_t asCommonKey); IrOp constAny(IrConst constant, uint64_t asCommonKey);

View file

@ -392,11 +392,19 @@ enum class IrCmd : uint8_t
// C: Rn or unsigned int (key) // C: Rn or unsigned int (key)
SET_TABLE, SET_TABLE,
// TODO: remove with FFlagLuauCodeGenSimplifyImport
// Lookup a value in the environment // Lookup a value in the environment
// A: Rn (where to store the result) // A: Rn (where to store the result)
// B: unsigned int (import path) // B: unsigned int (import path)
GET_IMPORT, 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 // Concatenate multiple TValues into a string
// A: Rn (value start) // A: Rn (value start)
// B: unsigned int (number of registers to go over) // B: unsigned int (number of registers to go over)
@ -763,6 +771,7 @@ enum class IrConstKind : uint8_t
Uint, Uint,
Double, Double,
Tag, Tag,
Import,
}; };
struct IrConst struct IrConst
@ -1129,6 +1138,14 @@ struct IrFunction
return value.valueUint; return value.valueUint;
} }
unsigned importOp(IrOp op)
{
IrConst& value = constOp(op);
CODEGEN_ASSERT(value.kind == IrConstKind::Import);
return value.valueUint;
}
std::optional<unsigned> asUintOp(IrOp op) std::optional<unsigned> asUintOp(IrOp op)
{ {
if (op.kind != IrOpKind::Constant) if (op.kind != IrOpKind::Constant)

View file

@ -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, const IrBlock& block, uint32_t index); // Block title
void toString(IrToStringContext& ctx, IrOp op); 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); const char* getBytecodeTypeName(uint8_t type, const char* const* userdataTypes);

View file

@ -68,6 +68,9 @@ static void visitVmRegDefsUses(T& visitor, IrFunction& function, const IrInst& i
case IrCmd::GET_IMPORT: case IrCmd::GET_IMPORT:
visitor.def(inst.a); visitor.def(inst.a);
break; break;
case IrCmd::GET_CACHED_IMPORT:
visitor.def(inst.a);
break;
case IrCmd::CONCAT: case IrCmd::CONCAT:
visitor.useRange(vmRegOp(inst.a), function.uintOp(inst.b)); visitor.useRange(vmRegOp(inst.a), function.uintOp(inst.b));

View file

@ -235,6 +235,14 @@ Udata* newUserdata(lua_State* L, size_t s, int tag)
return u; 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 // 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) Closure* callFallback(lua_State* L, StkId ra, StkId argtop, int nresults)
{ {

View file

@ -18,6 +18,7 @@ Closure* callProlog(lua_State* L, TValue* ra, StkId argtop, int nresults);
void callEpilogC(lua_State* L, int nresults, int n); void callEpilogC(lua_State* L, int nresults, int n);
Udata* newUserdata(lua_State* L, size_t s, int tag); 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 #define CALL_FALLBACK_YIELD 1

View file

@ -676,6 +676,14 @@ IrOp IrBuilder::constUint(unsigned value)
return constAny(constant, uint64_t(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) IrOp IrBuilder::constDouble(double value)
{ {
IrConst constant; IrConst constant;

View file

@ -251,6 +251,8 @@ const char* getCmdName(IrCmd cmd)
return "SET_TABLE"; return "SET_TABLE";
case IrCmd::GET_IMPORT: case IrCmd::GET_IMPORT:
return "GET_IMPORT"; return "GET_IMPORT";
case IrCmd::GET_CACHED_IMPORT:
return "GET_CACHED_IMPORT";
case IrCmd::CONCAT: case IrCmd::CONCAT:
return "CONCAT"; return "CONCAT";
case IrCmd::GET_UPVALUE: case IrCmd::GET_UPVALUE:
@ -501,7 +503,7 @@ void toString(IrToStringContext& ctx, IrOp op)
append(ctx.result, "undef"); append(ctx.result, "undef");
break; break;
case IrOpKind::Constant: case IrOpKind::Constant:
toString(ctx.result, ctx.constants[op.index]); toString(ctx.result, ctx.proto, ctx.constants[op.index]);
break; break;
case IrOpKind::Condition: case IrOpKind::Condition:
CODEGEN_ASSERT(op.index < uint32_t(IrCondition::Count)); 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) switch (constant.kind)
{ {
@ -558,6 +560,36 @@ void toString(std::string& result, IrConst constant)
case IrConstKind::Tag: case IrConstKind::Tag:
result.append(getTagName(constant.valueTag)); result.append(getTagName(constant.valueTag));
break; 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;
} }
} }

View file

@ -1382,6 +1382,47 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
emitUpdateBase(build); emitUpdateBase(build);
break; 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: case IrCmd::CONCAT:
regs.spill(build, index); regs.spill(build, index);
build.mov(x0, rState); build.mov(x0, rState);
@ -2720,6 +2761,11 @@ unsigned IrLoweringA64::uintOp(IrOp op) const
return function.uintOp(op); return function.uintOp(op);
} }
unsigned IrLoweringA64::importOp(IrOp op) const
{
return function.importOp(op);
}
double IrLoweringA64::doubleOp(IrOp op) const double IrLoweringA64::doubleOp(IrOp op) const
{ {
return function.doubleOp(op); return function.doubleOp(op);

View file

@ -54,6 +54,7 @@ struct IrLoweringA64
uint8_t tagOp(IrOp op) const; uint8_t tagOp(IrOp op) const;
int intOp(IrOp op) const; int intOp(IrOp op) const;
unsigned uintOp(IrOp op) const; unsigned uintOp(IrOp op) const;
unsigned importOp(IrOp op) const;
double doubleOp(IrOp op) const; double doubleOp(IrOp op) const;
IrBlock& blockOp(IrOp op) const; IrBlock& blockOp(IrOp op) const;

View file

@ -1219,6 +1219,36 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
emitUpdateBase(build); emitUpdateBase(build);
break; 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: case IrCmd::CONCAT:
{ {
IrCallWrapperX64 callWrap(regs, build, index); IrCallWrapperX64 callWrap(regs, build, index);
@ -2350,6 +2380,11 @@ unsigned IrLoweringX64::uintOp(IrOp op) const
return function.uintOp(op); return function.uintOp(op);
} }
unsigned IrLoweringX64::importOp(IrOp op) const
{
return function.importOp(op);
}
double IrLoweringX64::doubleOp(IrOp op) const double IrLoweringX64::doubleOp(IrOp op) const
{ {
return function.doubleOp(op); return function.doubleOp(op);

View file

@ -57,6 +57,7 @@ struct IrLoweringX64
uint8_t tagOp(IrOp op) const; uint8_t tagOp(IrOp op) const;
int intOp(IrOp op) const; int intOp(IrOp op) const;
unsigned uintOp(IrOp op) const; unsigned uintOp(IrOp op) const;
unsigned importOp(IrOp op) const;
double doubleOp(IrOp op) const; double doubleOp(IrOp op) const;
IrBlock& blockOp(IrOp op) const; IrBlock& blockOp(IrOp op) const;

View file

@ -12,6 +12,8 @@
#include "lstate.h" #include "lstate.h"
#include "ltm.h" #include "ltm.h"
LUAU_FASTFLAGVARIABLE(LuauCodeGenSimplifyImport)
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -1215,6 +1217,13 @@ void translateInstGetImport(IrBuilder& build, const Instruction* pc, int pcpos)
int k = LUAU_INSN_D(*pc); int k = LUAU_INSN_D(*pc);
uint32_t aux = pc[1]; uint32_t aux = pc[1];
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 fastPath = build.block(IrBlockKind::Internal);
IrOp fallback = build.block(IrBlockKind::Fallback); IrOp fallback = build.block(IrBlockKind::Fallback);
@ -1236,6 +1245,7 @@ void translateInstGetImport(IrBuilder& build, const Instruction* pc, int pcpos)
build.inst(IrCmd::SET_SAVEDPC, build.constUint(pcpos + 1)); build.inst(IrCmd::SET_SAVEDPC, build.constUint(pcpos + 1));
build.inst(IrCmd::GET_IMPORT, build.vmReg(ra), build.constUint(aux)); build.inst(IrCmd::GET_IMPORT, build.vmReg(ra), build.constUint(aux));
build.inst(IrCmd::JUMP, next); build.inst(IrCmd::JUMP, next);
}
} }
void translateInstGetTableKS(IrBuilder& build, const Instruction* pc, int pcpos) void translateInstGetTableKS(IrBuilder& build, const Instruction* pc, int pcpos)

View file

@ -240,6 +240,7 @@ IrValueKind getCmdValueKind(IrCmd cmd)
case IrCmd::GET_TABLE: case IrCmd::GET_TABLE:
case IrCmd::SET_TABLE: case IrCmd::SET_TABLE:
case IrCmd::GET_IMPORT: case IrCmd::GET_IMPORT:
case IrCmd::GET_CACHED_IMPORT:
case IrCmd::CONCAT: case IrCmd::CONCAT:
case IrCmd::GET_UPVALUE: case IrCmd::GET_UPVALUE:
case IrCmd::SET_UPVALUE: case IrCmd::SET_UPVALUE:

View file

@ -55,6 +55,7 @@ void IrValueLocationTracking::beforeInstLowering(IrInst& inst)
case IrCmd::DO_LEN: case IrCmd::DO_LEN:
case IrCmd::GET_TABLE: case IrCmd::GET_TABLE:
case IrCmd::GET_IMPORT: case IrCmd::GET_IMPORT:
case IrCmd::GET_CACHED_IMPORT:
invalidateRestoreOp(inst.a, /*skipValueInvalidation*/ false); invalidateRestoreOp(inst.a, /*skipValueInvalidation*/ false);
break; break;
case IrCmd::CONCAT: case IrCmd::CONCAT:

View file

@ -90,6 +90,7 @@ void initFunctions(NativeContext& context)
context.callProlog = callProlog; context.callProlog = callProlog;
context.callEpilogC = callEpilogC; context.callEpilogC = callEpilogC;
context.newUserdata = newUserdata; context.newUserdata = newUserdata;
context.getImport = getImport;
context.callFallback = callFallback; context.callFallback = callFallback;

View file

@ -94,6 +94,7 @@ struct NativeContext
Closure* (*callProlog)(lua_State* L, TValue* ra, StkId argtop, int nresults) = nullptr; Closure* (*callProlog)(lua_State* L, TValue* ra, StkId argtop, int nresults) = nullptr;
void (*callEpilogC)(lua_State* L, int nresults, int n) = nullptr; void (*callEpilogC)(lua_State* L, int nresults, int n) = nullptr;
Udata* (*newUserdata)(lua_State* L, size_t s, int tag) = 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; Closure* (*callFallback)(lua_State* L, StkId ra, StkId argtop, int nresults) = nullptr;

View file

@ -1539,6 +1539,13 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
state.invalidate(inst.a); state.invalidate(inst.a);
state.invalidateUserCall(); state.invalidateUserCall();
break; 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: case IrCmd::CONCAT:
state.invalidateRegisterRange(vmRegOp(inst.a), function.uintOp(inst.b)); 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 state.invalidateUserCall(); // TODO: if only strings and numbers are concatenated, there will be no user calls

View file

@ -767,6 +767,7 @@ static void markDeadStoresInInst(RemoveDeadStoreState& state, IrBuilder& build,
case IrCmd::GET_TABLE: case IrCmd::GET_TABLE:
case IrCmd::SET_TABLE: case IrCmd::SET_TABLE:
case IrCmd::GET_IMPORT: case IrCmd::GET_IMPORT:
case IrCmd::GET_CACHED_IMPORT:
case IrCmd::CONCAT: case IrCmd::CONCAT:
case IrCmd::INTERRUPT: case IrCmd::INTERRUPT:
case IrCmd::CHECK_GC: case IrCmd::CHECK_GC:

View file

@ -39,10 +39,26 @@ message Local {
required int32 name = 1; required int32 name = 1;
} }
message Typename { message RegularTypeName {
required int32 index = 1; 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 { message Name {
oneof name_oneof { oneof name_oneof {
int32 builtin = 1; int32 builtin = 1;
@ -97,8 +113,8 @@ message ExprIndexExpr {
} }
message ExprFunction { message ExprFunction {
repeated Typename generics = 1; repeated GenericTypeName generics = 1;
repeated Typename genericpacks = 2; repeated GenericTypeName genericpacks = 2;
repeated Local args = 3; repeated Local args = 3;
required bool vararg = 4; required bool vararg = 4;
required StatBlock body = 5; required StatBlock body = 5;
@ -195,6 +211,7 @@ message Stat {
StatLocalFunction local_function = 15; StatLocalFunction local_function = 15;
StatTypeAlias type_alias = 16; StatTypeAlias type_alias = 16;
StatRequireIntoLocalHelper require_into_local = 17; StatRequireIntoLocalHelper require_into_local = 17;
StatTypeFunction type_function = 18;
} }
} }
@ -289,10 +306,16 @@ message StatLocalFunction {
message StatTypeAlias { message StatTypeAlias {
required bool export = 1; required bool export = 1;
required Typename name = 2; required RegularTypeName name = 2;
required Type type = 3; required Type type = 3;
repeated Typename generics = 4; repeated GenericTypeName generics = 4;
repeated Typename genericpacks = 5; repeated GenericTypeName genericpacks = 5;
}
message StatTypeFunction {
required bool export = 1;
required RegularTypeName name = 2;
required ExprFunction func = 3;
} }
message StatRequireIntoLocalHelper { message StatRequireIntoLocalHelper {
@ -309,7 +332,7 @@ message Type {
TypeTypeof typeof = 5; TypeTypeof typeof = 5;
TypeUnion union = 6; TypeUnion union = 6;
TypeIntersection intersection = 7; TypeIntersection intersection = 7;
TypeClass class = 8; TypeExtern extern = 8;
TypeRef ref = 9; TypeRef ref = 9;
TypeBoolean boolean = 10; TypeBoolean boolean = 10;
TypeString string = 11; TypeString string = 11;
@ -321,19 +344,26 @@ message TypePrimitive {
} }
message TypeLiteral { message TypeLiteral {
required Typename name = 1; required TypeName name = 1;
repeated Type generics = 2; repeated Type generics = 2;
repeated Typename genericpacks = 3; repeated GenericTypeName genericpacks = 3;
}
enum TableFieldAccess {
Read = 1;
Write = 2;
} }
message TypeTableItem { message TypeTableItem {
required Name key = 1; optional TableFieldAccess access = 1;
required Type type = 2; required Name key = 2;
required Type type = 3;
} }
message TypeTableIndexer { message TypeTableIndexer {
required Type key = 1; optional TableFieldAccess access = 1;
required Type value = 2; required Type key = 2;
required Type value = 3;
} }
message TypeTable { message TypeTable {
@ -342,8 +372,8 @@ message TypeTable {
} }
message TypeFunction { message TypeFunction {
repeated Typename generics = 1; repeated GenericTypeName generics = 1;
repeated Typename genericpacks = 2; repeated GenericTypeName genericpacks = 2;
repeated Type args = 3; repeated Type args = 3;
repeated Type rets = 4; repeated Type rets = 4;
// TODO: vararg? // TODO: vararg?
@ -363,13 +393,13 @@ message TypeIntersection {
required Type right = 2; required Type right = 2;
} }
message TypeClass { message TypeExtern {
required int32 kind = 1; required int32 kind = 1;
} }
message TypeRef { message TypeRef {
required Local prefix = 1; required Local prefix = 1;
required Typename index = 2; required TypeName index = 2;
} }
message TypeBoolean { message TypeBoolean {

View file

@ -22,7 +22,8 @@ static const std::string kNames[] = {
"sub", "table", "tan", "tanh", "thread", "time", "tonumber", "tostring", "tostring", "sub", "table", "tan", "tanh", "thread", "time", "tonumber", "tostring", "tostring",
"traceback", "type", "typeof", "unpack", "upper", "userdata", "utf8", "vector", "wrap", "traceback", "type", "typeof", "unpack", "upper", "userdata", "utf8", "vector", "wrap",
"writef32", "writef64", "writei16", "writei32", "writei8", "writestring", "writeu16", "writeu32", "writeu8", "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[] = { static const std::string kTypes[] = {
@ -34,6 +35,8 @@ static const std::string kTypes[] = {
"string", "string",
"thread", "thread",
"vector", "vector",
"unknown",
"never",
}; };
static const std::string kExternTypes[] = { static const std::string kExternTypes[] = {
@ -42,6 +45,28 @@ static const std::string kExternTypes[] = {
"Part", "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 ProtoToLuau
{ {
struct Function struct Function
@ -79,12 +104,35 @@ struct ProtoToLuau
} }
} }
void ident(const luau::Typename& name) void ident(const luau::RegularTypeName& name)
{ {
source += 't'; source += 't';
source += std::to_string(name.index() & 0xff); 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<typename T> template<typename T>
void genericidents(const T& node) void genericidents(const T& node)
{ {
@ -480,6 +528,8 @@ struct ProtoToLuau
print(stat.type_alias()); print(stat.type_alias());
else if (stat.has_require_into_local()) else if (stat.has_require_into_local())
print(stat.require_into_local()); print(stat.require_into_local());
else if (stat.has_type_function())
print(stat.type_function());
else else
source += "do end\n"; source += "do end\n";
} }
@ -768,6 +818,17 @@ struct ProtoToLuau
source += " = require(module" + std::to_string(stat.modulenum() % 2) + ")\n"; 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) void print(const luau::Type& type)
{ {
if (type.has_primitive()) if (type.has_primitive())
@ -784,8 +845,8 @@ struct ProtoToLuau
print(type.union_()); print(type.union_());
else if (type.has_intersection()) else if (type.has_intersection())
print(type.intersection()); print(type.intersection());
else if (type.has_class_()) else if (type.has_extern_())
print(type.class_()); print(type.extern_());
else if (type.has_ref()) else if (type.has_ref())
print(type.ref()); print(type.ref());
else if (type.has_boolean()) 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) void print(const luau::TypeTable& type)
{ {
source += '{'; source += '{';
for (size_t i = 0; i < type.items_size(); ++i) 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 += ':'; source += ':';
print(type.items(i).type()); print(item.type());
source += ','; source += ',';
} }
if (type.has_indexer()) if (type.has_indexer())
{ {
auto& indexer = type.indexer();
if (indexer.has_access())
{
print(indexer.access());
source += ' ';
}
source += '['; source += '[';
print(type.indexer().key()); print(indexer.key());
source += "]:"; source += "]:";
print(type.indexer().value()); print(indexer.value());
} }
source += '}'; source += '}';
} }
@ -900,7 +985,7 @@ struct ProtoToLuau
source += ')'; source += ')';
} }
void print(const luau::TypeClass& type) void print(const luau::TypeExtern& type)
{ {
size_t index = size_t(type.kind()) % std::size(kExternTypes); size_t index = size_t(type.kind()) % std::size(kExternTypes);
source += kExternTypes[index]; source += kExternTypes[index];

View file

@ -19,8 +19,9 @@
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2) LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauExpectedTypeVisitor) LUAU_FASTFLAG(LuauExpectedTypeVisitor)
LUAU_FASTFLAG(LuauImplicitTableIndexerKeys)
using namespace Luau; using namespace Luau;
@ -45,9 +46,9 @@ struct ACFixtureImpl : BaseType
// NOTE: Autocomplete does *not* require strict checking, meaning we should // NOTE: Autocomplete does *not* require strict checking, meaning we should
// try to check all of these examples in `--!nocheck` mode. // try to check all of these examples in `--!nocheck` mode.
this->configResolver.defaultConfig.mode = Mode::NoCheck; 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) AutocompleteResult autocomplete(char marker, StringCompletionCallback callback = nullCallback)
@ -58,9 +59,9 @@ struct ACFixtureImpl : BaseType
// NOTE: Autocomplete does *not* require strict checking, meaning we should // NOTE: Autocomplete does *not* require strict checking, meaning we should
// try to check all of these examples in `--!nocheck` mode. // try to check all of these examples in `--!nocheck` mode.
this->configResolver.defaultConfig.mode = Mode::NoCheck; 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) 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 // NOTE: Autocomplete does *not* require strict checking, meaning we should
// try to check all of these examples in `--!nocheck` mode. // try to check all of these examples in `--!nocheck` mode.
this->configResolver.defaultConfig.mode = Mode::NoCheck; 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) CheckResult check(const std::string& source)
@ -120,18 +121,18 @@ struct ACFixtureImpl : BaseType
LoadDefinitionFileResult loadDefinition(const std::string& source) LoadDefinitionFileResult loadDefinition(const std::string& source)
{ {
GlobalTypes& globals = this->frontend.globalsForAutocomplete; GlobalTypes& globals = this->getFrontend().globalsForAutocomplete;
unfreeze(globals.globalTypes); unfreeze(globals.globalTypes);
LoadDefinitionFileResult result = this->frontend.loadDefinitionFile( LoadDefinitionFileResult result = this->getFrontend().loadDefinitionFile(
globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typeCheckForAutocomplete */ true globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typeCheckForAutocomplete */ true
); );
freeze(globals.globalTypes); freeze(globals.globalTypes);
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2)
{ {
GlobalTypes& globals = this->frontend.globals; GlobalTypes& globals = this->getFrontend().globals;
unfreeze(globals.globalTypes); unfreeze(globals.globalTypes);
LoadDefinitionFileResult result = this->frontend.loadDefinitionFile( LoadDefinitionFileResult result = this->getFrontend().loadDefinitionFile(
globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typeCheckForAutocomplete */ true globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typeCheckForAutocomplete */ true
); );
freeze(globals.globalTypes); freeze(globals.globalTypes);
@ -156,10 +157,11 @@ struct ACFixture : ACFixtureImpl<Fixture>
ACFixture() ACFixture()
: ACFixtureImpl<Fixture>() : ACFixtureImpl<Fixture>()
{ {
addGlobalBinding(frontend.globals, "table", Binding{builtinTypes->anyType}); // TODO - move this into its own consructor
addGlobalBinding(frontend.globals, "math", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globals, "table", Binding{getBuiltins()->anyType});
addGlobalBinding(frontend.globalsForAutocomplete, "table", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globals, "math", Binding{getBuiltins()->anyType});
addGlobalBinding(frontend.globalsForAutocomplete, "math", Binding{builtinTypes->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 {} return {}
)"; )";
LUAU_REQUIRE_NO_ERRORS(frontend.check("Module/A")); LUAU_REQUIRE_NO_ERRORS(getFrontend().check("Module/A"));
fileResolver.source["Module/B"] = R"( fileResolver.source["Module/B"] = R"(
local aaa = require(script.Parent.A) local aaa = require(script.Parent.A)
local a: aa local a: aa
)"; )";
frontend.check("Module/B"); getFrontend().check("Module/B");
auto ac = autocomplete("Module/B", Position{2, 11}); auto ac = autocomplete("Module/B", Position{2, 11});
@ -1411,14 +1413,14 @@ export type B = { z: number, w: number }
return {} return {}
)"; )";
LUAU_REQUIRE_NO_ERRORS(frontend.check("Module/A")); LUAU_REQUIRE_NO_ERRORS(getFrontend().check("Module/A"));
fileResolver.source["Module/B"] = R"( fileResolver.source["Module/B"] = R"(
local aaa = require(script.Parent.A) local aaa = require(script.Parent.A)
local a: aaa. local a: aaa.
)"; )";
frontend.check("Module/B"); getFrontend().check("Module/B");
auto ac = autocomplete("Module/B", Position{2, 13}); 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} 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"( fileResolver.source["Module/B"] = R"(
local ex = require(script.Parent.A) local ex = require(script.Parent.A)
ex.a(function(x: ex.a(function(x:
)"; )";
frontend.check("Module/B"); getFrontend().check("Module/B");
auto ac = autocomplete("Module/B", Position{2, 16}); auto ac = autocomplete("Module/B", Position{2, 16});
@ -2089,7 +2091,7 @@ local ex = require(script.Parent.A)
ex.b(function(x: ex.b(function(x:
)"; )";
frontend.check("Module/C"); getFrontend().check("Module/C");
ac = autocomplete("Module/C", Position{2, 16}); 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} 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"( fileResolver.source["Module/B"] = R"(
local ex = require(script.Parent.A) local ex = require(script.Parent.A)
ex.a(function(x: ex.a(function(x:
)"; )";
frontend.check("Module/B"); getFrontend().check("Module/B");
auto ac = autocomplete("Module/B", Position{2, 16}); auto ac = autocomplete("Module/B", Position{2, 16});
@ -2125,7 +2127,7 @@ local ex = require(script.Parent.A)
ex.b(function(x: ex.b(function(x:
)"; )";
frontend.check("Module/C"); getFrontend().check("Module/C");
ac = autocomplete("Module/C", Position{2, 16}); ac = autocomplete("Module/C", Position{2, 16});
@ -2441,14 +2443,14 @@ export type other = { z: number, w: number }
return {} return {}
)"; )";
LUAU_REQUIRE_NO_ERRORS(frontend.check("Module/A")); LUAU_REQUIRE_NO_ERRORS(getFrontend().check("Module/A"));
fileResolver.source["Module/B"] = R"( fileResolver.source["Module/B"] = R"(
local aaa = require(script.Parent.A) local aaa = require(script.Parent.A)
local a: aaa.do local a: aaa.do
)"; )";
frontend.check("Module/B"); getFrontend().check("Module/B");
auto ac = autocomplete("Module/B", Position{2, 15}); 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")); 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 if (FFlag::LuauSolverV2)
// (overloads and singletons)
return; 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"( check(R"(
type tag = "cat" | "dog" type tag = "cat" | "dog"
local function f(a: tag) end 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("cat"));
CHECK(ac.entryMap.count("dog")); CHECK(ac.entryMap.count("dog"));
CHECK_EQ(ac.context, AutocompleteContext::String); 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") TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key_iso")
@ -3705,7 +3713,7 @@ local a = { x = 2, y = 4 }
a.@1 a.@1
)"); )");
frontend.clear(); getFrontend().clear();
auto ac = autocomplete('1'); auto ac = autocomplete('1');
@ -3714,21 +3722,21 @@ a.@1
CHECK(ac.entryMap.count("x")); CHECK(ac.entryMap.count("x"));
CHECK(ac.entryMap.count("y")); CHECK(ac.entryMap.count("y"));
frontend.check("MainModule", {}); getFrontend().check("MainModule", {});
ac = autocomplete('1'); ac = autocomplete('1');
CHECK(ac.entryMap.count("x")); CHECK(ac.entryMap.count("x"));
CHECK(ac.entryMap.count("y")); CHECK(ac.entryMap.count("y"));
frontend.markDirty("MainModule", nullptr); getFrontend().markDirty("MainModule", nullptr);
ac = autocomplete('1'); ac = autocomplete('1');
CHECK(ac.entryMap.count("x")); CHECK(ac.entryMap.count("x"));
CHECK(ac.entryMap.count("y")); CHECK(ac.entryMap.count("y"));
frontend.check("MainModule", {}); getFrontend().check("MainModule", {});
ac = autocomplete('1'); ac = autocomplete('1');
@ -3763,7 +3771,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback")
declare function require(path: string): any declare function require(path: string): any
)"); )");
GlobalTypes& globals = FFlag::LuauSolverV2 ? frontend.globals : frontend.globalsForAutocomplete; GlobalTypes& globals = FFlag::LuauSolverV2 ? getFrontend().globals : getFrontend().globalsForAutocomplete;
std::optional<Binding> require = globals.globalScope->linearSearchForBinding("require"); std::optional<Binding> require = globals.globalScope->linearSearchForBinding("require");
REQUIRE(require); REQUIRE(require);
@ -3944,7 +3952,7 @@ local a: T@1
CHECK_EQ(ac.context, AutocompleteContext::Type); 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"( loadDefinition(R"(
declare class Instance declare class Instance
@ -3970,7 +3978,7 @@ TEST_CASE_FIXTURE(ACFixture, "string_completion_outside_quotes")
declare function require(path: string): any declare function require(path: string): any
)"); )");
GlobalTypes& globals = FFlag::LuauSolverV2 ? frontend.globals : frontend.globalsForAutocomplete; GlobalTypes& globals = FFlag::LuauSolverV2 ? getFrontend().globals : getFrontend().globalsForAutocomplete;
std::optional<Binding> require = globals.globalScope->linearSearchForBinding("require"); std::optional<Binding> require = globals.globalScope->linearSearchForBinding("require");
REQUIRE(require); REQUIRE(require);
@ -4468,9 +4476,9 @@ TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
auto ac = autocomplete('1'); 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 // sets some flags
CHECK(ac.entryMap.count("BaseMethod") > 0); CHECK(ac.entryMap.count("BaseMethod") > 0);
CHECK(ac.entryMap.count("Method") > 0); CHECK(ac.entryMap.count("Method") > 0);
@ -4501,8 +4509,8 @@ TEST_CASE_FIXTURE(ACBuiltinsFixture, "type_function_private_scope")
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
// Global scope polution by the embedder has no effect // Global scope polution by the embedder has no effect
addGlobalBinding(frontend.globals, "thisAlsoShouldNotBeThere", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globals, "thisAlsoShouldNotBeThere", Binding{getBuiltins()->anyType});
addGlobalBinding(frontend.globalsForAutocomplete, "thisAlsoShouldNotBeThere", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globalsForAutocomplete, "thisAlsoShouldNotBeThere", Binding{getBuiltins()->anyType});
check(R"( check(R"(
local function thisShouldNotBeThere() end local function thisShouldNotBeThere() end
@ -4607,4 +4615,29 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_in_type_assertion")
CHECK_EQ(ac2.entryMap.count("prop"), 1); 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(); TEST_SUITE_END();

View file

@ -12,9 +12,9 @@ TEST_SUITE_BEGIN("BuiltinDefinitionsTest");
TEST_CASE_FIXTURE(BuiltinsFixture, "lib_documentation_symbols") 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 nameString(name.c_str());
std::string expectedRootSymbol = "@luau/global/" + nameString; std::string expectedRootSymbol = "@luau/global/" + nameString;

View file

@ -12,10 +12,17 @@ namespace Luau
ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete) ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
: BuiltinsFixture(prepareAutocomplete) : BuiltinsFixture(prepareAutocomplete)
{ {
GlobalTypes& globals = frontend.globals;
}
Frontend& ExternTypeFixture::getFrontend()
{
Frontend& f = BuiltinsFixture::getFrontend();
GlobalTypes& globals = f.globals;
TypeArena& arena = globals.globalTypes; TypeArena& arena = globals.globalTypes;
TypeId numberType = builtinTypes->numberType; TypeId numberType = getBuiltins()->numberType;
TypeId stringType = builtinTypes->stringType; TypeId stringType = getBuiltins()->stringType;
unfreeze(arena); unfreeze(arena);
@ -30,8 +37,7 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
}; };
getMutable<ExternType>(connectionType)->props = { getMutable<ExternType>(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", {}}); TypeId baseClassType = arena.addType(ExternType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
getMutable<ExternType>(baseClassType)->props = { getMutable<ExternType>(baseClassType)->props = {
@ -107,9 +113,8 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
{"__mul", {"__mul",
{arena.addType(IntersectionType{{ {arena.addType(IntersectionType{{
makeFunction(arena, vector2InstanceType, {vector2InstanceType}, {vector2InstanceType}), makeFunction(arena, vector2InstanceType, {vector2InstanceType}, {vector2InstanceType}),
makeFunction(arena, vector2InstanceType, {builtinTypes->numberType}, {vector2InstanceType}), makeFunction(arena, vector2InstanceType, {getBuiltins()->numberType}, {vector2InstanceType}),
}})}} }})}}};
};
globals.globalScope->exportedTypeBindings["Vector2"] = TypeFun{{}, vector2InstanceType}; globals.globalScope->exportedTypeBindings["Vector2"] = TypeFun{{}, vector2InstanceType};
addGlobalBinding(globals, "Vector2", vector2Type, "@test"); addGlobalBinding(globals, "Vector2", vector2Type, "@test");
@ -146,6 +151,7 @@ ExternTypeFixture::ExternTypeFixture(bool prepareAutocomplete)
persist(tf.type); persist(tf.type);
freeze(arena); freeze(arena);
return *frontend;
} }
} // namespace Luau } // namespace Luau

View file

@ -10,6 +10,8 @@ struct ExternTypeFixture : BuiltinsFixture
{ {
explicit ExternTypeFixture(bool prepareAutocomplete = false); explicit ExternTypeFixture(bool prepareAutocomplete = false);
Frontend& getFrontend() override;
TypeId vector2Type; TypeId vector2Type;
TypeId vector2InstanceType; TypeId vector2InstanceType;
}; };

View file

@ -10,9 +10,10 @@ namespace Luau
ConstraintGeneratorFixture::ConstraintGeneratorFixture() ConstraintGeneratorFixture::ConstraintGeneratorFixture()
: Fixture() : Fixture()
, mainModule(new Module) , mainModule(new Module)
, simplifier(newSimplifier(NotNull{&arena}, builtinTypes)) , simplifier(newSimplifier(NotNull{&arena}, getBuiltins()))
, forceTheFlag{FFlag::LuauSolverV2, true} , forceTheFlag{FFlag::LuauSolverV2, true}
{ {
getFrontend(); // Force the frontend to exist in the constructor.
mainModule->name = "MainModule"; mainModule->name = "MainModule";
mainModule->humanReadableName = "MainModule"; mainModule->humanReadableName = "MainModule";
@ -31,10 +32,10 @@ void ConstraintGeneratorFixture::generateConstraints(const std::string& code)
NotNull{simplifier.get()}, NotNull{simplifier.get()},
NotNull{&typeFunctionRuntime}, NotNull{&typeFunctionRuntime},
NotNull(&moduleResolver), NotNull(&moduleResolver),
builtinTypes, getBuiltins(),
NotNull(&ice), NotNull(&ice),
frontend.globals.globalScope, getFrontend().globals.globalScope,
frontend.globals.globalTypeFunctionScope, getFrontend().globals.globalTypeFunctionScope,
/*prepareModuleScope*/ nullptr, /*prepareModuleScope*/ nullptr,
&logger, &logger,
NotNull{dfg.get()}, NotNull{dfg.get()},
@ -60,8 +61,7 @@ void ConstraintGeneratorFixture::solve(const std::string& code)
{}, {},
&logger, &logger,
NotNull{dfg.get()}, NotNull{dfg.get()},
{} {}};
};
cs.run(); cs.run();
} }

View file

@ -20,7 +20,7 @@ struct ConstraintGeneratorFixture : Fixture
ModulePtr mainModule; ModulePtr mainModule;
DcrLogger logger; DcrLogger logger;
UnifierSharedState sharedState{&ice}; UnifierSharedState sharedState{&ice};
Normalizer normalizer{&arena, builtinTypes, NotNull{&sharedState}}; Normalizer normalizer{&arena, getBuiltins(), NotNull{&sharedState}};
SimplifierPtr simplifier; SimplifierPtr simplifier;
TypeCheckLimits limits; TypeCheckLimits limits;
TypeFunctionRuntime typeFunctionRuntime{NotNull{&ice}, NotNull{&limits}}; TypeFunctionRuntime typeFunctionRuntime{NotNull{&ice}, NotNull{&limits}};

View file

@ -25,17 +25,17 @@ struct ESFixture : Fixture
TypeId genericU = arena_.addType(GenericType{"U"}); TypeId genericU = arena_.addType(GenericType{"U"});
TypeId numberToString = 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 = 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() 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; parentClass = moduleScope->linearSearchForBinding("Parent")->typeId;
childClass = moduleScope->linearSearchForBinding("Child")->typeId; childClass = moduleScope->linearSearchForBinding("Child")->typeId;
@ -60,116 +60,116 @@ TEST_SUITE_BEGIN("EqSatSimplification");
TEST_CASE_FIXTURE(ESFixture, "primitive") TEST_CASE_FIXTURE(ESFixture, "primitive")
{ {
CHECK("number" == simplifyStr(builtinTypes->numberType)); CHECK("number" == simplifyStr(getBuiltins()->numberType));
} }
TEST_CASE_FIXTURE(ESFixture, "number | number") 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)); CHECK("number" == simplifyStr(ty));
} }
TEST_CASE_FIXTURE(ESFixture, "number | string") 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") TEST_CASE_FIXTURE(ESFixture, "t1 where t1 = number | t1")
{ {
TypeId ty = arena->freshType(builtinTypes, nullptr); TypeId ty = arena->freshType(getBuiltins(), nullptr);
asMutable(ty)->ty.emplace<UnionType>(std::vector<TypeId>{builtinTypes->numberType, ty}); asMutable(ty)->ty.emplace<UnionType>(std::vector<TypeId>{getBuiltins()->numberType, ty});
CHECK("number" == simplifyStr(ty)); CHECK("number" == simplifyStr(ty));
} }
TEST_CASE_FIXTURE(ESFixture, "number | string | number") 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)); CHECK("number | string" == simplifyStr(ty));
} }
TEST_CASE_FIXTURE(ESFixture, "string | (number | string) | number") TEST_CASE_FIXTURE(ESFixture, "string | (number | string) | number")
{ {
TypeId u1 = arena->addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType}}); TypeId u1 = arena->addType(UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}});
TypeId u2 = arena->addType(UnionType{{builtinTypes->stringType, u1, builtinTypes->numberType}}); TypeId u2 = arena->addType(UnionType{{getBuiltins()->stringType, u1, getBuiltins()->numberType}});
CHECK("number | string" == simplifyStr(u2)); CHECK("number | string" == simplifyStr(u2));
} }
TEST_CASE_FIXTURE(ESFixture, "string | any") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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") 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)") TEST_CASE_FIXTURE(ESFixture, "string & (unknown | never)")
{ {
CHECK( CHECK(
"string" == simplifyStr(arena->addType( "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") 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)") TEST_CASE_FIXTURE(ESFixture, "t1 where t1 = string & (number | t1)")
{ {
TypeId intersectionTy = arena->addType(BlockedType{}); 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<IntersectionType>(std::vector<TypeId>{builtinTypes->stringType, unionTy}); asMutable(intersectionTy)->ty.emplace<IntersectionType>(std::vector<TypeId>{getBuiltins()->stringType, unionTy});
CHECK("string" == simplifyStr(intersectionTy)); 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)") TEST_CASE_FIXTURE(ESFixture, "t1 where t1 = string & (unknown | t1)")
{ {
TypeId intersectionTy = arena->addType(BlockedType{}); 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<IntersectionType>(std::vector<TypeId>{builtinTypes->stringType, unionTy}); asMutable(intersectionTy)->ty.emplace<IntersectionType>(std::vector<TypeId>{getBuiltins()->stringType, unionTy});
CHECK("string" == simplifyStr(intersectionTy)); CHECK("string" == simplifyStr(intersectionTy));
} }
TEST_CASE_FIXTURE(ESFixture, "error | unknown") 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") 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\"") TEST_CASE_FIXTURE(ESFixture, "\"hello\" | \"world\" | \"hello\"")
@ -227,22 +227,22 @@ TEST_CASE_FIXTURE(ESFixture, "nil | boolean | number | string | thread | functio
{ {
CHECK( CHECK(
"unknown" == simplifyStr(arena->addType(UnionType{{ "unknown" == simplifyStr(arena->addType(UnionType{{
builtinTypes->nilType, getBuiltins()->nilType,
builtinTypes->booleanType, getBuiltins()->booleanType,
builtinTypes->numberType, getBuiltins()->numberType,
builtinTypes->stringType, getBuiltins()->stringType,
builtinTypes->threadType, getBuiltins()->threadType,
builtinTypes->functionType, getBuiltins()->functionType,
builtinTypes->tableType, getBuiltins()->tableType,
builtinTypes->externType, getBuiltins()->externType,
builtinTypes->bufferType, getBuiltins()->bufferType,
}})) }}))
); );
} }
TEST_CASE_FIXTURE(ESFixture, "Parent & number") 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") TEST_CASE_FIXTURE(ESFixture, "Child & Parent")
@ -262,12 +262,12 @@ TEST_CASE_FIXTURE(ESFixture, "Child | Parent")
TEST_CASE_FIXTURE(ESFixture, "class | Child") 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") 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") TEST_CASE_FIXTURE(ESFixture, "Parent | Unrelated")
@ -277,16 +277,16 @@ TEST_CASE_FIXTURE(ESFixture, "Parent | Unrelated")
TEST_CASE_FIXTURE(ESFixture, "never | 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") TEST_CASE_FIXTURE(ESFixture, "never | Parent | (number & string) | Unrelated")
{ {
CHECK( CHECK(
"Parent | Unrelated" == simplifyStr(arena->addType(UnionType{ "Parent | Unrelated" == simplifyStr(arena->addType(UnionType{
{builtinTypes->neverType, {getBuiltins()->neverType,
parentClass, parentClass,
arena->addType(IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}), arena->addType(IntersectionType{{getBuiltins()->numberType, getBuiltins()->stringType}}),
unrelatedClass} unrelatedClass}
})) }))
); );
@ -299,33 +299,33 @@ TEST_CASE_FIXTURE(ESFixture, "T & U")
TEST_CASE_FIXTURE(ESFixture, "boolean & true") 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)") TEST_CASE_FIXTURE(ESFixture, "boolean & (true | number | string | thread | function | table | class | buffer)")
{ {
TypeId truthy = arena->addType(UnionType{{ TypeId truthy = arena->addType(UnionType{{
builtinTypes->trueType, getBuiltins()->trueType,
builtinTypes->numberType, getBuiltins()->numberType,
builtinTypes->stringType, getBuiltins()->stringType,
builtinTypes->threadType, getBuiltins()->threadType,
builtinTypes->functionType, getBuiltins()->functionType,
builtinTypes->tableType, getBuiltins()->tableType,
builtinTypes->externType, getBuiltins()->externType,
builtinTypes->bufferType, 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?)") 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?)") 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") 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") 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") 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") 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") 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}}))); CHECK("never" == simplifyStr(arena->addType(IntersectionType{{numberToString, notFunction}})));
} }
TEST_CASE_FIXTURE(ESFixture, "(number) -> string | function") 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") TEST_CASE_FIXTURE(ESFixture, "(number) -> string & (string) -> number")
@ -378,7 +378,7 @@ TEST_CASE_FIXTURE(ESFixture, "add<number, number>")
{ {
CHECK( CHECK(
"number" == "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<number, number>")
{ {
CHECK( CHECK(
"number" == "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") TEST_CASE_FIXTURE(ESFixture, "never & ~string")
{ {
CHECK( 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{}); 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") TEST_CASE_FIXTURE(ESFixture, "blocked & ~number & function")
{ {
const TypeId blocked = arena->addType(BlockedType{}); 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"; 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)") TEST_CASE_FIXTURE(ESFixture, "(number | boolean | string | nil | table) & (false | nil)")
{ {
const TypeId t1 = arena->addType( 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)") 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)") 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 // (('a & false) | ('a & nil)) | number
@ -450,16 +450,16 @@ TEST_CASE_FIXTURE(ESFixture, "(boolean | nil) & (false | nil)")
TEST_CASE_FIXTURE(ESFixture, "free & string & number") TEST_CASE_FIXTURE(ESFixture, "free & string & number")
{ {
Scope scope{builtinTypes->anyTypePack}; Scope scope{getBuiltins()->anyTypePack};
const TypeId freeTy = arena->freshType(builtinTypes, &scope); 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)") TEST_CASE_FIXTURE(ESFixture, "(blocked & number) | (blocked & number)")
{ {
const TypeId blocked = arena->addType(BlockedType{}); 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 TypeId ty = arena->addType(UnionType{{u, u}});
const std::string blockedStr = toString(blocked); const std::string blockedStr = toString(blocked);
@ -469,23 +469,23 @@ TEST_CASE_FIXTURE(ESFixture, "(blocked & number) | (blocked & number)")
TEST_CASE_FIXTURE(ESFixture, "{} & unknown") 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") 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?)") 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}") TEST_CASE_FIXTURE(ESFixture, "{x: number?} & {x: number}")
{ {
const TypeId hasOptionalX = tbl({{"x", builtinTypes->optionalNumberType}}); const TypeId hasOptionalX = tbl({{"x", getBuiltins()->optionalNumberType}});
const TypeId hasX = tbl({{"x", builtinTypes->numberType}}); const TypeId hasX = tbl({{"x", getBuiltins()->numberType}});
const TypeId ty = arena->addType(IntersectionType{{hasOptionalX, hasX}}); const TypeId ty = arena->addType(IntersectionType{{hasOptionalX, hasX}});
auto res = eqSatSimplify(NotNull{simplifier.get()}, ty); 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?)}") TEST_CASE_FIXTURE(ESFixture, "{x: number?} & {x: ~(false?)}")
{ {
const TypeId hasOptionalX = tbl({{"x", builtinTypes->optionalNumberType}}); const TypeId hasOptionalX = tbl({{"x", getBuiltins()->optionalNumberType}});
const TypeId hasX = tbl({{"x", builtinTypes->truthyType}}); const TypeId hasX = tbl({{"x", getBuiltins()->truthyType}});
const TypeId ty = arena->addType(IntersectionType{{hasOptionalX, hasX}}); const TypeId ty = arena->addType(IntersectionType{{hasOptionalX, hasX}});
auto res = eqSatSimplify(NotNull{simplifier.get()}, ty); 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?) }") TEST_CASE_FIXTURE(ESFixture, "(({ x: number? }?) & { x: ~(false?) }")
{ {
// {x: number?}? // {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?)} // {x: ~(false?)}
const TypeId xWithTruthy = tbl({{"x", builtinTypes->truthyType}}); const TypeId xWithTruthy = tbl({{"x", getBuiltins()->truthyType}});
const TypeId ty = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy}}); 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?) })") TEST_CASE_FIXTURE(ESFixture, "never | (({ x: number? }?) & { x: ~(false?) })")
{ {
// {x: number?}? // {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?)} // {x: ~(false?)}
const TypeId xWithTruthy = tbl({{"x", builtinTypes->truthyType}}); const TypeId xWithTruthy = tbl({{"x", getBuiltins()->truthyType}});
// ({x: number?}?) & {x: ~(false?)} // ({x: number?}?) & {x: ~(false?)}
const TypeId intersectionTy = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy}}); 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)); 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?)") TEST_CASE_FIXTURE(ESFixture, "({ x: number? }?) & { x: ~(false?) } & ~(false?)")
{ {
// {x: number?}? // {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?)} // {x: ~(false?)}
const TypeId xWithTruthy = tbl({{"x", builtinTypes->truthyType}}); const TypeId xWithTruthy = tbl({{"x", getBuiltins()->truthyType}});
// ({x: number?}?) & {x: ~(false?)} & ~(false?) // ({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)); 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") TEST_CASE_FIXTURE(ESFixture, "(({ x: number? }?) & { x: ~(false?) } & ~(false?)) | number")
{ {
// ({ x: number? }?) & { x: ~(false?) } & ~(false?) // ({ x: number? }?) & { x: ~(false?) } & ~(false?)
const TypeId xWithOptionalNumber = tbl({{"x", builtinTypes->optionalNumberType}}); const TypeId xWithOptionalNumber = tbl({{"x", getBuiltins()->optionalNumberType}});
const TypeId xWithTruthy = tbl({{"x", builtinTypes->truthyType}}); const TypeId xWithTruthy = tbl({{"x", getBuiltins()->truthyType}});
const TypeId intersectionTy = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy, builtinTypes->truthyType}}); const TypeId intersectionTy = arena->addType(IntersectionType{{xWithOptionalNumber, xWithTruthy, getBuiltins()->truthyType}});
const TypeId ty = arena->addType(UnionType{{intersectionTy, builtinTypes->numberType}}); const TypeId ty = arena->addType(UnionType{{intersectionTy, getBuiltins()->numberType}});
CHECK("{ x: number } | number" == simplifyStr(ty)); 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") 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") 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)); CHECK("{ x: number }" == simplifyStr(ty));
} }
TEST_CASE_FIXTURE(ESFixture, "(nil & string)?") TEST_CASE_FIXTURE(ESFixture, "(nil & string)?")
{ {
const TypeId nilAndString = arena->addType(IntersectionType{{builtinTypes->nilType, builtinTypes->stringType}}); const TypeId nilAndString = arena->addType(IntersectionType{{getBuiltins()->nilType, getBuiltins()->stringType}});
const TypeId ty = arena->addType(UnionType{{nilAndString, builtinTypes->nilType}}); const TypeId ty = arena->addType(UnionType{{nilAndString, getBuiltins()->nilType}});
CHECK("nil" == simplifyStr(ty)); CHECK("nil" == simplifyStr(ty));
} }
@ -590,7 +590,7 @@ TEST_CASE_FIXTURE(ESFixture, "string & \"hi\"")
{ {
const TypeId hi = arena->addType(SingletonType{StringSingleton{"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\")") 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 hi = arena->addType(SingletonType{StringSingleton{"hi"}});
const TypeId bye = arena->addType(SingletonType{StringSingleton{"bye"}}); 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\"") TEST_CASE_FIXTURE(ESFixture, "(\"err\" | \"ok\") & ~\"ok\"")
@ -622,7 +622,7 @@ TEST_CASE_FIXTURE(ESFixture, "(Child | Unrelated) & ~Child")
TEST_CASE_FIXTURE(ESFixture, "string & ~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") 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 }") 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)); CHECK("never" == simplifyStr(ty));
} }
TEST_CASE_FIXTURE(ESFixture, "{ tag: \"Part\", x: number? } & { x: string }") 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 leftTable = tbl({{"tag", arena->addType(SingletonType{StringSingleton{"Part"}})}, {"x", getBuiltins()->optionalNumberType}});
const TypeId rightTable = tbl({{"x", builtinTypes->stringType}}); const TypeId rightTable = tbl({{"x", getBuiltins()->stringType}});
CHECK("never" == simplifyStr(arena->addType(IntersectionType{{leftTable, rightTable}}))); CHECK("never" == simplifyStr(arena->addType(IntersectionType{{leftTable, rightTable}})));
} }
TEST_CASE_FIXTURE(ESFixture, "Child & add<Child | AnotherChild | string, Parent>") TEST_CASE_FIXTURE(ESFixture, "Child & add<Child | AnotherChild | string, Parent>")
{ {
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 intersectTf = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().addFunc, {u, parentClass}, {}});
const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}}); const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}});
@ -662,7 +662,7 @@ TEST_CASE_FIXTURE(ESFixture, "Child & add<Child | AnotherChild | string, Parent>
TEST_CASE_FIXTURE(ESFixture, "Child & intersect<Child | AnotherChild | string, Parent>") TEST_CASE_FIXTURE(ESFixture, "Child & intersect<Child | AnotherChild | string, Parent>")
{ {
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 intersectTf = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions().intersectFunc, {u, parentClass}, {}});
const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}}); const TypeId intersection = arena->addType(IntersectionType{{childClass, intersectTf}});
@ -673,10 +673,10 @@ TEST_CASE_FIXTURE(ESFixture, "Child & intersect<Child | AnotherChild | string, P
TEST_CASE_FIXTURE(ESFixture, "lt<number, _> == boolean") TEST_CASE_FIXTURE(ESFixture, "lt<number, _> == boolean")
{ {
std::vector<std::pair<TypeId, TypeId>> cases{ std::vector<std::pair<TypeId, TypeId>> cases{
{builtinTypes->numberType, arena->addType(BlockedType{})}, {getBuiltins()->numberType, arena->addType(BlockedType{})},
{builtinTypes->stringType, arena->addType(BlockedType{})}, {getBuiltins()->stringType, arena->addType(BlockedType{})},
{arena->addType(BlockedType{}), builtinTypes->numberType}, {arena->addType(BlockedType{}), getBuiltins()->numberType},
{arena->addType(BlockedType{}), builtinTypes->stringType}, {arena->addType(BlockedType{}), getBuiltins()->stringType},
}; };
for (const auto& [lhs, rhs] : cases) for (const auto& [lhs, rhs] : cases)
@ -689,7 +689,7 @@ TEST_CASE_FIXTURE(ESFixture, "lt<number, _> == boolean")
TEST_CASE_FIXTURE(ESFixture, "unknown & ~string") TEST_CASE_FIXTURE(ESFixture, "unknown & ~string")
{ {
CHECK_EQ( 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( CHECK_EQ(
"string & ~\"foo\"", "string & ~\"foo\"",
simplifyStr(arena->addType( 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"}})})}}
)) ))
); );
} }

View file

@ -18,7 +18,7 @@ TEST_CASE("TypeError_code_should_return_nonzero_code")
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_names_show_instead_of_tables") TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_names_show_instead_of_tables")
{ {
frontend.options.retainFullTypeGraphs = false; getFrontend().options.retainFullTypeGraphs = false;
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
@ -38,7 +38,7 @@ local x: Account = 5
TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_function_errors") TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_function_errors")
{ {
frontend.options.retainFullTypeGraphs = false; getFrontend().options.retainFullTypeGraphs = false;
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
@ -58,7 +58,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "binary_op_type_function_errors")
TEST_CASE_FIXTURE(BuiltinsFixture, "unary_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"( CheckResult result = check(R"(
--!strict --!strict

View file

@ -257,37 +257,8 @@ const Config& TestConfigResolver::getConfig(const ModuleName& name) const
} }
Fixture::Fixture(bool prepareAutocomplete) Fixture::Fixture(bool prepareAutocomplete)
: frontend( : forAutocomplete(prepareAutocomplete)
&fileResolver,
&configResolver,
{/* retainFullTypeGraphs= */ true, /* forAutocomplete */ false, /* runLintChecks */ false, /* randomConstraintResolutionSeed */ randomSeed}
)
, builtinTypes(frontend.builtinTypes)
{ {
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() Fixture::~Fixture()
@ -318,27 +289,27 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars
*sourceModule, *sourceModule,
mode, mode,
{}, {},
builtinTypes, getBuiltins(),
NotNull{&ice}, NotNull{&ice},
NotNull{&moduleResolver}, NotNull{&moduleResolver},
NotNull{&fileResolver}, NotNull{&fileResolver},
frontend.globals.globalScope, getFrontend().globals.globalScope,
frontend.globals.globalTypeFunctionScope, getFrontend().globals.globalTypeFunctionScope,
/*prepareModuleScope*/ nullptr, /*prepareModuleScope*/ nullptr,
frontend.options, getFrontend().options,
{}, {},
false, 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 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); 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<FrontendOptions> options) CheckResult Fixture::check(Mode mode, const std::string& source, std::optional<FrontendOptions> options)
{ {
// Force the frontend here
getFrontend();
ModuleName mm = fromString(mainModuleName); ModuleName mm = fromString(mainModuleName);
configResolver.defaultConfig.mode = mode; configResolver.defaultConfig.mode = mode;
fileResolver.source[mm] = std::move(source); 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; return result;
} }
@ -370,18 +343,18 @@ LintResult Fixture::lint(const std::string& source, const std::optional<LintOpti
ModuleName mm = fromString(mainModuleName); ModuleName mm = fromString(mainModuleName);
configResolver.defaultConfig.mode = Mode::Strict; configResolver.defaultConfig.mode = Mode::Strict;
fileResolver.source[mm] = std::move(source); fileResolver.source[mm] = std::move(source);
frontend.markDirty(mm); getFrontend().markDirty(mm);
return lintModule(mm, lintOptions); return lintModule(mm, lintOptions);
} }
LintResult Fixture::lintModule(const ModuleName& moduleName, const std::optional<LintOptions>& lintOptions) LintResult Fixture::lintModule(const ModuleName& moduleName, const std::optional<LintOptions>& lintOptions)
{ {
FrontendOptions options = frontend.options; FrontendOptions options = getFrontend().options;
options.runLintChecks = true; options.runLintChecks = true;
options.enabledLintWarnings = lintOptions; options.enabledLintWarnings = lintOptions;
CheckResult result = frontend.check(moduleName, options); CheckResult result = getFrontend().check(moduleName, options);
return result.lintResult; return result.lintResult;
} }
@ -450,14 +423,14 @@ ParseResult Fixture::matchParseErrorPrefix(const std::string& source, const std:
ModulePtr Fixture::getMainModule(bool forAutocomplete) ModulePtr Fixture::getMainModule(bool forAutocomplete)
{ {
if (forAutocomplete && !FFlag::LuauSolverV2) 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() SourceModule* Fixture::getMainSourceModule()
{ {
return frontend.getSourceModule(fromString(mainModuleName)); return getFrontend().getSourceModule(fromString(mainModuleName));
} }
std::optional<PrimitiveType::Type> Fixture::getPrimitiveType(TypeId ty) std::optional<PrimitiveType::Type> 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) TypeId Fixture::requireType(const ModuleName& moduleName, const std::string& name)
{ {
ModulePtr module = frontend.moduleResolver.getModule(moduleName); ModulePtr module = getFrontend().moduleResolver.getModule(moduleName);
REQUIRE(module); REQUIRE(module);
return requireType(module, name); 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) TypeId Fixture::requireExportedType(const ModuleName& moduleName, const std::string& name)
{ {
ModulePtr module = frontend.moduleResolver.getModule(moduleName); ModulePtr module = getFrontend().moduleResolver.getModule(moduleName);
REQUIRE(module); REQUIRE(module);
auto it = module->exportedTypeBindings.find(name); auto it = module->exportedTypeBindings.find(name);
@ -586,10 +559,10 @@ std::string Fixture::decorateWithTypes(const std::string& code)
{ {
fileResolver.source[mainModuleName] = code; fileResolver.source[mainModuleName] = code;
Luau::CheckResult typeInfo = frontend.check(mainModuleName); Luau::CheckResult typeInfo = getFrontend().check(mainModuleName);
SourceModule* sourceModule = frontend.getSourceModule(mainModuleName); SourceModule* sourceModule = getFrontend().getSourceModule(mainModuleName);
attachTypeData(*sourceModule, *frontend.moduleResolver.getModule(mainModuleName)); attachTypeData(*sourceModule, *getFrontend().moduleResolver.getModule(mainModuleName));
return transpileWithTypes(*sourceModule->root); return transpileWithTypes(*sourceModule->root);
} }
@ -621,9 +594,9 @@ void Fixture::dumpErrors(std::ostream& os, const std::vector<TypeError>& errors)
void Fixture::registerTestTypes() void Fixture::registerTestTypes()
{ {
addGlobalBinding(frontend.globals, "game", builtinTypes->anyType, "@luau"); addGlobalBinding(getFrontend().globals, "game", getBuiltins()->anyType, "@luau");
addGlobalBinding(frontend.globals, "workspace", builtinTypes->anyType, "@luau"); addGlobalBinding(getFrontend().globals, "workspace", getBuiltins()->anyType, "@luau");
addGlobalBinding(frontend.globals, "script", builtinTypes->anyType, "@luau"); addGlobalBinding(getFrontend().globals, "script", getBuiltins()->anyType, "@luau");
} }
void Fixture::dumpErrors(const CheckResult& cr) void Fixture::dumpErrors(const CheckResult& cr)
@ -682,9 +655,9 @@ void Fixture::validateErrors(const std::vector<Luau::TypeError>& errors)
LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool forAutocomplete) 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); unfreeze(globals.globalTypes);
LoadDefinitionFileResult result = frontend.loadDefinitionFile( LoadDefinitionFileResult result = getFrontend().loadDefinitionFile(
globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typecheckForAutocomplete */ forAutocomplete globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typecheckForAutocomplete */ forAutocomplete
); );
freeze(globals.globalTypes); freeze(globals.globalTypes);
@ -695,19 +668,80 @@ LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool
return result; return result;
} }
NotNull<BuiltinTypes> 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) BuiltinsFixture::BuiltinsFixture(bool prepareAutocomplete)
: Fixture(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(); registerTestTypes();
Luau::freeze(frontend.globals.globalTypes); Luau::freeze(f.globals.globalTypes);
Luau::freeze(frontend.globalsForAutocomplete.globalTypes); Luau::freeze(f.globalsForAutocomplete.globalTypes);
return *frontend;
} }
static std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr) static std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr)
@ -831,9 +865,9 @@ std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name)
return std::nullopt; return std::nullopt;
} }
void registerHiddenTypes(Frontend* frontend) void registerHiddenTypes(Frontend& frontend)
{ {
GlobalTypes& globals = frontend->globals; GlobalTypes& globals = frontend.globals;
unfreeze(globals.globalTypes); unfreeze(globals.globalTypes);
@ -846,24 +880,24 @@ void registerHiddenTypes(Frontend* frontend)
ScopePtr globalScope = globals.globalScope; ScopePtr globalScope = globals.globalScope;
globalScope->exportedTypeBindings["Not"] = TypeFun{{genericT}, globals.globalTypes.addType(NegationType{t})}; globalScope->exportedTypeBindings["Not"] = TypeFun{{genericT}, globals.globalTypes.addType(NegationType{t})};
globalScope->exportedTypeBindings["Mt"] = TypeFun{{genericT, genericU}, globals.globalTypes.addType(MetatableType{t, u})}; globalScope->exportedTypeBindings["Mt"] = TypeFun{{genericT, genericU}, globals.globalTypes.addType(MetatableType{t, u})};
globalScope->exportedTypeBindings["fun"] = TypeFun{{}, frontend->builtinTypes->functionType}; globalScope->exportedTypeBindings["fun"] = TypeFun{{}, frontend.builtinTypes->functionType};
globalScope->exportedTypeBindings["cls"] = TypeFun{{}, frontend->builtinTypes->externType}; globalScope->exportedTypeBindings["cls"] = TypeFun{{}, frontend.builtinTypes->externType};
globalScope->exportedTypeBindings["err"] = TypeFun{{}, frontend->builtinTypes->errorType}; globalScope->exportedTypeBindings["err"] = TypeFun{{}, frontend.builtinTypes->errorType};
globalScope->exportedTypeBindings["tbl"] = TypeFun{{}, frontend->builtinTypes->tableType}; globalScope->exportedTypeBindings["tbl"] = TypeFun{{}, frontend.builtinTypes->tableType};
freeze(globals.globalTypes); freeze(globals.globalTypes);
} }
void createSomeExternTypes(Frontend* frontend) void createSomeExternTypes(Frontend& frontend)
{ {
GlobalTypes& globals = frontend->globals; GlobalTypes& globals = frontend.globals;
TypeArena& arena = globals.globalTypes; TypeArena& arena = globals.globalTypes;
unfreeze(arena); unfreeze(arena);
ScopePtr moduleScope = globals.globalScope; 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<ExternType>(parentType); ExternType* parentExternType = getMutable<ExternType>(parentType);
parentExternType->props["method"] = {makeFunction(arena, parentType, {}, {})}; parentExternType->props["method"] = {makeFunction(arena, parentType, {}, {})};
@ -883,7 +917,7 @@ void createSomeExternTypes(Frontend* frontend)
addGlobalBinding(globals, "AnotherChild", {anotherChildType}); addGlobalBinding(globals, "AnotherChild", {anotherChildType});
moduleScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, 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}); addGlobalBinding(globals, "Unrelated", {unrelatedType});
moduleScope->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType}; moduleScope->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType};

View file

@ -160,9 +160,8 @@ struct Fixture
TestConfigResolver configResolver; TestConfigResolver configResolver;
NullModuleResolver moduleResolver; NullModuleResolver moduleResolver;
std::unique_ptr<SourceModule> sourceModule; std::unique_ptr<SourceModule> sourceModule;
Frontend frontend;
InternalErrorReporter ice; InternalErrorReporter ice;
NotNull<BuiltinTypes> builtinTypes;
std::string decorateWithTypes(const std::string& code); std::string decorateWithTypes(const std::string& code);
@ -179,9 +178,15 @@ struct Fixture
void registerTestTypes(); void registerTestTypes();
LoadDefinitionFileResult loadDefinition(const std::string& source, bool forAutocomplete = false); LoadDefinitionFileResult loadDefinition(const std::string& source, bool forAutocomplete = false);
// TODO: test theory about dynamic dispatch
NotNull<BuiltinTypes> getBuiltins();
virtual Frontend& getFrontend();
private: private:
bool hasDumpedErrors = false; bool hasDumpedErrors = false;
protected:
bool forAutocomplete = false;
std::optional<Frontend> frontend;
BuiltinTypes* builtinTypes = nullptr;
}; };
struct BuiltinsFixture : Fixture 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. // For the purpose of our tests, we're always the latest version of type functions.
ScopedFastFlag sff_optionalInTypeFunctionLib{FFlag::LuauTypeFunOptional, true}; ScopedFastFlag sff_optionalInTypeFunctionLib{FFlag::LuauTypeFunOptional, true};
Frontend& getFrontend() override;
}; };
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments); std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments);
@ -220,8 +226,8 @@ std::optional<TypeId> lookupName(ScopePtr scope, const std::string& name); // Wa
std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name); std::optional<TypeId> linearSearchForBinding(Scope* scope, const char* name);
void registerHiddenTypes(Frontend* frontend); void registerHiddenTypes(Frontend& frontend);
void createSomeExternTypes(Frontend* frontend); void createSomeExternTypes(Frontend& frontend);
template<typename E> template<typename E>
const E* findError(const CheckResult& result) const E* findError(const CheckResult& result)

View file

@ -51,9 +51,9 @@ static FrontendOptions getOptions()
return options; 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<class BaseType> template<class BaseType>
@ -130,7 +130,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType
) )
{ {
ParseResult p = parseHelper(document); 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; return result;
} }
@ -145,7 +145,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType
ParseResult parseResult = parseHelper(document); ParseResult parseResult = parseHelper(document);
FrontendOptions options = getOptions(); FrontendOptions options = getOptions();
FragmentContext context{document, parseResult, options, fragmentEndPosition}; 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( void autocompleteFragmentInNewSolver(
@ -211,7 +211,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType
) )
{ {
ParseResult pr = parseHelper(document); 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( FragmentAutocompleteStatusResult autocompleteFragmentForModule(
@ -226,7 +226,7 @@ struct FragmentAutocompleteFixtureImpl : BaseType
ParseResult parseResult = parseHelper(document); ParseResult parseResult = parseHelper(document);
FrontendOptions options; FrontendOptions options;
FragmentContext context{document, parseResult, options, fragmentEndPosition}; 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() SourceModule& getSource()
@ -244,10 +244,10 @@ struct FragmentAutocompleteFixture : FragmentAutocompleteFixtureImpl<Fixture>
FragmentAutocompleteFixture() FragmentAutocompleteFixture()
: FragmentAutocompleteFixtureImpl<Fixture>() : FragmentAutocompleteFixtureImpl<Fixture>()
{ {
addGlobalBinding(frontend.globals, "table", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globals, "table", Binding{getBuiltins()->anyType});
addGlobalBinding(frontend.globals, "math", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globals, "math", Binding{getBuiltins()->anyType});
addGlobalBinding(frontend.globalsForAutocomplete, "table", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globalsForAutocomplete, "table", Binding{getBuiltins()->anyType});
addGlobalBinding(frontend.globalsForAutocomplete, "math", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globalsForAutocomplete, "math", Binding{getBuiltins()->anyType});
} }
}; };
@ -272,8 +272,8 @@ end
loadDefinition(fakeVecDecl); loadDefinition(fakeVecDecl);
loadDefinition(fakeVecDecl, /* For Autocomplete Module */ true); loadDefinition(fakeVecDecl, /* For Autocomplete Module */ true);
addGlobalBinding(frontend.globals, "game", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globals, "game", Binding{getBuiltins()->anyType});
addGlobalBinding(frontend.globalsForAutocomplete, "game", Binding{builtinTypes->anyType}); addGlobalBinding(getFrontend().globalsForAutocomplete, "game", Binding{getBuiltins()->anyType});
} }
}; };
@ -1317,7 +1317,7 @@ abc("bar")
CHECK_EQ(Position{3, 1}, parent->location.end); 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(); DOES_NOT_PASS_NEW_SOLVER_GUARD();
@ -1330,21 +1330,21 @@ t
FrontendOptions opts; FrontendOptions opts;
opts.forAutocomplete = true; opts.forAutocomplete = true;
frontend.check("game/A", opts); getFrontend().check("game/A", opts);
CHECK_NE(frontend.moduleResolverForAutocomplete.getModule("game/A"), nullptr); CHECK_NE(getFrontend().moduleResolverForAutocomplete.getModule("game/A"), nullptr);
CHECK_EQ(frontend.moduleResolver.getModule("game/A"), nullptr); CHECK_EQ(getFrontend().moduleResolver.getModule("game/A"), nullptr);
ParseOptions parseOptions; ParseOptions parseOptions;
parseOptions.captureComments = true; parseOptions.captureComments = true;
SourceModule sourceMod; SourceModule sourceMod;
ParseResult parseResult = Parser::parse(source.c_str(), source.length(), *sourceMod.names, *sourceMod.allocator, parseOptions); ParseResult parseResult = Parser::parse(source.c_str(), source.length(), *sourceMod.names, *sourceMod.allocator, parseOptions);
FragmentContext context{source, parseResult, opts, std::nullopt}; 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);
REQUIRE(frag.result->incrementalModule); REQUIRE(frag.result->incrementalModule);
CHECK_EQ("game/A", frag.result->incrementalModule->name); CHECK_EQ("game/A", frag.result->incrementalModule->name);
CHECK_NE(frontend.moduleResolverForAutocomplete.getModule("game/A"), nullptr); CHECK_NE(getFrontend().moduleResolverForAutocomplete.getModule("game/A"), nullptr);
CHECK_EQ(frontend.moduleResolver.getModule("game/A"), nullptr); CHECK_EQ(getFrontend().moduleResolver.getModule("game/A"), nullptr);
} }
TEST_SUITE_END(); TEST_SUITE_END();
@ -1496,16 +1496,16 @@ return { hello = B }
const std::string sourceB = "game/Gui/Modules/B"; const std::string sourceB = "game/Gui/Modules/B";
fileResolver.source[sourceB] = R"(return {hello = "hello"})"; fileResolver.source[sourceB] = R"(return {hello = "hello"})";
CheckResult result = frontend.check(sourceA, getOptions()); CheckResult result = getFrontend().check(sourceA, getOptions());
CHECK(!frontend.isDirty(sourceA, getOptions().forAutocomplete)); CHECK(!getFrontend().isDirty(sourceA, getOptions().forAutocomplete));
std::weak_ptr<Module> weakModule = getModuleResolver(frontend).getModule(sourceB); std::weak_ptr<Module> weakModule = getModuleResolver(getFrontend()).getModule(sourceB);
REQUIRE(!weakModule.expired()); REQUIRE(!weakModule.expired());
frontend.markDirty(sourceB); getFrontend().markDirty(sourceB);
CHECK(frontend.isDirty(sourceA, getOptions().forAutocomplete)); CHECK(getFrontend().isDirty(sourceA, getOptions().forAutocomplete));
frontend.check(sourceB, getOptions()); getFrontend().check(sourceB, getOptions());
CHECK(weakModule.expired()); CHECK(weakModule.expired());
auto [status, _] = typecheckFragmentForModule(sourceA, fileResolver.source[sourceA], Luau::Position(0, 0)); auto [status, _] = typecheckFragmentForModule(sourceA, fileResolver.source[sourceA], Luau::Position(0, 0));

View file

@ -17,7 +17,7 @@ LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2) LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
namespace namespace
{ {
@ -59,8 +59,8 @@ struct FrontendFixture : BuiltinsFixture
{ {
FrontendFixture() FrontendFixture()
{ {
addGlobalBinding(frontend.globals, "game", builtinTypes->anyType, "@test"); addGlobalBinding(getFrontend().globals, "game", getBuiltins()->anyType, "@test");
addGlobalBinding(frontend.globals, "script", builtinTypes->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} 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); REQUIRE(bModule != nullptr);
CHECK(bModule->errors.empty()); CHECK(bModule->errors.empty());
Luau::dumpErrors(bModule); Luau::dumpErrors(bModule);
@ -169,7 +169,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "automatically_check_cyclically_dependent_scr
return {} return {}
)"; )";
CheckResult result1 = frontend.check("game/Gui/Modules/B"); CheckResult result1 = getFrontend().check("game/Gui/Modules/B");
LUAU_REQUIRE_ERROR_COUNT(4, result1); LUAU_REQUIRE_ERROR_COUNT(4, result1);
CHECK_MESSAGE(get<ModuleHasCyclicDependency>(result1.errors[0]), "Should have been a ModuleHasCyclicDependency: " << toString(result1.errors[0])); CHECK_MESSAGE(get<ModuleHasCyclicDependency>(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<ModuleHasCyclicDependency>(result1.errors[2]), "Should have been a ModuleHasCyclicDependency: " << toString(result1.errors[2])); CHECK_MESSAGE(get<ModuleHasCyclicDependency>(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); LUAU_REQUIRE_ERROR_COUNT(0, result2);
} }
@ -195,7 +195,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "any_annotation_breaks_cycle")
return {hello = A.hello} 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); LUAU_REQUIRE_NO_ERRORS(result);
} }
@ -218,16 +218,16 @@ TEST_CASE_FIXTURE(FrontendFixture, "nocheck_modules_are_typed")
local five : A.Foo = 5 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); 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)); REQUIRE(bool(aModule));
std::optional<TypeId> aExports = first(aModule->returnType); std::optional<TypeId> aExports = first(aModule->returnType);
REQUIRE(bool(aExports)); REQUIRE(bool(aExports));
ModulePtr bModule = frontend.moduleResolver.getModule("game/Gui/Modules/B"); ModulePtr bModule = getFrontend().moduleResolver.getModule("game/Gui/Modules/B");
REQUIRE(bool(bModule)); REQUIRE(bool(bModule));
std::optional<TypeId> bExports = first(bModule->returnType); std::optional<TypeId> bExports = first(bModule->returnType);
@ -250,7 +250,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_detection_between_check_and_nocheck")
return {hello = A.hello} 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); LUAU_REQUIRE_ERROR_COUNT(1, result);
} }
@ -276,10 +276,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "nocheck_cycle_used_by_checked")
return {a=A, b=B} 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); 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)); REQUIRE(bool(cModule));
std::optional<TypeId> cExports = first(cModule->returnType); std::optional<TypeId> cExports = first(cModule->returnType);
@ -306,7 +306,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_detection_disabled_in_nocheck")
return {hello = A.hello} 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); LUAU_REQUIRE_NO_ERRORS(result);
} }
@ -323,7 +323,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_errors_can_be_fixed")
return {hello = A.hello} 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); LUAU_REQUIRE_ERROR_COUNT(2, result1);
CHECK_MESSAGE(get<ModuleHasCyclicDependency>(result1.errors[0]), "Should have been a ModuleHasCyclicDependency: " << toString(result1.errors[0])); CHECK_MESSAGE(get<ModuleHasCyclicDependency>(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"( fileResolver.source["game/Gui/Modules/B"] = R"(
return {hello = 42} 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); LUAU_REQUIRE_NO_ERRORS(result2);
} }
@ -352,7 +352,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_error_paths")
return {hello = A.hello} 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); LUAU_REQUIRE_ERROR_COUNT(2, result);
auto ce1 = get<ModuleHasCyclicDependency>(result.errors[0]); auto ce1 = get<ModuleHasCyclicDependency>(result.errors[0]);
@ -376,16 +376,16 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_incremental_type_surface")
return {hello = 2} return {hello = 2}
)"; )";
CheckResult result = frontend.check("game/A"); CheckResult result = getFrontend().check("game/A");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
fileResolver.source["game/A"] = R"( fileResolver.source["game/A"] = R"(
local me = require(game.A) local me = require(game.A)
return {hello = 2} 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); LUAU_REQUIRE_ERRORS(result);
auto ty = requireType("game/A", "me"); auto ty = requireType("game/A", "me");
@ -398,7 +398,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_incremental_type_surface_longer")
return {mod_a = 2} return {mod_a = 2}
)"; )";
CheckResult result = frontend.check("game/A"); CheckResult result = getFrontend().check("game/A");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
fileResolver.source["game/B"] = R"( fileResolver.source["game/B"] = R"(
@ -406,7 +406,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_incremental_type_surface_longer")
return {mod_b = 4} return {mod_b = 4}
)"; )";
result = frontend.check("game/B"); result = getFrontend().check("game/B");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
fileResolver.source["game/A"] = R"( fileResolver.source["game/A"] = R"(
@ -414,16 +414,16 @@ TEST_CASE_FIXTURE(FrontendFixture, "cycle_incremental_type_surface_longer")
return {mod_a_prime = 3} return {mod_a_prime = 3}
)"; )";
frontend.markDirty("game/A"); getFrontend().markDirty("game/A");
frontend.markDirty("game/B"); getFrontend().markDirty("game/B");
result = frontend.check("game/A"); result = getFrontend().check("game/A");
LUAU_REQUIRE_ERRORS(result); LUAU_REQUIRE_ERRORS(result);
TypeId tyA = requireType("game/A", "me"); TypeId tyA = requireType("game/A", "me");
CHECK_EQ(toString(tyA), "any"); CHECK_EQ(toString(tyA), "any");
result = frontend.check("game/B"); result = getFrontend().check("game/B");
LUAU_REQUIRE_ERRORS(result); LUAU_REQUIRE_ERRORS(result);
TypeId tyB = requireType("game/B", "me"); TypeId tyB = requireType("game/B", "me");
@ -452,10 +452,10 @@ return {mod_b = 2}
ToStringOptions opts; ToStringOptions opts;
opts.exhaustive = true; opts.exhaustive = true;
CheckResult resultA = frontend.check("game/A"); CheckResult resultA = getFrontend().check("game/A");
LUAU_REQUIRE_ERRORS(resultA); LUAU_REQUIRE_ERRORS(resultA);
CheckResult resultB = frontend.check("game/B"); CheckResult resultB = getFrontend().check("game/B");
LUAU_REQUIRE_ERRORS(resultB); LUAU_REQUIRE_ERRORS(resultB);
TypeId tyB = requireExportedType("game/B", "btype"); TypeId tyB = requireExportedType("game/B", "btype");
@ -470,8 +470,8 @@ return {mod_b = 2}
else else
CHECK_EQ(toString(tyA, opts), "{| x: any |}"); CHECK_EQ(toString(tyA, opts), "{| x: any |}");
frontend.markDirty("game/B"); getFrontend().markDirty("game/B");
resultB = frontend.check("game/B"); resultB = getFrontend().check("game/B");
LUAU_REQUIRE_ERRORS(resultB); LUAU_REQUIRE_ERRORS(resultB);
tyB = requireExportedType("game/B", "btype"); 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} return {b_value = A.hello}
)"; )";
frontend.check("game/Gui/Modules/B"); getFrontend().check("game/Gui/Modules/B");
fileResolver.source["game/Gui/Modules/A"] = 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()); CHECK(bModule->errors.empty());
Luau::dumpErrors(bModule); Luau::dumpErrors(bModule);
} }
@ -543,14 +543,14 @@ TEST_CASE_FIXTURE(FrontendFixture, "recheck_if_dependent_script_is_dirty")
return {b_value = A.hello} 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!'}"; 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()); CHECK(bModule->errors.empty());
Luau::dumpErrors(bModule); Luau::dumpErrors(bModule);
@ -575,10 +575,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "mark_non_immediate_reverse_deps_as_dirty")
return {c_value = B.hello} return {c_value = B.hello}
)"; )";
frontend.check("game/Gui/Modules/C"); getFrontend().check("game/Gui/Modules/C");
std::vector<Luau::ModuleName> markedDirty; std::vector<Luau::ModuleName> markedDirty;
frontend.markDirty("game/Gui/Modules/A", &markedDirty); getFrontend().markDirty("game/Gui/Modules/A", &markedDirty);
REQUIRE(markedDirty.size() == 3); REQUIRE(markedDirty.size() == 3);
CHECK(std::find(markedDirty.begin(), markedDirty.end(), "game/Gui/Modules/A") != markedDirty.end()); 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 {} return {}
)"; )";
CheckResult result = frontend.check("Modules/B"); CheckResult result = getFrontend().check("Modules/B");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("Modules/A", result.errors[0].moduleName); 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); LUAU_REQUIRE_ERROR_COUNT(1, result2);
CHECK_EQ(result2.errors[0], result.errors[0]); 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!!"; fileResolver.source["Modules/A"] = "oh no a blatant syntax error!!";
CheckResult one = frontend.check("Modules/A"); CheckResult one = getFrontend().check("Modules/A");
CheckResult two = frontend.check("Modules/A"); CheckResult two = getFrontend().check("Modules/A");
CHECK(!one.errors.empty()); CHECK(!one.errors.empty());
CHECK(!two.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'"; 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!"; 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 = frontend.check("Modules/A"); CheckResult secondResult = getFrontend().check("Modules/A");
CHECK_EQ(1, secondResult.errors.size()); 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!' 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); LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK_EQ("game/Gui/Modules/A", result.errors[0].moduleName); 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) local B = require(Modules.B)
)"; )";
CheckResult result = frontend.check("Modules/A"); CheckResult result = getFrontend().check("Modules/A");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
std::string s = toString(result.errors[0]); 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 local B = require(Modules.B) :: any
)"; )";
CheckResult result = frontend.check("Modules/A"); CheckResult result = getFrontend().check("Modules/A");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
@ -683,7 +683,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "report_syntax_error_in_required_file")
local A = require(Modules.A) local A = require(Modules.A)
)"; )";
CheckResult result = frontend.check("Modules/B"); CheckResult result = getFrontend().check("Modules/B");
LUAU_REQUIRE_ERRORS(result); LUAU_REQUIRE_ERRORS(result);
CHECK_EQ("Modules/A", result.errors[0].moduleName); 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) print(A.n)
)"; )";
CheckResult result = frontend.check("Modules/B"); CheckResult result = getFrontend().check("Modules/B");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CheckResult result2 = frontend.check("Modules/B"); CheckResult result2 = getFrontend().check("Modules/B");
LUAU_REQUIRE_ERROR_COUNT(1, result2); LUAU_REQUIRE_ERROR_COUNT(1, result2);
CHECK_EQ("Modules/A", result.errors[0].moduleName); CHECK_EQ("Modules/A", result.errors[0].moduleName);
@ -739,14 +739,14 @@ TEST_CASE_FIXTURE(FrontendFixture, "accumulate_cached_errors")
print(A, b) print(A, b)
)"; )";
CheckResult result1 = frontend.check("Modules/B"); CheckResult result1 = getFrontend().check("Modules/B");
LUAU_REQUIRE_ERROR_COUNT(2, result1); LUAU_REQUIRE_ERROR_COUNT(2, result1);
CHECK_EQ("Modules/A", result1.errors[0].moduleName); CHECK_EQ("Modules/A", result1.errors[0].moduleName);
CHECK_EQ("Modules/B", result1.errors[1].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); LUAU_REQUIRE_ERROR_COUNT(2, result2);
@ -769,7 +769,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "accumulate_cached_errors_in_consistent_order
return {} return {}
)"; )";
CheckResult result1 = frontend.check("Modules/A"); CheckResult result1 = getFrontend().check("Modules/A");
LUAU_REQUIRE_ERROR_COUNT(4, result1); 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[0].moduleName);
CHECK_EQ("Modules/B", result1.errors[1].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()); CHECK_EQ(4, result2.errors.size());
for (size_t i = 0; i < result1.errors.size(); ++i) 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()); CHECK_EQ(1, result.warnings.size());
configResolver.configFiles["Module/A"].enabledLint.disableWarning(LintWarning::Code_ForRange); configResolver.configFiles["Module/A"].enabledLint.disableWarning(LintWarning::Code_ForRange);
frontend.markDirty("Module/A"); getFrontend().markDirty("Module/A");
auto result2 = lintModule("Module/A"); auto result2 = lintModule("Module/A");
CHECK_EQ(0, result2.warnings.size()); CHECK_EQ(0, result2.warnings.size());
@ -825,13 +825,13 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_lint_uses_correct_config")
LintOptions overrideOptions; LintOptions overrideOptions;
overrideOptions.enableWarning(LintWarning::Code_ForRange); overrideOptions.enableWarning(LintWarning::Code_ForRange);
frontend.markDirty("Module/A"); getFrontend().markDirty("Module/A");
auto result3 = lintModule("Module/A", overrideOptions); auto result3 = lintModule("Module/A", overrideOptions);
CHECK_EQ(1, result3.warnings.size()); CHECK_EQ(1, result3.warnings.size());
overrideOptions.disableWarning(LintWarning::Code_ForRange); overrideOptions.disableWarning(LintWarning::Code_ForRange);
frontend.markDirty("Module/A"); getFrontend().markDirty("Module/A");
auto result4 = lintModule("Module/A", overrideOptions); auto result4 = lintModule("Module/A", overrideOptions);
CHECK_EQ(0, result4.warnings.size()); 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") 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}}; Frontend fe{&fileResolver, &configResolver, {false}};
@ -932,7 +932,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "trace_requires_in_nonstrict_mode")
print(A.f(5)) -- OK print(A.f(5)) -- OK
)"; )";
CheckResult result = frontend.check("Module/B"); CheckResult result = getFrontend().check("Module/B");
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
@ -942,11 +942,11 @@ TEST_CASE_FIXTURE(FrontendFixture, "trace_requires_in_nonstrict_mode")
TEST_CASE_FIXTURE(FrontendFixture, "environments") TEST_CASE_FIXTURE(FrontendFixture, "environments")
{ {
ScopePtr testScope = frontend.addEnvironment("test"); ScopePtr testScope = getFrontend().addEnvironment("test");
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
frontend.loadDefinitionFile( getFrontend().loadDefinitionFile(
frontend.globals, getFrontend().globals,
testScope, testScope,
R"( R"(
export type Foo = number | string export type Foo = number | string
@ -954,7 +954,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "environments")
"@test", "@test",
/* captureComments */ false /* captureComments */ false
); );
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
fileResolver.source["A"] = R"( fileResolver.source["A"] = R"(
--!nonstrict --!nonstrict
@ -973,16 +973,16 @@ TEST_CASE_FIXTURE(FrontendFixture, "environments")
fileResolver.environments["A"] = "test"; fileResolver.environments["A"] = "test";
CheckResult resultA = frontend.check("A"); CheckResult resultA = getFrontend().check("A");
LUAU_REQUIRE_NO_ERRORS(resultA); LUAU_REQUIRE_NO_ERRORS(resultA);
CheckResult resultB = frontend.check("B"); CheckResult resultB = getFrontend().check("B");
if (FFlag::LuauSolverV2 && !FFlag::LuauNewNonStrictVisitTypes2) if (FFlag::LuauSolverV2 && !FFlag::LuauNewNonStrictVisitTypes2)
LUAU_REQUIRE_NO_ERRORS(resultB); LUAU_REQUIRE_NO_ERRORS(resultB);
else else
LUAU_REQUIRE_ERROR_COUNT(1, resultB); LUAU_REQUIRE_ERROR_COUNT(1, resultB);
CheckResult resultC = frontend.check("C"); CheckResult resultC = getFrontend().check("C");
LUAU_REQUIRE_ERROR_COUNT(1, resultC); LUAU_REQUIRE_ERROR_COUNT(1, resultC);
} }
@ -1021,18 +1021,18 @@ TEST_CASE_FIXTURE(FrontendFixture, "stats_are_not_reset_between_checks")
return {foo = 1} return {foo = 1}
)"; )";
CheckResult r1 = frontend.check("Module/A"); CheckResult r1 = getFrontend().check("Module/A");
LUAU_REQUIRE_NO_ERRORS(r1); LUAU_REQUIRE_NO_ERRORS(r1);
Frontend::Stats stats1 = frontend.stats; Frontend::Stats stats1 = getFrontend().stats;
CHECK_EQ(2, stats1.files); CHECK_EQ(2, stats1.files);
frontend.markDirty("Module/A"); getFrontend().markDirty("Module/A");
frontend.markDirty("Module/B"); getFrontend().markDirty("Module/B");
CheckResult r2 = frontend.check("Module/A"); CheckResult r2 = getFrontend().check("Module/A");
LUAU_REQUIRE_NO_ERRORS(r2); LUAU_REQUIRE_NO_ERRORS(r2);
Frontend::Stats stats2 = frontend.stats; Frontend::Stats stats2 = getFrontend().stats;
CHECK_EQ(4, stats2.files); CHECK_EQ(4, stats2.files);
} }
@ -1050,19 +1050,19 @@ TEST_CASE_FIXTURE(FrontendFixture, "clearStats")
return {foo = 1} return {foo = 1}
)"; )";
CheckResult r1 = frontend.check("Module/A"); CheckResult r1 = getFrontend().check("Module/A");
LUAU_REQUIRE_NO_ERRORS(r1); LUAU_REQUIRE_NO_ERRORS(r1);
Frontend::Stats stats1 = frontend.stats; Frontend::Stats stats1 = getFrontend().stats;
CHECK_EQ(2, stats1.files); CHECK_EQ(2, stats1.files);
frontend.markDirty("Module/A"); getFrontend().markDirty("Module/A");
frontend.markDirty("Module/B"); getFrontend().markDirty("Module/B");
frontend.clearStats(); getFrontend().clearStats();
CheckResult r2 = frontend.check("Module/A"); CheckResult r2 = getFrontend().check("Module/A");
LUAU_REQUIRE_NO_ERRORS(r2); LUAU_REQUIRE_NO_ERRORS(r2);
Frontend::Stats stats2 = frontend.stats; Frontend::Stats stats2 = getFrontend().stats;
CHECK_EQ(2, stats2.files); CHECK_EQ(2, stats2.files);
} }
@ -1073,9 +1073,9 @@ TEST_CASE_FIXTURE(FrontendFixture, "typecheck_twice_for_ast_types")
local a = 1 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); REQUIRE_EQ(module->astTypes.size(), 1);
auto it = module->astTypes.begin(); auto it = module->astTypes.begin();
@ -1088,7 +1088,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "imported_table_modification_2")
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2)
return; return;
frontend.options.retainFullTypeGraphs = false; getFrontend().options.retainFullTypeGraphs = false;
fileResolver.source["Module/A"] = R"( fileResolver.source["Module/A"] = R"(
--!nonstrict --!nonstrict
@ -1112,13 +1112,13 @@ local b = require(script.Parent.B)
a:b() -- this should error, since A doesn't define a: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); LUAU_REQUIRE_NO_ERRORS(resultA);
CheckResult resultB = frontend.check("Module/B"); CheckResult resultB = getFrontend().check("Module/B");
LUAU_REQUIRE_ERRORS(resultB); LUAU_REQUIRE_ERRORS(resultB);
CheckResult resultC = frontend.check("Module/C"); CheckResult resultC = getFrontend().check("Module/C");
LUAU_REQUIRE_ERRORS(resultC); LUAU_REQUIRE_ERRORS(resultC);
} }
@ -1143,7 +1143,7 @@ return false;
)"; )";
// We don't care about the result. That we haven't crashed is enough. // 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") TEST_CASE("check_without_builtin_next")
@ -1151,7 +1151,6 @@ TEST_CASE("check_without_builtin_next")
TestFileResolver fileResolver; TestFileResolver fileResolver;
TestConfigResolver configResolver; TestConfigResolver configResolver;
Frontend frontend(&fileResolver, &configResolver); Frontend frontend(&fileResolver, &configResolver);
fileResolver.source["Module/A"] = "for k,v in 2 do end"; fileResolver.source["Module/A"] = "for k,v in 2 do end";
fileResolver.source["Module/B"] = "return next"; 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); LUAU_REQUIRE_NO_ERRORS(result);
} }
@ -1212,23 +1211,23 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "reexport_type_alias")
export type TestFileEvent = A.TestFileEvent export type TestFileEvent = A.TestFileEvent
)"; )";
CheckResult result = frontend.check("Module/B"); CheckResult result = getFrontend().check("Module/B");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "module_scope_check") 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"( fileResolver.source["game/A"] = R"(
local a = x local a = x
)"; )";
CheckResult result = frontend.check("game/A"); CheckResult result = getFrontend().check("game/A");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
auto ty = requireType("game/A", "a"); auto ty = requireType("game/A", "a");
@ -1248,18 +1247,18 @@ TEST_CASE_FIXTURE(FrontendFixture, "parse_only")
local b: number = 2 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(getFrontend().sourceNodes.count("game/Gui/Modules/A"));
REQUIRE(frontend.sourceNodes.count("game/Gui/Modules/B")); 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")); CHECK(node->requireSet.contains("game/Gui/Modules/A"));
REQUIRE_EQ(node->requireLocations.size(), 1); REQUIRE_EQ(node->requireLocations.size(), 1);
CHECK_EQ(node->requireLocations[0].second, Luau::Location(Position(2, 18), Position(2, 36))); CHECK_EQ(node->requireLocations[0].second, Luau::Location(Position(2, 18), Position(2, 36)));
// Early parse doesn't cause typechecking to be skipped // 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); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("game/Gui/Modules/A", result.errors[0].moduleName); CHECK_EQ("game/Gui/Modules/A", result.errors[0].moduleName);
@ -1275,15 +1274,15 @@ TEST_CASE_FIXTURE(FrontendFixture, "markdirty_early_return")
{ {
std::vector<ModuleName> markedDirty; std::vector<ModuleName> markedDirty;
frontend.markDirty(moduleName, &markedDirty); getFrontend().markDirty(moduleName, &markedDirty);
CHECK(markedDirty.empty()); CHECK(markedDirty.empty());
} }
frontend.parse(moduleName); getFrontend().parse(moduleName);
{ {
std::vector<ModuleName> markedDirty; std::vector<ModuleName> markedDirty;
frontend.markDirty(moduleName, &markedDirty); getFrontend().markDirty(moduleName, &markedDirty);
CHECK(!markedDirty.empty()); CHECK(!markedDirty.empty());
} }
} }
@ -1302,7 +1301,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "attribute_ices_to_the_correct_module")
try try
{ {
frontend.check("game/one"); getFrontend().check("game/one");
} }
catch (InternalCompilerError& err) catch (InternalCompilerError& err)
{ {
@ -1330,19 +1329,19 @@ TEST_CASE_FIXTURE(FrontendFixture, "checked_modules_have_the_correct_mode")
local a = 10 local a = 10
)"; )";
frontend.check("game/A"); getFrontend().check("game/A");
frontend.check("game/B"); getFrontend().check("game/B");
frontend.check("game/C"); getFrontend().check("game/C");
ModulePtr moduleA = frontend.moduleResolver.getModule("game/A"); ModulePtr moduleA = getFrontend().moduleResolver.getModule("game/A");
REQUIRE(moduleA); REQUIRE(moduleA);
CHECK(moduleA->mode == Mode::NoCheck); CHECK(moduleA->mode == Mode::NoCheck);
ModulePtr moduleB = frontend.moduleResolver.getModule("game/B"); ModulePtr moduleB = getFrontend().moduleResolver.getModule("game/B");
REQUIRE(moduleB); REQUIRE(moduleB);
CHECK(moduleB->mode == Mode::Nonstrict); CHECK(moduleB->mode == Mode::Nonstrict);
ModulePtr moduleC = frontend.moduleResolver.getModule("game/C"); ModulePtr moduleC = getFrontend().moduleResolver.getModule("game/C");
REQUIRE(moduleC); REQUIRE(moduleC);
CHECK(moduleC->mode == Mode::Strict); CHECK(moduleC->mode == Mode::Strict);
} }
@ -1361,17 +1360,17 @@ TEST_CASE_FIXTURE(FrontendFixture, "separate_caches_for_autocomplete")
FrontendOptions opts; FrontendOptions opts;
opts.forAutocomplete = true; 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); REQUIRE(acModule != nullptr);
CHECK(acModule->mode == Mode::Strict); 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); REQUIRE(module != nullptr);
CHECK(module->mode == Mode::Nonstrict); CHECK(module->mode == Mode::Nonstrict);
@ -1391,11 +1390,11 @@ TEST_CASE_FIXTURE(FrontendFixture, "no_separate_caches_with_the_new_solver")
FrontendOptions opts; FrontendOptions opts;
opts.forAutocomplete = true; 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); REQUIRE(module != nullptr);
CHECK(module->mode == Mode::Nonstrict); 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. // isDirty(name) is true, getRequiredScripts should not hit the cache.
frontend.markDirty("game/workspace/MyScript"); getFrontend().markDirty("game/workspace/MyScript");
std::vector<ModuleName> requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); std::vector<ModuleName> requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript");
REQUIRE(requiredScripts.size() == 2); REQUIRE(requiredScripts.size() == 2);
CHECK(requiredScripts[0] == "game/workspace/MyModuleScript"); CHECK(requiredScripts[0] == "game/workspace/MyModuleScript");
CHECK(requiredScripts[1] == "game/workspace/MyModuleScript2"); CHECK(requiredScripts[1] == "game/workspace/MyModuleScript2");
// Call frontend.check first, then getRequiredScripts should hit the cache because isDirty(name) is false. // Call getFrontend().check first, then getRequiredScripts should hit the cache because isDirty(name) is false.
frontend.check("game/workspace/MyScript"); getFrontend().check("game/workspace/MyScript");
requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript");
REQUIRE(requiredScripts.size() == 2); REQUIRE(requiredScripts.size() == 2);
CHECK(requiredScripts[0] == "game/workspace/MyModuleScript"); CHECK(requiredScripts[0] == "game/workspace/MyModuleScript");
CHECK(requiredScripts[1] == "game/workspace/MyModuleScript2"); CHECK(requiredScripts[1] == "game/workspace/MyModuleScript2");
@ -1478,8 +1477,8 @@ TEST_CASE_FIXTURE(FrontendFixture, "get_required_scripts_dirty")
return module return module
)"; )";
frontend.check("game/workspace/MyScript"); getFrontend().check("game/workspace/MyScript");
std::vector<ModuleName> requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); std::vector<ModuleName> requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript");
REQUIRE(requiredScripts.size() == 0); REQUIRE(requiredScripts.size() == 0);
fileResolver.source["game/workspace/MyScript"] = R"( fileResolver.source["game/workspace/MyScript"] = R"(
@ -1487,11 +1486,11 @@ TEST_CASE_FIXTURE(FrontendFixture, "get_required_scripts_dirty")
MyModuleScript.myPrint() MyModuleScript.myPrint()
)"; )";
requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript");
REQUIRE(requiredScripts.size() == 0); REQUIRE(requiredScripts.size() == 0);
frontend.markDirty("game/workspace/MyScript"); getFrontend().markDirty("game/workspace/MyScript");
requiredScripts = frontend.getRequiredScripts("game/workspace/MyScript"); requiredScripts = getFrontend().getRequiredScripts("game/workspace/MyScript");
REQUIRE(requiredScripts.size() == 1); REQUIRE(requiredScripts.size() == 1);
CHECK(requiredScripts[0] == "game/workspace/MyModuleScript"); CHECK(requiredScripts[0] == "game/workspace/MyModuleScript");
} }
@ -1502,10 +1501,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_allocator")
print("Hello World") print("Hello World")
)"; )";
frontend.check("game/workspace/MyScript"); getFrontend().check("game/workspace/MyScript");
ModulePtr module = frontend.moduleResolver.getModule("game/workspace/MyScript"); ModulePtr module = getFrontend().moduleResolver.getModule("game/workspace/MyScript");
SourceModule* source = frontend.getSourceModule("game/workspace/MyScript"); SourceModule* source = getFrontend().getSourceModule("game/workspace/MyScript");
CHECK(module); CHECK(module);
CHECK(source); CHECK(source);
@ -1519,10 +1518,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "check_module_references_correct_ast_root")
print("Hello World") print("Hello World")
)"; )";
frontend.check("game/workspace/MyScript"); getFrontend().check("game/workspace/MyScript");
ModulePtr module = frontend.moduleResolver.getModule("game/workspace/MyScript"); ModulePtr module = getFrontend().moduleResolver.getModule("game/workspace/MyScript");
SourceModule* source = frontend.getSourceModule("game/workspace/MyScript"); SourceModule* source = getFrontend().getSourceModule("game/workspace/MyScript");
CHECK(module); CHECK(module);
CHECK(source); CHECK(source);
@ -1539,19 +1538,19 @@ local c = 3
return {x = a, y = b, z = c} return {x = a, y = b, z = c}
)"; )";
frontend.options.retainFullTypeGraphs = true; getFrontend().options.retainFullTypeGraphs = true;
frontend.check("game/A"); 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->defArena.allocator.empty());
CHECK(!mod->keyArena.allocator.empty()); CHECK(!mod->keyArena.allocator.empty());
// We should check that the dfg arena is empty once retainFullTypeGraphs is unset // We should check that the dfg arena is empty once retainFullTypeGraphs is unset
frontend.options.retainFullTypeGraphs = false; getFrontend().options.retainFullTypeGraphs = false;
frontend.markDirty("game/A"); getFrontend().markDirty("game/A");
frontend.check("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->defArena.allocator.empty());
CHECK(mod->keyArena.allocator.empty()); CHECK(mod->keyArena.allocator.empty());
} }
@ -1573,10 +1572,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_traverse_dependents")
return {d_value = C.c_value} return {d_value = C.c_value}
)"; )";
frontend.check("game/Gui/Modules/D"); getFrontend().check("game/Gui/Modules/D");
std::vector<ModuleName> visited; std::vector<ModuleName> visited;
frontend.traverseDependents( getFrontend().traverseDependents(
"game/Gui/Modules/B", "game/Gui/Modules/B",
[&visited](SourceNode& node) [&visited](SourceNode& node)
{ {
@ -1600,10 +1599,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "test_traverse_dependents_early_exit")
return {c_value = B.hello} return {c_value = B.hello}
)"; )";
frontend.check("game/Gui/Modules/C"); getFrontend().check("game/Gui/Modules/C");
std::vector<ModuleName> visited; std::vector<ModuleName> visited;
frontend.traverseDependents( getFrontend().traverseDependents(
"game/Gui/Modules/A", "game/Gui/Modules/A",
[&visited](SourceNode& node) [&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) auto updateSource = [&](const std::string& name, const std::string& source)
{ {
fileResolver.source[name] = source; fileResolver.source[name] = source;
frontend.markDirty(name); getFrontend().markDirty(name);
}; };
auto validateMatchesRequireLists = [&](const std::string& message) auto validateMatchesRequireLists = [&](const std::string& message)
{ {
DenseHashMap<ModuleName, std::vector<ModuleName>> dependents{{}}; DenseHashMap<ModuleName, std::vector<ModuleName>> dependents{{}};
for (const auto& module : frontend.sourceNodes) for (const auto& module : getFrontend().sourceNodes)
{ {
for (const auto& dep : module.second->requireSet) for (const auto& dep : module.second->requireSet)
dependents[dep].push_back(module.first); dependents[dep].push_back(module.first);
} }
for (const auto& module : frontend.sourceNodes) for (const auto& module : getFrontend().sourceNodes)
{ {
Set<ModuleName>& dependentsForModule = module.second->dependents; Set<ModuleName>& dependentsForModule = module.second->dependents;
for (const auto& dep : dependents[module.first]) 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) auto validateSecondDependsOnFirst = [&](const std::string& from, const std::string& to, bool expected)
{ {
SourceNode& fromNode = *frontend.sourceNodes[from]; SourceNode& fromNode = *getFrontend().sourceNodes[from];
CHECK_MESSAGE( CHECK_MESSAGE(
fromNode.dependents.count(to) == int(expected), fromNode.dependents.count(to) == int(expected),
"Expected " << from << " to " << (expected ? std::string() : std::string("not ")) << "have a reverse dependency on " << to "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) local B = require(Modules.B)
return {c_value = B} return {c_value = B}
)"); )");
frontend.check("game/Gui/Modules/C"); getFrontend().check("game/Gui/Modules/C");
validateMatchesRequireLists("Initial check"); 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"( updateSource("game/Gui/Modules/B", R"(
return 1 return 1
)"); )");
frontend.check("game/Gui/Modules/C"); getFrontend().check("game/Gui/Modules/C");
validateMatchesRequireLists("Removing dependency B->A"); validateMatchesRequireLists("Removing dependency B->A");
validateSecondDependsOnFirst("game/Gui/Modules/A", "game/Gui/Modules/B", false); 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"( updateSource("game/Gui/Modules/B", R"(
return require(game:GetService('Gui').Modules.A) return require(game:GetService('Gui').Modules.A)
)"); )");
frontend.check("game/Gui/Modules/C"); getFrontend().check("game/Gui/Modules/C");
validateMatchesRequireLists("Adding back B->A"); validateMatchesRequireLists("Adding back B->A");
validateSecondDependsOnFirst("game/Gui/Modules/A", "game/Gui/Modules/B", true); 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) local A = require(game:GetService('Gui').Modules.A)
return {d_value = C.c_value} 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"); validateMatchesRequireLists("Adding D->C, D->B, D->A");
validateSecondDependsOnFirst("game/Gui/Modules/A", "game/Gui/Modules/D", true); 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/D", "return require(game:GetService('Gui').Modules.C)");
updateSource("game/Gui/Modules/C", "return require(game:GetService('Gui').Modules.D)"); 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"); validateMatchesRequireLists("Adding cycle D->C, C->D");
validateSecondDependsOnFirst("game/Gui/Modules/C", "game/Gui/Modules/D", true); 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 // B -> A, C -> D, D -> error
{ {
updateSource("game/Gui/Modules/D", "return require(game:GetService('Gui').Modules.C.)"); 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."); validateMatchesRequireLists("Adding error dependency D->C.");
validateSecondDependsOnFirst("game/Gui/Modules/D", "game/Gui/Modules/C", true); 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; FrontendOptions opts;
opts.forAutocomplete = false; opts.forAutocomplete = false;
frontend.check("game/Gui/Modules/B", opts); getFrontend().check("game/Gui/Modules/B", opts);
CHECK(frontend.allModuleDependenciesValid("game/Gui/Modules/B", opts.forAutocomplete)); CHECK(getFrontend().allModuleDependenciesValid("game/Gui/Modules/B", opts.forAutocomplete));
CHECK(!frontend.allModuleDependenciesValid("game/Gui/Modules/B", !opts.forAutocomplete)); CHECK(!getFrontend().allModuleDependenciesValid("game/Gui/Modules/B", !opts.forAutocomplete));
opts.forAutocomplete = true; 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(!getFrontend().allModuleDependenciesValid("game/Gui/Modules/B", opts.forAutocomplete));
CHECK(frontend.allModuleDependenciesValid("game/Gui/Modules/B", !opts.forAutocomplete)); CHECK(getFrontend().allModuleDependenciesValid("game/Gui/Modules/B", !opts.forAutocomplete));
CHECK(frontend.allModuleDependenciesValid("game/Gui/Modules/A", !opts.forAutocomplete)); CHECK(getFrontend().allModuleDependenciesValid("game/Gui/Modules/A", !opts.forAutocomplete));
CHECK(frontend.allModuleDependenciesValid("game/Gui/Modules/A", opts.forAutocomplete)); CHECK(getFrontend().allModuleDependenciesValid("game/Gui/Modules/A", opts.forAutocomplete));
} }
TEST_CASE_FIXTURE(FrontendFixture, "queue_check_simple") TEST_CASE_FIXTURE(FrontendFixture, "queue_check_simple")
@ -1765,10 +1764,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "queue_check_simple")
return {b_value = A.hello} return {b_value = A.hello}
)"; )";
frontend.queueModuleCheck("game/Gui/Modules/B"); getFrontend().queueModuleCheck("game/Gui/Modules/B");
frontend.checkQueuedModules(); getFrontend().checkQueuedModules();
auto result = frontend.getCheckResult("game/Gui/Modules/B", true); auto result = getFrontend().getCheckResult("game/Gui/Modules/B", true);
REQUIRE(result); REQUIRE(result);
LUAU_REQUIRE_NO_ERRORS(*result); LUAU_REQUIRE_NO_ERRORS(*result);
} }
@ -1788,10 +1787,10 @@ TEST_CASE_FIXTURE(FrontendFixture, "queue_check_cycle_instant")
return {b_value = A.hello} return {b_value = A.hello}
)"; )";
frontend.queueModuleCheck("game/Gui/Modules/B"); getFrontend().queueModuleCheck("game/Gui/Modules/B");
frontend.checkQueuedModules(); getFrontend().checkQueuedModules();
auto result = frontend.getCheckResult("game/Gui/Modules/B", true); auto result = getFrontend().getCheckResult("game/Gui/Modules/B", true);
REQUIRE(result); REQUIRE(result);
LUAU_REQUIRE_ERROR_COUNT(2, *result); LUAU_REQUIRE_ERROR_COUNT(2, *result);
CHECK(toString(result->errors[0]) == "Cyclic module dependency: game/Gui/Modules/B -> game/Gui/Modules/A"); 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} return {b_value = A.hello + C.c_value}
)"; )";
frontend.queueModuleCheck("game/Gui/Modules/B"); getFrontend().queueModuleCheck("game/Gui/Modules/B");
frontend.checkQueuedModules(); getFrontend().checkQueuedModules();
auto result = frontend.getCheckResult("game/Gui/Modules/B", true); auto result = getFrontend().getCheckResult("game/Gui/Modules/B", true);
REQUIRE(result); REQUIRE(result);
LUAU_REQUIRE_ERROR_COUNT(2, *result); LUAU_REQUIRE_ERROR_COUNT(2, *result);
CHECK(toString(result->errors[0]) == "Cyclic module dependency: game/Gui/Modules/B -> game/Gui/Modules/A"); 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 --!strict
local a: _luau_ice = 55 local a: _luau_ice = 55
)"; )";
frontend.markDirty(mm); getFrontend().markDirty(mm);
frontend.queueModuleCheck("MainModule"); getFrontend().queueModuleCheck("MainModule");
CHECK_THROWS_AS(frontend.checkQueuedModules(), InternalCompilerError); CHECK_THROWS_AS(getFrontend().checkQueuedModules(), InternalCompilerError);
} }
TEST_SUITE_END(); TEST_SUITE_END();

View file

@ -15,7 +15,7 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(DebugLuauForbidInternalTypes) LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) 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})") 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; TableType tt;
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType}; 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})) -> ()") TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
{ {
ScopedFastFlag sff{FFlag::LuauEagerGeneralization3, true}; ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
auto [aTy, aFree] = freshType(); auto [aTy, aFree] = freshType();
auto [bTy, bFree] = freshType(); auto [bTy, bFree] = freshType();
@ -430,7 +430,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "avoid_cross_module_mutation_in_bidirectional
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true}, {FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true},
{FFlag::LuauEagerGeneralization3, true}, {FFlag::LuauEagerGeneralization4, true},
}; };
fileResolver.source["Module/ListFns"] = R"( fileResolver.source["Module/ListFns"] = R"(
@ -454,12 +454,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "avoid_cross_module_mutation_in_bidirectional
return {} return {}
)"; )";
CheckResult result = frontend.check("Module/ListFns"); CheckResult result = getFrontend().check("Module/ListFns");
auto modListFns = frontend.moduleResolver.getModule("Module/ListFns"); auto modListFns = getFrontend().moduleResolver.getModule("Module/ListFns");
freeze(modListFns->interfaceTypes); freeze(modListFns->interfaceTypes);
freeze(modListFns->internalTypes); freeze(modListFns->internalTypes);
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
CheckResult result2 = frontend.check("Module/B"); CheckResult result2 = getFrontend().check("Module/B");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }

View file

@ -8,16 +8,17 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauEagerGeneralization3); LUAU_FASTFLAG(LuauEagerGeneralization4);
LUAU_FASTFLAG(LuauInferPolarityOfReadWriteProperties)
TEST_SUITE_BEGIN("InferPolarity"); TEST_SUITE_BEGIN("InferPolarity");
TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }") TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
{ {
ScopedFastFlag sff{FFlag::LuauEagerGeneralization3, true}; ScopedFastFlag sff{FFlag::LuauEagerGeneralization4, true};
TypeArena arena; TypeArena arena;
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack); ScopePtr globalScope = std::make_shared<Scope>(getBuiltins()->anyTypePack);
TypeId tType = arena.addType(BlockedType{}); TypeId tType = arena.addType(BlockedType{});
TypeId aType = arena.addType(GenericType{globalScope.get(), "a"}); TypeId aType = arena.addType(GenericType{globalScope.get(), "a"});
@ -48,4 +49,40 @@ TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
CHECK(aGeneric->polarity == Polarity::Negative); CHECK(aGeneric->polarity == Polarity::Negative);
} }
TEST_CASE_FIXTURE(Fixture, "<a, b>({ read x: a, write x: b }) -> ()")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauInferPolarityOfReadWriteProperties, true},
};
TypeArena arena;
ScopePtr globalScope = std::make_shared<Scope>(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<GenericType>(aType);
REQUIRE(aGeneric);
CHECK(aGeneric->polarity == Polarity::Negative);
const GenericType* bGeneric = get<GenericType>(bType);
REQUIRE(bGeneric);
CHECK(bGeneric->polarity == Polarity::Positive);
}
TEST_SUITE_END(); TEST_SUITE_END();

View file

@ -16,7 +16,7 @@ TEST_SUITE_BEGIN("Instantiation2Test");
TEST_CASE_FIXTURE(Fixture, "weird_cyclic_instantiation") TEST_CASE_FIXTURE(Fixture, "weird_cyclic_instantiation")
{ {
TypeArena arena; TypeArena arena;
Scope scope(builtinTypes->anyTypePack); Scope scope(getBuiltins()->anyTypePack);
TypeId genericT = arena.addType(GenericType{"T"}); TypeId genericT = arena.addType(GenericType{"T"});
@ -30,11 +30,11 @@ TEST_CASE_FIXTURE(Fixture, "weird_cyclic_instantiation")
DenseHashMap<TypeId, TypeId> genericSubstitutions{nullptr}; DenseHashMap<TypeId, TypeId> genericSubstitutions{nullptr};
DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions{nullptr}; DenseHashMap<TypePackId, TypePackId> genericPackSubstitutions{nullptr};
TypeId freeTy = arena.freshType(builtinTypes, &scope); TypeId freeTy = arena.freshType(getBuiltins(), &scope);
FreeType* ft = getMutable<FreeType>(freeTy); FreeType* ft = getMutable<FreeType>(freeTy);
REQUIRE(ft); REQUIRE(ft);
ft->lowerBound = idTy; ft->lowerBound = idTy;
ft->upperBound = builtinTypes->unknownType; ft->upperBound = getBuiltins()->unknownType;
genericSubstitutions[genericT] = freeTy; genericSubstitutions[genericT] = freeTy;

View file

@ -16,6 +16,8 @@
#include <memory> #include <memory>
#include <string_view> #include <string_view>
LUAU_FASTFLAG(LuauCodeGenSimplifyImport)
static void luauLibraryConstantLookup(const char* library, const char* member, Luau::CompileConstant* constant) 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 // 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") TEST_CASE("DseInitialStackState")
{ {
ScopedFastFlag luauCodeGenSimplifyImport{FFlag::LuauCodeGenSimplifyImport, true};
CHECK_EQ( CHECK_EQ(
"\n" + getCodegenAssembly(R"( "\n" + getCodegenAssembly(R"(
local function foo() local function foo()
@ -518,17 +522,11 @@ bb_bytecode_0:
JUMP bb_2 JUMP bb_2
bb_2: bb_2:
CHECK_SAFE_ENV exit(3) CHECK_SAFE_ENV exit(3)
JUMP_EQ_TAG K1 (nil), tnil, bb_fallback_4, bb_3 GET_CACHED_IMPORT R1, K1 (nil), 1073741824u ('_'), 4u
bb_3:
%9 = LOAD_TVALUE K1 (nil)
STORE_TVALUE R1, %9
JUMP bb_5
bb_5:
SET_SAVEDPC 7u SET_SAVEDPC 7u
%21 = NEW_TABLE 0u, 0u %14 = NEW_TABLE 0u, 0u
STORE_POINTER R1, %21 STORE_POINTER R1, %14
STORE_TAG R1, ttable STORE_TAG R1, ttable
CHECK_GC
STORE_TAG R0, tnil STORE_TAG R0, tnil
INTERRUPT 9u INTERRUPT 9u
JUMP bb_bytecode_0 JUMP bb_bytecode_0
@ -1569,6 +1567,8 @@ end
TEST_CASE("ForInManualAnnotation") TEST_CASE("ForInManualAnnotation")
{ {
ScopedFastFlag luauCodeGenSimplifyImport{FFlag::LuauCodeGenSimplifyImport, true};
CHECK_EQ( CHECK_EQ(
"\n" + getCodegenAssembly( "\n" + getCodegenAssembly(
R"( R"(
@ -1601,22 +1601,17 @@ bb_bytecode_1:
STORE_DOUBLE R1, 0 STORE_DOUBLE R1, 0
STORE_TAG R1, tnumber STORE_TAG R1, tnumber
CHECK_SAFE_ENV exit(1) CHECK_SAFE_ENV exit(1)
JUMP_EQ_TAG K1 (nil), tnil, bb_fallback_6, bb_5 GET_CACHED_IMPORT R2, K1 (nil), 1073741824u ('ipairs'), 2u
bb_5: %8 = LOAD_TVALUE R0
%9 = LOAD_TVALUE K1 (nil) STORE_TVALUE R3, %8
STORE_TVALUE R2, %9
JUMP bb_7
bb_7:
%15 = LOAD_TVALUE R0
STORE_TVALUE R3, %15
INTERRUPT 4u INTERRUPT 4u
SET_SAVEDPC 5u SET_SAVEDPC 5u
CALL R2, 1i, 3i CALL R2, 1i, 3i
CHECK_SAFE_ENV exit(5) CHECK_SAFE_ENV exit(5)
CHECK_TAG R3, ttable, bb_fallback_8 CHECK_TAG R3, ttable, bb_fallback_5
CHECK_TAG R4, tnumber, bb_fallback_8 CHECK_TAG R4, tnumber, bb_fallback_5
JUMP_CMP_NUM R4, 0, not_eq, bb_fallback_8, bb_9 JUMP_CMP_NUM R4, 0, not_eq, bb_fallback_5, bb_6
bb_9: bb_6:
STORE_TAG R2, tnil STORE_TAG R2, tnil
STORE_POINTER R4, 0i STORE_POINTER R4, 0i
STORE_EXTRA R4, 128i STORE_EXTRA R4, 128i
@ -1624,41 +1619,41 @@ bb_9:
JUMP bb_bytecode_3 JUMP bb_bytecode_3
bb_bytecode_2: bb_bytecode_2:
CHECK_TAG R6, ttable, exit(6) CHECK_TAG R6, ttable, exit(6)
%35 = LOAD_POINTER R6 %28 = LOAD_POINTER R6
%36 = GET_SLOT_NODE_ADDR %35, 6u, K2 ('pos') %29 = GET_SLOT_NODE_ADDR %28, 6u, K2 ('pos')
CHECK_SLOT_MATCH %36, K2 ('pos'), bb_fallback_10 CHECK_SLOT_MATCH %29, K2 ('pos'), bb_fallback_7
%38 = LOAD_TVALUE %36, 0i %31 = LOAD_TVALUE %29, 0i
STORE_TVALUE R8, %38 STORE_TVALUE R8, %31
JUMP bb_11 JUMP bb_8
bb_11: bb_8:
CHECK_TAG R8, tvector, exit(8) CHECK_TAG R8, tvector, exit(8)
%45 = LOAD_FLOAT R8, 0i %38 = LOAD_FLOAT R8, 0i
STORE_DOUBLE R7, %45 STORE_DOUBLE R7, %38
STORE_TAG R7, tnumber STORE_TAG R7, tnumber
CHECK_TAG R1, tnumber, exit(10) CHECK_TAG R1, tnumber, exit(10)
%52 = LOAD_DOUBLE R1 %45 = LOAD_DOUBLE R1
%54 = ADD_NUM %52, %45 %47 = ADD_NUM %45, %38
STORE_DOUBLE R1, %54 STORE_DOUBLE R1, %47
JUMP bb_bytecode_3 JUMP bb_bytecode_3
bb_bytecode_3: bb_bytecode_3:
INTERRUPT 11u INTERRUPT 11u
CHECK_TAG R2, tnil, bb_fallback_13 CHECK_TAG R2, tnil, bb_fallback_10
%60 = LOAD_POINTER R3 %53 = LOAD_POINTER R3
%61 = LOAD_INT R4 %54 = LOAD_INT R4
%62 = GET_ARR_ADDR %60, %61 %55 = GET_ARR_ADDR %53, %54
CHECK_ARRAY_SIZE %60, %61, bb_12 CHECK_ARRAY_SIZE %53, %54, bb_9
%64 = LOAD_TAG %62 %57 = LOAD_TAG %55
JUMP_EQ_TAG %64, tnil, bb_12, bb_14 JUMP_EQ_TAG %57, tnil, bb_9, bb_11
bb_14: bb_11:
%66 = ADD_INT %61, 1i %59 = ADD_INT %54, 1i
STORE_INT R4, %66 STORE_INT R4, %59
%68 = INT_TO_NUM %66 %61 = INT_TO_NUM %59
STORE_DOUBLE R5, %68 STORE_DOUBLE R5, %61
STORE_TAG R5, tnumber STORE_TAG R5, tnumber
%71 = LOAD_TVALUE %62 %64 = LOAD_TVALUE %55
STORE_TVALUE R6, %71 STORE_TVALUE R6, %64
JUMP bb_bytecode_2 JUMP bb_bytecode_2
bb_12: bb_9:
INTERRUPT 13u INTERRUPT 13u
RETURN R1, 1i RETURN R1, 1i
)" )"

View file

@ -8,9 +8,7 @@
#include "doctest.h" #include "doctest.h"
LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LintRedundantNativeAttribute); LUAU_FASTFLAG(LuauEagerGeneralization4);
LUAU_FASTFLAG(LuauDeprecatedAttribute);
LUAU_FASTFLAG(LuauEagerGeneralization3);
using namespace Luau; using namespace Luau;
@ -51,7 +49,7 @@ TEST_CASE_FIXTURE(Fixture, "UnknownGlobal")
TEST_CASE_FIXTURE(Fixture, "DeprecatedGlobal") TEST_CASE_FIXTURE(Fixture, "DeprecatedGlobal")
{ {
// Normally this would be defined externally, so hack it in for testing // 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)"); 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 // Normally this would be defined externally, so hack it in for testing
const char* deprecationReplacementString = ""; 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()"); LintResult result = lint("Version()");
@ -390,7 +388,7 @@ return bar()
TEST_CASE_FIXTURE(Fixture, "ImportUnused") TEST_CASE_FIXTURE(Fixture, "ImportUnused")
{ {
// Normally this would be defined externally, so hack it in for testing // 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"( LintResult result = lint(R"(
local Roact = require(game.Packages.Roact) local Roact = require(game.Packages.Roact)
@ -621,16 +619,16 @@ return foo1
TEST_CASE_FIXTURE(Fixture, "UnknownType") TEST_CASE_FIXTURE(Fixture, "UnknownType")
{ {
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
TableType::Props instanceProps{ TableType::Props instanceProps{
{"ClassName", {builtinTypes->anyType}}, {"ClassName", {getBuiltins()->anyType}},
}; };
TableType instanceTable{instanceProps, std::nullopt, frontend.globals.globalScope->level, Luau::TableState::Sealed}; TableType instanceTable{instanceProps, std::nullopt, getFrontend().globals.globalScope->level, Luau::TableState::Sealed};
TypeId instanceType = frontend.globals.globalTypes.addType(instanceTable); TypeId instanceType = getFrontend().globals.globalTypes.addType(instanceTable);
TypeFun instanceTypeFun{{}, instanceType}; TypeFun instanceTypeFun{{}, instanceType};
frontend.globals.globalScope->exportedTypeBindings["Part"] = instanceTypeFun; getFrontend().globals.globalScope->exportedTypeBindings["Part"] = instanceTypeFun;
LintResult result = lint(R"( LintResult result = lint(R"(
local game = ... 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") TEST_CASE_FIXTURE(Fixture, "use_all_parent_scopes_for_globals")
{ {
ScopePtr testScope = frontend.addEnvironment("Test"); ScopePtr testScope = getFrontend().addEnvironment("Test");
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
frontend.loadDefinitionFile( getFrontend().loadDefinitionFile(
frontend.globals, getFrontend().globals,
testScope, testScope,
R"( R"(
declare Foo: number declare Foo: number
@ -1352,7 +1350,7 @@ TEST_CASE_FIXTURE(Fixture, "use_all_parent_scopes_for_globals")
"@test", "@test",
/* captureComments */ false /* captureComments */ false
); );
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
fileResolver.environments["A"] = "Test"; fileResolver.environments["A"] = "Test";
@ -1521,32 +1519,32 @@ TEST_CASE_FIXTURE(Fixture, "LintHygieneUAF")
TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiTyped") TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiTyped")
{ {
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
TypeId instanceType = frontend.globals.globalTypes.addType(ExternType{"Instance", {}, std::nullopt, std::nullopt, {}, {}, "Test", {}}); TypeId instanceType = getFrontend().globals.globalTypes.addType(ExternType{"Instance", {}, std::nullopt, std::nullopt, {}, {}, "Test", {}});
persist(instanceType); persist(instanceType);
frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType}; getFrontend().globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
getMutable<ExternType>(instanceType)->props = { getMutable<ExternType>(instanceType)->props = {
{"Name", {builtinTypes->stringType}}, {"Name", {getBuiltins()->stringType}},
{"DataCost", {builtinTypes->numberType, /* deprecated= */ true}}, {"DataCost", {getBuiltins()->numberType, /* deprecated= */ true}},
{"Wait", {builtinTypes->anyType, /* deprecated= */ true}}, {"Wait", {getBuiltins()->anyType, /* deprecated= */ true}},
}; };
TypeId colorType = 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<TableType>(colorType)->props = {{"toHSV", {builtinTypes->anyType, /* deprecated= */ true, "Color3:ToHSV"}}}; getMutable<TableType>(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<TableType>(getGlobalBinding(frontend.globals, "table"))) if (TableType* ttv = getMutable<TableType>(getGlobalBinding(getFrontend().globals, "table")))
{ {
ttv->props["foreach"].deprecated = true; ttv->props["foreach"].deprecated = true;
ttv->props["getn"].deprecated = true; ttv->props["getn"].deprecated = true;
ttv->props["getn"].deprecatedSuggestion = "#"; ttv->props["getn"].deprecatedSuggestion = "#";
} }
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
LintResult result = lint(R"( LintResult result = lint(R"(
return function (i: Instance) return function (i: Instance)
@ -1571,7 +1569,7 @@ end
TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiUntyped") TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiUntyped")
{ {
if (TableType* ttv = getMutable<TableType>(getGlobalBinding(frontend.globals, "table"))) if (TableType* ttv = getMutable<TableType>(getGlobalBinding(getFrontend().globals, "table")))
{ {
ttv->props["foreach"].deprecated = true; ttv->props["foreach"].deprecated = true;
ttv->props["getn"].deprecated = true; ttv->props["getn"].deprecated = true;
@ -2337,8 +2335,6 @@ local _ = a <= (b == 0)
TEST_CASE_FIXTURE(Fixture, "RedundantNativeAttribute") TEST_CASE_FIXTURE(Fixture, "RedundantNativeAttribute")
{ {
ScopedFastFlag sff[] = {{FFlag::LintRedundantNativeAttribute, true}};
LintResult result = lint(R"( LintResult result = lint(R"(
--!native --!native

View file

@ -79,22 +79,22 @@ TEST_CASE_FIXTURE(Fixture, "is_within_comment_parse_result")
TEST_CASE_FIXTURE(Fixture, "dont_clone_persistent_primitive") TEST_CASE_FIXTURE(Fixture, "dont_clone_persistent_primitive")
{ {
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
// numberType is persistent. We leave it as-is. // numberType is persistent. We leave it as-is.
TypeId newNumber = clone(builtinTypes->numberType, dest, cloneState); TypeId newNumber = clone(getBuiltins()->numberType, dest, cloneState);
CHECK_EQ(newNumber, builtinTypes->numberType); CHECK_EQ(newNumber, getBuiltins()->numberType);
} }
TEST_CASE_FIXTURE(Fixture, "deepClone_non_persistent_primitive") TEST_CASE_FIXTURE(Fixture, "deepClone_non_persistent_primitive")
{ {
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
// Create a new number type that isn't persistent // Create a new number type that isn't persistent
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
TypeId oldNumber = frontend.globals.globalTypes.addType(PrimitiveType{PrimitiveType::Number}); TypeId oldNumber = getFrontend().globals.globalTypes.addType(PrimitiveType{PrimitiveType::Number});
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
TypeId newNumber = clone(oldNumber, dest, cloneState); TypeId newNumber = clone(oldNumber, dest, cloneState);
CHECK_NE(newNumber, oldNumber); CHECK_NE(newNumber, oldNumber);
@ -128,7 +128,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table")
TypeId ty = requireType("Cyclic"); TypeId ty = requireType("Cyclic");
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
TypeId cloneTy = clone(ty, dest, cloneState); TypeId cloneTy = clone(ty, dest, cloneState);
TableType* ttv = getMutable<TableType>(cloneTy); TableType* ttv = getMutable<TableType>(cloneTy);
@ -164,7 +164,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table_2")
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
TypeId cloneTy = clone(tableTy, dest, cloneState); TypeId cloneTy = clone(tableTy, dest, cloneState);
TableType* ctt = getMutable<TableType>(cloneTy); TableType* ctt = getMutable<TableType>(cloneTy);
REQUIRE(ctt); REQUIRE(ctt);
@ -189,7 +189,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena")
dumpErrors(result); dumpErrors(result);
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
ModulePtr module = frontend.moduleResolver.getModule("MainModule"); ModulePtr module = getFrontend().moduleResolver.getModule("MainModule");
std::optional<TypeId> exports = first(module->returnType); std::optional<TypeId> exports = first(module->returnType);
REQUIRE(bool(exports)); REQUIRE(bool(exports));
@ -202,17 +202,17 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena")
REQUIRE(signType != nullptr); REQUIRE(signType != nullptr);
CHECK(!isInArena(signType, module->interfaceTypes)); CHECK(!isInArena(signType, module->interfaceTypes));
CHECK(isInArena(signType, frontend.globals.globalTypes)); CHECK(isInArena(signType, getFrontend().globals.globalTypes));
} }
TEST_CASE_FIXTURE(Fixture, "deepClone_union") TEST_CASE_FIXTURE(Fixture, "deepClone_union")
{ {
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
TypeId oldUnion = frontend.globals.globalTypes.addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType}}); TypeId oldUnion = getFrontend().globals.globalTypes.addType(UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}});
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
TypeId newUnion = clone(oldUnion, dest, cloneState); TypeId newUnion = clone(oldUnion, dest, cloneState);
CHECK_NE(newUnion, oldUnion); CHECK_NE(newUnion, oldUnion);
@ -223,11 +223,11 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_union")
TEST_CASE_FIXTURE(Fixture, "deepClone_intersection") TEST_CASE_FIXTURE(Fixture, "deepClone_intersection")
{ {
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
TypeId oldIntersection = frontend.globals.globalTypes.addType(IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}); TypeId oldIntersection = getFrontend().globals.globalTypes.addType(IntersectionType{{getBuiltins()->numberType, getBuiltins()->stringType}});
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
TypeId newIntersection = clone(oldIntersection, dest, cloneState); TypeId newIntersection = clone(oldIntersection, dest, cloneState);
CHECK_NE(newIntersection, oldIntersection); CHECK_NE(newIntersection, oldIntersection);
@ -240,7 +240,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
Type exampleMetaClass{ExternType{ Type exampleMetaClass{ExternType{
"ExampleClassMeta", "ExampleClassMeta",
{ {
{"__add", {builtinTypes->anyType}}, {"__add", {getBuiltins()->anyType}},
}, },
std::nullopt, std::nullopt,
std::nullopt, std::nullopt,
@ -252,8 +252,8 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
Type exampleClass{ExternType{ Type exampleClass{ExternType{
"ExampleClass", "ExampleClass",
{ {
{"PropOne", {builtinTypes->numberType}}, {"PropOne", {getBuiltins()->numberType}},
{"PropTwo", {builtinTypes->stringType}}, {"PropTwo", {getBuiltins()->stringType}},
}, },
std::nullopt, std::nullopt,
&exampleMetaClass, &exampleMetaClass,
@ -264,7 +264,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
}}; }};
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
TypeId cloned = clone(&exampleClass, dest, cloneState); TypeId cloned = clone(&exampleClass, dest, cloneState);
const ExternType* etv = get<ExternType>(cloned); const ExternType* etv = get<ExternType>(cloned);
@ -281,16 +281,16 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
TEST_CASE_FIXTURE(Fixture, "clone_free_types") TEST_CASE_FIXTURE(Fixture, "clone_free_types")
{ {
TypeArena arena; TypeArena arena;
TypeId freeTy = freshType(NotNull{&arena}, builtinTypes, nullptr); TypeId freeTy = freshType(NotNull{&arena}, getBuiltins(), nullptr);
TypePackVar freeTp(FreeTypePack{TypeLevel{}}); TypePackVar freeTp(FreeTypePack{TypeLevel{}});
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
TypeId clonedTy = clone(freeTy, dest, cloneState); TypeId clonedTy = clone(freeTy, dest, cloneState);
CHECK(get<FreeType>(clonedTy)); CHECK(get<FreeType>(clonedTy));
cloneState = {builtinTypes}; cloneState = {getBuiltins()};
TypePackId clonedTp = clone(&freeTp, dest, cloneState); TypePackId clonedTp = clone(&freeTp, dest, cloneState);
CHECK(get<FreeTypePack>(clonedTp)); CHECK(get<FreeTypePack>(clonedTp));
} }
@ -302,7 +302,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_free_tables")
ttv->state = TableState::Free; ttv->state = TableState::Free;
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
TypeId cloned = clone(&tableTy, dest, cloneState); TypeId cloned = clone(&tableTy, dest, cloneState);
const TableType* clonedTtv = get<TableType>(cloned); const TableType* clonedTtv = get<TableType>(cloned);
@ -323,7 +323,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_self_property")
return a; return a;
)"; )";
CheckResult result = frontend.check("Module/A"); CheckResult result = getFrontend().check("Module/A");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
fileResolver.source["Module/B"] = R"( fileResolver.source["Module/B"] = R"(
@ -332,7 +332,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_self_property")
return a.foo(5) return a.foo(5)
)"; )";
result = frontend.check("Module/B"); result = getFrontend().check("Module/B");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
@ -357,7 +357,7 @@ TEST_CASE_FIXTURE(Fixture, "clone_iteration_limit")
} }
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
TypeId ty = clone(table, dest, cloneState); TypeId ty = clone(table, dest, cloneState);
CHECK(get<ErrorType>(ty)); CHECK(get<ErrorType>(ty));
@ -373,14 +373,14 @@ TEST_CASE_FIXTURE(Fixture, "clone_cyclic_union")
{ {
TypeArena src; TypeArena src;
TypeId u = src.addType(UnionType{{builtinTypes->numberType, builtinTypes->stringType}}); TypeId u = src.addType(UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}});
UnionType* uu = getMutable<UnionType>(u); UnionType* uu = getMutable<UnionType>(u);
REQUIRE(uu); REQUIRE(uu);
uu->options.push_back(u); uu->options.push_back(u);
TypeArena dest; TypeArena dest;
CloneState cloneState{builtinTypes}; CloneState cloneState{getBuiltins()};
TypeId cloned = clone(u, dest, cloneState); TypeId cloned = clone(u, dest, cloneState);
REQUIRE(cloned); REQUIRE(cloned);
@ -389,8 +389,8 @@ TEST_CASE_FIXTURE(Fixture, "clone_cyclic_union")
REQUIRE(clonedUnion); REQUIRE(clonedUnion);
REQUIRE(3 == clonedUnion->options.size()); REQUIRE(3 == clonedUnion->options.size());
CHECK(builtinTypes->numberType == clonedUnion->options[0]); CHECK(getBuiltins()->numberType == clonedUnion->options[0]);
CHECK(builtinTypes->stringType == clonedUnion->options[1]); CHECK(getBuiltins()->stringType == clonedUnion->options[1]);
CHECK(cloned == clonedUnion->options[2]); CHECK(cloned == clonedUnion->options[2]);
} }
@ -403,10 +403,10 @@ type B = A
FrontendOptions opts; FrontendOptions opts;
opts.retainFullTypeGraphs = false; opts.retainFullTypeGraphs = false;
CheckResult result = frontend.check("Module/A", opts); CheckResult result = getFrontend().check("Module/A", opts);
LUAU_REQUIRE_ERRORS(result); LUAU_REQUIRE_ERRORS(result);
auto mod = frontend.moduleResolver.getModule("Module/A"); auto mod = getFrontend().moduleResolver.getModule("Module/A");
auto it = mod->exportedTypeBindings.find("A"); auto it = mod->exportedTypeBindings.find("A");
REQUIRE(it != mod->exportedTypeBindings.end()); REQUIRE(it != mod->exportedTypeBindings.end());
@ -429,11 +429,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_reexports")
return {} return {}
)"; )";
CheckResult result = frontend.check("Module/B"); CheckResult result = getFrontend().check("Module/B");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
ModulePtr modA = frontend.moduleResolver.getModule("Module/A"); ModulePtr modA = getFrontend().moduleResolver.getModule("Module/A");
ModulePtr modB = frontend.moduleResolver.getModule("Module/B"); ModulePtr modB = getFrontend().moduleResolver.getModule("Module/B");
REQUIRE(modA); REQUIRE(modA);
REQUIRE(modB); REQUIRE(modB);
auto modAiter = modA->exportedTypeBindings.find("A"); auto modAiter = modA->exportedTypeBindings.find("A");
@ -460,12 +460,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values")
return exports return exports
)"; )";
CheckResult result = frontend.check("Module/B"); CheckResult result = getFrontend().check("Module/B");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
ModulePtr modA = frontend.moduleResolver.getModule("Module/A"); ModulePtr modA = getFrontend().moduleResolver.getModule("Module/A");
REQUIRE(modA); REQUIRE(modA);
ModulePtr modB = frontend.moduleResolver.getModule("Module/B"); ModulePtr modB = getFrontend().moduleResolver.getModule("Module/B");
REQUIRE(modB); REQUIRE(modB);
std::optional<TypeId> typeA = first(modA->returnType); std::optional<TypeId> typeA = first(modA->returnType);
@ -498,7 +498,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_table_bound_to_table_bound_to_table")
getMutable<TableType>(b)->boundTo = c; getMutable<TableType>(b)->boundTo = c;
TypeArena dest; TypeArena dest;
CloneState state{builtinTypes}; CloneState state{getBuiltins()};
TypeId res = clone(a, dest, state); TypeId res = clone(a, dest, state);
REQUIRE(dest.types.size() == 1); REQUIRE(dest.types.size() == 1);
@ -513,11 +513,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_a_bound_type_to_a_persistent_type")
{ {
TypeArena arena; TypeArena arena;
TypeId boundTo = arena.addType(BoundType{builtinTypes->numberType}); TypeId boundTo = arena.addType(BoundType{getBuiltins()->numberType});
REQUIRE(builtinTypes->numberType->persistent); REQUIRE(getBuiltins()->numberType->persistent);
TypeArena dest; TypeArena dest;
CloneState state{builtinTypes}; CloneState state{getBuiltins()};
TypeId res = clone(boundTo, dest, state); TypeId res = clone(boundTo, dest, state);
REQUIRE(res == follow(boundTo)); REQUIRE(res == follow(boundTo));
@ -527,11 +527,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "clone_a_bound_typepack_to_a_persistent_typep
{ {
TypeArena arena; TypeArena arena;
TypePackId boundTo = arena.addTypePack(BoundTypePack{builtinTypes->neverTypePack}); TypePackId boundTo = arena.addTypePack(BoundTypePack{getBuiltins()->neverTypePack});
REQUIRE(builtinTypes->neverTypePack->persistent); REQUIRE(getBuiltins()->neverTypePack->persistent);
TypeArena dest; TypeArena dest;
CloneState state{builtinTypes}; CloneState state{getBuiltins()};
TypePackId res = clone(boundTo, dest, state); TypePackId res = clone(boundTo, dest, state);
REQUIRE(res == follow(boundTo)); REQUIRE(res == follow(boundTo));
@ -556,7 +556,7 @@ for i,v in x do
end end
)"); )");
auto& module = frontend.moduleResolver.getModule("MainModule"); auto& module = getFrontend().moduleResolver.getModule("MainModule");
CHECK(module->getModuleScope()->children.size() == 7); CHECK(module->getModuleScope()->children.size() == 7);
} }

View file

@ -65,7 +65,9 @@ struct NonStrictTypeCheckerFixture : Fixture
NonStrictTypeCheckerFixture() NonStrictTypeCheckerFixture()
{ {
registerHiddenTypes(&frontend); // Force the frontend
getFrontend();
registerHiddenTypes(getFrontend());
registerTestTypes(); registerTestTypes();
} }
@ -86,7 +88,7 @@ struct NonStrictTypeCheckerFixture : Fixture
}; };
LoadDefinitionFileResult res = loadDefinition(definitions); LoadDefinitionFileResult res = loadDefinition(definitions);
LUAU_ASSERT(res.success); LUAU_ASSERT(res.success);
return frontend.check(moduleName); return getFrontend().check(moduleName);
} }
std::string definitions = R"BUILTIN_SRC( std::string definitions = R"BUILTIN_SRC(
@ -639,14 +641,14 @@ buffer.readi8(b, 0)
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls") TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls")
{ {
Luau::unfreeze(frontend.globals.globalTypes); Luau::unfreeze(getFrontend().globals.globalTypes);
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); Luau::unfreeze(getFrontend().globalsForAutocomplete.globalTypes);
registerBuiltinGlobals(frontend, frontend.globals); registerBuiltinGlobals(getFrontend(), getFrontend().globals);
registerTestTypes(); registerTestTypes();
Luau::freeze(frontend.globals.globalTypes); Luau::freeze(getFrontend().globals.globalTypes);
Luau::freeze(frontend.globalsForAutocomplete.globalTypes); Luau::freeze(getFrontend().globalsForAutocomplete.globalTypes);
CheckResult result = checkNonStrict(R"( CheckResult result = checkNonStrict(R"(
local test = "test" local test = "test"
@ -656,7 +658,7 @@ TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls")
LUAU_REQUIRE_NO_ERRORS(result); 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"( CheckResult result = check(Mode::Nonstrict, R"(
foo = 5 foo = 5

View file

@ -67,7 +67,7 @@ TEST_CASE_FIXTURE(Fixture, "return_annotation_is_still_checked")
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
REQUIRE_NE(*builtinTypes->anyType, *requireType("foo")); REQUIRE_NE(*getBuiltins()->anyType, *requireType("foo"));
} }
#endif #endif
@ -111,7 +111,7 @@ TEST_CASE_FIXTURE(Fixture, "locals_are_any_by_default")
LUAU_REQUIRE_NO_ERRORS(result); 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") 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(); TypeId fooProp = ttv->props["foo"].type();
REQUIRE(fooProp != nullptr); REQUIRE(fooProp != nullptr);
CHECK_EQ(*fooProp, *builtinTypes->anyType); CHECK_EQ(*fooProp, *getBuiltins()->anyType);
} }
TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any") 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<TableType>(requireType("T")); TableType* ttv = getMutable<TableType>(requireType("T"));
REQUIRE_MESSAGE(ttv, "Should be a table: " << toString(requireType("T"))); REQUIRE_MESSAGE(ttv, "Should be a table: " << toString(requireType("T")));
CHECK_EQ(*builtinTypes->anyType, *ttv->props["one"].type()); CHECK_EQ(*getBuiltins()->anyType, *ttv->props["one"].type());
CHECK_EQ(*builtinTypes->anyType, *ttv->props["two"].type()); CHECK_EQ(*getBuiltins()->anyType, *ttv->props["two"].type());
CHECK_MESSAGE(get<FunctionType>(follow(ttv->props["three"].type())), "Should be a function: " << *ttv->props["three"].type()); CHECK_MESSAGE(get<FunctionType>(follow(ttv->props["three"].type())), "Should be a function: " << *ttv->props["three"].type());
} }

View file

@ -32,9 +32,9 @@ struct IsSubtypeFixture : Fixture
if (!module->hasModuleScope()) if (!module->hasModuleScope())
FAIL("isSubtype: module scope data is not available"); 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 } // namespace
@ -325,13 +325,13 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "cyclic_table")
TEST_CASE_FIXTURE(IsSubtypeFixture, "extern_types") TEST_CASE_FIXTURE(IsSubtypeFixture, "extern_types")
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
check(""); // Ensure that we have a main Module. check(""); // Ensure that we have a main Module.
TypeId p = frontend.globals.globalScope->lookupType("Parent")->type; TypeId p = getFrontend().globals.globalScope->lookupType("Parent")->type;
TypeId c = frontend.globals.globalScope->lookupType("Child")->type; TypeId c = getFrontend().globals.globalScope->lookupType("Child")->type;
TypeId u = frontend.globals.globalScope->lookupType("Unrelated")->type; TypeId u = getFrontend().globals.globalScope->lookupType("Unrelated")->type;
CHECK(isSubtype(c, p)); CHECK(isSubtype(c, p));
CHECK(!isSubtype(p, c)); CHECK(!isSubtype(p, c));
@ -400,10 +400,10 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "error_suppression")
{ {
check(""); check("");
TypeId any = builtinTypes->anyType; TypeId any = getBuiltins()->anyType;
TypeId err = builtinTypes->errorType; TypeId err = getBuiltins()->errorType;
TypeId str = builtinTypes->stringType; TypeId str = getBuiltins()->stringType;
TypeId unk = builtinTypes->unknownType; TypeId unk = getBuiltins()->unknownType;
CHECK(!isSubtype(any, err)); CHECK(!isSubtype(any, err));
CHECK(isSubtype(err, any)); CHECK(isSubtype(err, any));
@ -447,12 +447,12 @@ struct NormalizeFixture : Fixture
TypeArena arena; TypeArena arena;
InternalErrorReporter iceHandler; InternalErrorReporter iceHandler;
UnifierSharedState unifierState{&iceHandler}; UnifierSharedState unifierState{&iceHandler};
Normalizer normalizer{&arena, builtinTypes, NotNull{&unifierState}}; Normalizer normalizer{&arena, getBuiltins(), NotNull{&unifierState}};
Scope globalScope{builtinTypes->anyTypePack}; Scope globalScope{getBuiltins()->anyTypePack};
NormalizeFixture() NormalizeFixture()
{ {
registerHiddenTypes(&frontend); registerHiddenTypes(getFrontend());
} }
std::shared_ptr<const NormalizedType> toNormalizedType(const std::string& annotation, int expectedErrors = 0) std::shared_ptr<const NormalizedType> 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. // 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}}); TypeId c = arena.addType(IntersectionType{{a, a}});
std::shared_ptr<const NormalizedType> n = normalizer.normalize(c); std::shared_ptr<const NormalizedType> 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") TEST_CASE_FIXTURE(BuiltinsFixture, "skip_force_normal_on_external_types")
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
CheckResult result = check(R"( CheckResult result = check(R"(
export type t0 = { a: Child } 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") TEST_CASE_FIXTURE(NormalizeFixture, "unions_of_extern_types")
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
CHECK("Parent | Unrelated" == toString(normal("Parent | Unrelated"))); CHECK("Parent | Unrelated" == toString(normal("Parent | Unrelated")));
CHECK("Parent" == toString(normal("Parent | Child"))); CHECK("Parent" == toString(normal("Parent | Child")));
CHECK("Parent | Unrelated" == toString(normal("Parent | Child | Unrelated"))); 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") TEST_CASE_FIXTURE(NormalizeFixture, "intersections_of_extern_types")
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
CHECK("Child" == toString(normal("Parent & Child"))); CHECK("Child" == toString(normal("Parent & Child")));
CHECK("never" == toString(normal("Child & Unrelated"))); CHECK("never" == toString(normal("Child & Unrelated")));
} }
TEST_CASE_FIXTURE(NormalizeFixture, "narrow_union_of_extern_types_with_intersection") TEST_CASE_FIXTURE(NormalizeFixture, "narrow_union_of_extern_types_with_intersection")
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
CHECK("Child" == toString(normal("(Child | Unrelated) & Child"))); CHECK("Child" == toString(normal("(Child | Unrelated) & Child")));
} }
@ -828,8 +828,8 @@ TEST_CASE_FIXTURE(NormalizeFixture, "cyclic_union")
{ {
// T where T = any & (number | T) // T where T = any & (number | T)
TypeId t = arena.addType(BlockedType{}); TypeId t = arena.addType(BlockedType{});
TypeId u = arena.addType(UnionType{{builtinTypes->numberType, t}}); TypeId u = arena.addType(UnionType{{getBuiltins()->numberType, t}});
asMutable(t)->ty.emplace<IntersectionType>(IntersectionType{{builtinTypes->anyType, u}}); asMutable(t)->ty.emplace<IntersectionType>(IntersectionType{{getBuiltins()->anyType, u}});
std::shared_ptr<const NormalizedType> nt = normalizer.normalize(t); std::shared_ptr<const NormalizedType> nt = normalizer.normalize(t);
REQUIRE(nt); REQUIRE(nt);
@ -841,8 +841,8 @@ TEST_CASE_FIXTURE(NormalizeFixture, "cyclic_union_of_intersection")
{ {
// t1 where t1 = (string & t1) | string // t1 where t1 = (string & t1) | string
TypeId boundTy = arena.addType(BlockedType{}); TypeId boundTy = arena.addType(BlockedType{});
TypeId intersectTy = arena.addType(IntersectionType{{builtinTypes->stringType, boundTy}}); TypeId intersectTy = arena.addType(IntersectionType{{getBuiltins()->stringType, boundTy}});
TypeId unionTy = arena.addType(UnionType{{builtinTypes->stringType, intersectTy}}); TypeId unionTy = arena.addType(UnionType{{getBuiltins()->stringType, intersectTy}});
asMutable(boundTy)->reassign(Type{BoundType{unionTy}}); asMutable(boundTy)->reassign(Type{BoundType{unionTy}});
std::shared_ptr<const NormalizedType> nt = normalizer.normalize(unionTy); std::shared_ptr<const NormalizedType> nt = normalizer.normalize(unionTy);
@ -854,8 +854,8 @@ TEST_CASE_FIXTURE(NormalizeFixture, "cyclic_intersection_of_unions")
{ {
// t1 where t1 = (string & t1) | string // t1 where t1 = (string & t1) | string
TypeId boundTy = arena.addType(BlockedType{}); TypeId boundTy = arena.addType(BlockedType{});
TypeId unionTy = arena.addType(UnionType{{builtinTypes->stringType, boundTy}}); TypeId unionTy = arena.addType(UnionType{{getBuiltins()->stringType, boundTy}});
TypeId intersectionTy = arena.addType(IntersectionType{{builtinTypes->stringType, unionTy}}); TypeId intersectionTy = arena.addType(IntersectionType{{getBuiltins()->stringType, unionTy}});
asMutable(boundTy)->reassign(Type{BoundType{intersectionTy}}); asMutable(boundTy)->reassign(Type{BoundType{intersectionTy}});
std::shared_ptr<const NormalizedType> nt = normalizer.normalize(intersectionTy); std::shared_ptr<const NormalizedType> nt = normalizer.normalize(intersectionTy);
@ -870,7 +870,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "crazy_metatable")
TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_extern_types") TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_extern_types")
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
CHECK("(Parent & ~Child) | Unrelated" == toString(normal("(Parent & Not<Child>) | Unrelated"))); CHECK("(Parent & ~Child) | Unrelated" == toString(normal("(Parent & Not<Child>) | Unrelated")));
CHECK("((class & ~Child) | boolean | buffer | function | number | string | table | thread)?" == toString(normal("Not<Child>"))); CHECK("((class & ~Child) | boolean | buffer | function | number | string | table | thread)?" == toString(normal("Not<Child>")));
CHECK("never" == toString(normal("Not<Parent> & Child"))); CHECK("never" == toString(normal("Not<Parent> & Child")));
@ -885,13 +885,13 @@ TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_extern_types")
TEST_CASE_FIXTURE(NormalizeFixture, "extern_types_and_unknown") TEST_CASE_FIXTURE(NormalizeFixture, "extern_types_and_unknown")
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
CHECK("Parent" == toString(normal("Parent & unknown"))); CHECK("Parent" == toString(normal("Parent & unknown")));
} }
TEST_CASE_FIXTURE(NormalizeFixture, "extern_types_and_never") TEST_CASE_FIXTURE(NormalizeFixture, "extern_types_and_never")
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
CHECK("never" == toString(normal("Parent & never"))); 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") TEST_CASE_FIXTURE(NormalizeFixture, "normalize_is_exactly_number")
{ {
std::shared_ptr<const NormalizedType> number = normalizer.normalize(builtinTypes->numberType); std::shared_ptr<const NormalizedType> number = normalizer.normalize(getBuiltins()->numberType);
// 1. all types for which Types::number say true for, NormalizedType::isExactlyNumber should say true as well // 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` // 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<const NormalizedType> normIntersection = normalizer.normalize(intersection); std::shared_ptr<const NormalizedType> normIntersection = normalizer.normalize(intersection);
CHECK(normIntersection->isExactlyNumber()); CHECK(normIntersection->isExactlyNumber());
// 3. isExactlyNumber should reject things that are definitely not precisely numbers `number | any` // 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<const NormalizedType> unionIntersection = normalizer.normalize(yoonion); std::shared_ptr<const NormalizedType> unionIntersection = normalizer.normalize(yoonion);
CHECK(!unionIntersection->isExactlyNumber()); CHECK(!unionIntersection->isExactlyNumber());
} }
@ -972,15 +972,15 @@ TEST_CASE_FIXTURE(NormalizeFixture, "read_only_props_3")
TEST_CASE_FIXTURE(NormalizeFixture, "final_types_are_cached") TEST_CASE_FIXTURE(NormalizeFixture, "final_types_are_cached")
{ {
std::shared_ptr<const NormalizedType> na1 = normalizer.normalize(builtinTypes->numberType); std::shared_ptr<const NormalizedType> na1 = normalizer.normalize(getBuiltins()->numberType);
std::shared_ptr<const NormalizedType> na2 = normalizer.normalize(builtinTypes->numberType); std::shared_ptr<const NormalizedType> na2 = normalizer.normalize(getBuiltins()->numberType);
CHECK(na1 == na2); CHECK(na1 == na2);
} }
TEST_CASE_FIXTURE(NormalizeFixture, "non_final_types_can_be_normalized_but_are_not_cached") 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<const NormalizedType> na1 = normalizer.normalize(a); std::shared_ptr<const NormalizedType> na1 = normalizer.normalize(a);
std::shared_ptr<const NormalizedType> na2 = normalizer.normalize(a); std::shared_ptr<const NormalizedType> 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") TEST_CASE_FIXTURE(NormalizeFixture, "intersect_with_not_unknown")
{ {
TypeId notUnknown = arena.addType(NegationType{builtinTypes->unknownType}); TypeId notUnknown = arena.addType(NegationType{getBuiltins()->unknownType});
TypeId type = arena.addType(IntersectionType{{builtinTypes->numberType, notUnknown}}); TypeId type = arena.addType(IntersectionType{{getBuiltins()->numberType, notUnknown}});
std::shared_ptr<const NormalizedType> normalized = normalizer.normalize(type); std::shared_ptr<const NormalizedType> normalized = normalizer.normalize(type);
CHECK("never" == toString(normalizer.typeFromNormal(*normalized.get()))); 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}; ScopedFastFlag sff{FFlag::LuauSolverV2, true};
// { x: ~(false?) } // { 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? }? // { x: number? }?
TypeId t2 = arena.addType(UnionType{ TypeId t2 = arena.addType(UnionType{
{arena.addType(TableType{TableType::Props{{"x", builtinTypes->optionalNumberType}}, std::nullopt, TypeLevel{}, TableState::Sealed}), {arena.addType(TableType{TableType::Props{{"x", getBuiltins()->optionalNumberType}}, std::nullopt, TypeLevel{}, TableState::Sealed}),
builtinTypes->nilType} getBuiltins()->nilType}
}); });
TypeId intersection = arena.addType(IntersectionType{{t2, t1}}); 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 {FFlag::LuauSolverV2, true}, // Only because it affects the stringification of free types
}; };
TypeId freeTy = arena.freshType(builtinTypes, &globalScope); TypeId freeTy = arena.freshType(getBuiltins(), &globalScope);
TypeId notTruthy = arena.addType(NegationType{builtinTypes->truthyType}); // ~~(false?) TypeId notTruthy = arena.addType(NegationType{getBuiltins()->truthyType}); // ~~(false?)
TypeId intersectionTy = arena.addType(IntersectionType{{freeTy, notTruthy}}); // 'a & ~~(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}, {FFlag::LuauNormalizationReorderFreeTypeIntersect, true},
}; };
TypeId freeTy = arena.freshType(builtinTypes, &globalScope); TypeId freeTy = arena.freshType(getBuiltins(), &globalScope);
TypeId orderA = arena.addType(IntersectionType{{freeTy, builtinTypes->stringType}}); TypeId orderA = arena.addType(IntersectionType{{freeTy, getBuiltins()->stringType}});
auto normA = normalizer.normalize(orderA); auto normA = normalizer.normalize(orderA);
REQUIRE(normA); REQUIRE(normA);
CHECK_EQ("'a & string", toString(normalizer.typeFromNormal(*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); auto normB = normalizer.normalize(orderB);
REQUIRE(normB); REQUIRE(normB);
// Prior to LuauNormalizationReorderFreeTypeIntersect this became `never` :skull: // Prior to LuauNormalizationReorderFreeTypeIntersect this became `never` :skull:

View file

@ -21,30 +21,30 @@ struct SimplifyFixture : Fixture
ToStringOptions opts; ToStringOptions opts;
Scope scope{builtinTypes->anyTypePack}; Scope scope{getBuiltins()->anyTypePack};
const TypeId anyTy = builtinTypes->anyType; const TypeId anyTy = getBuiltins()->anyType;
const TypeId unknownTy = builtinTypes->unknownType; const TypeId unknownTy = getBuiltins()->unknownType;
const TypeId neverTy = builtinTypes->neverType; const TypeId neverTy = getBuiltins()->neverType;
const TypeId errorTy = builtinTypes->errorType; const TypeId errorTy = getBuiltins()->errorType;
const TypeId functionTy = builtinTypes->functionType; const TypeId functionTy = getBuiltins()->functionType;
const TypeId tableTy = builtinTypes->tableType; const TypeId tableTy = getBuiltins()->tableType;
const TypeId numberTy = builtinTypes->numberType; const TypeId numberTy = getBuiltins()->numberType;
const TypeId stringTy = builtinTypes->stringType; const TypeId stringTy = getBuiltins()->stringType;
const TypeId booleanTy = builtinTypes->booleanType; const TypeId booleanTy = getBuiltins()->booleanType;
const TypeId nilTy = builtinTypes->nilType; const TypeId nilTy = getBuiltins()->nilType;
const TypeId classTy = builtinTypes->externType; const TypeId classTy = getBuiltins()->externType;
const TypeId trueTy = builtinTypes->trueType; const TypeId trueTy = getBuiltins()->trueType;
const TypeId falseTy = builtinTypes->falseType; const TypeId falseTy = getBuiltins()->falseType;
const TypeId truthyTy = builtinTypes->truthyType; const TypeId truthyTy = getBuiltins()->truthyType;
const TypeId falsyTy = builtinTypes->falsyType; 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 genericTy = arena->addType(GenericType{});
const TypeId blockedTy = arena->addType(BlockedType{}); const TypeId blockedTy = arena->addType(BlockedType{});
const TypeId pendingTy = arena->addType(PendingExpansionType{{}, {}, {}, {}}); const TypeId pendingTy = arena->addType(PendingExpansionType{{}, {}, {}, {}});
@ -55,7 +55,7 @@ struct SimplifyFixture : Fixture
const TypePackId emptyTypePack = arena->addTypePack({}); const TypePackId emptyTypePack = arena->addTypePack({});
const TypeId fn1Ty = arena->addType(FunctionType{emptyTypePack, emptyTypePack}); 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 parentClassTy = nullptr;
TypeId childClassTy = nullptr; TypeId childClassTy = nullptr;
@ -66,17 +66,17 @@ struct SimplifyFixture : Fixture
SimplifyFixture() SimplifyFixture()
{ {
createSomeExternTypes(&frontend); createSomeExternTypes(getFrontend());
parentClassTy = frontend.globals.globalScope->linearSearchForBinding("Parent")->typeId; parentClassTy = getFrontend().globals.globalScope->linearSearchForBinding("Parent")->typeId;
childClassTy = frontend.globals.globalScope->linearSearchForBinding("Child")->typeId; childClassTy = getFrontend().globals.globalScope->linearSearchForBinding("Child")->typeId;
anotherChildClassTy = frontend.globals.globalScope->linearSearchForBinding("AnotherChild")->typeId; anotherChildClassTy = getFrontend().globals.globalScope->linearSearchForBinding("AnotherChild")->typeId;
unrelatedClassTy = frontend.globals.globalScope->linearSearchForBinding("Unrelated")->typeId; unrelatedClassTy = getFrontend().globals.globalScope->linearSearchForBinding("Unrelated")->typeId;
} }
TypeId intersect(TypeId a, TypeId b) 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) std::string intersectStr(TypeId a, TypeId b)
@ -110,7 +110,7 @@ struct SimplifyFixture : Fixture
TypeId union_(TypeId a, TypeId b) 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(t1, nilTy));
// CHECK(nilTy == intersect(nilTy, t1)); // TODO? // 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") 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") TEST_CASE_FIXTURE(SimplifyFixture, "cyclic_never_union_and_string")
{ {
// t1 where t1 = never | t1 // 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<UnionType>(leftType); UnionType* leftUnion = getMutable<UnionType>(leftType);
REQUIRE(leftUnion); REQUIRE(leftUnion);
leftUnion->options[0] = leftType; leftUnion->options[0] = leftType;
CHECK(builtinTypes->stringType == union_(leftType, builtinTypes->stringType)); CHECK(getBuiltins()->stringType == union_(leftType, getBuiltins()->stringType));
} }
TEST_SUITE_END(); TEST_SUITE_END();

File diff suppressed because it is too large Load diff

View file

@ -15,7 +15,7 @@ struct ToDotClassFixture : Fixture
{ {
ToDotClassFixture() ToDotClassFixture()
{ {
TypeArena& arena = frontend.globals.globalTypes; TypeArena& arena = getFrontend().globals.globalTypes;
unfreeze(arena); unfreeze(arena);
@ -23,17 +23,17 @@ struct ToDotClassFixture : Fixture
TypeId baseClassInstanceType = arena.addType(ExternType{"BaseClass", {}, std::nullopt, baseClassMetaType, {}, {}, "Test", {}}); TypeId baseClassInstanceType = arena.addType(ExternType{"BaseClass", {}, std::nullopt, baseClassMetaType, {}, {}, "Test", {}});
getMutable<ExternType>(baseClassInstanceType)->props = { getMutable<ExternType>(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", {}}); TypeId childClassInstanceType = arena.addType(ExternType{"ChildClass", {}, baseClassInstanceType, std::nullopt, {}, {}, "Test", {}});
getMutable<ExternType>(childClassInstanceType)->props = { getMutable<ExternType>(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); persist(ty.type);
freeze(arena); freeze(arena);
@ -48,35 +48,35 @@ TEST_CASE_FIXTURE(Fixture, "primitive")
R"(digraph graphname { R"(digraph graphname {
n1 [label="nil"]; n1 [label="nil"];
})", })",
toDot(builtinTypes->nilType) toDot(getBuiltins()->nilType)
); );
CHECK_EQ( CHECK_EQ(
R"(digraph graphname { R"(digraph graphname {
n1 [label="number"]; n1 [label="number"];
})", })",
toDot(builtinTypes->numberType) toDot(getBuiltins()->numberType)
); );
CHECK_EQ( CHECK_EQ(
R"(digraph graphname { R"(digraph graphname {
n1 [label="any"]; n1 [label="any"];
})", })",
toDot(builtinTypes->anyType) toDot(getBuiltins()->anyType)
); );
CHECK_EQ( CHECK_EQ(
R"(digraph graphname { R"(digraph graphname {
n1 [label="unknown"]; n1 [label="unknown"];
})", })",
toDot(builtinTypes->unknownType) toDot(getBuiltins()->unknownType)
); );
CHECK_EQ( CHECK_EQ(
R"(digraph graphname { R"(digraph graphname {
n1 [label="never"]; n1 [label="never"];
})", })",
toDot(builtinTypes->neverType) toDot(getBuiltins()->neverType)
); );
} }
@ -90,28 +90,28 @@ TEST_CASE_FIXTURE(Fixture, "no_duplicatePrimitives")
R"(digraph graphname { R"(digraph graphname {
n1 [label="PrimitiveType number"]; n1 [label="PrimitiveType number"];
})", })",
toDot(builtinTypes->numberType, opts) toDot(getBuiltins()->numberType, opts)
); );
CHECK_EQ( CHECK_EQ(
R"(digraph graphname { R"(digraph graphname {
n1 [label="AnyType 1"]; n1 [label="AnyType 1"];
})", })",
toDot(builtinTypes->anyType, opts) toDot(getBuiltins()->anyType, opts)
); );
CHECK_EQ( CHECK_EQ(
R"(digraph graphname { R"(digraph graphname {
n1 [label="UnknownType 1"]; n1 [label="UnknownType 1"];
})", })",
toDot(builtinTypes->unknownType, opts) toDot(getBuiltins()->unknownType, opts)
); );
CHECK_EQ( CHECK_EQ(
R"(digraph graphname { R"(digraph graphname {
n1 [label="NeverType 1"]; n1 [label="NeverType 1"];
})", })",
toDot(builtinTypes->neverType, opts) toDot(getBuiltins()->neverType, opts)
); );
} }
@ -119,7 +119,7 @@ TEST_CASE_FIXTURE(Fixture, "bound")
{ {
TypeArena arena; TypeArena arena;
TypeId ty = arena.addType(BoundType{builtinTypes->numberType}); TypeId ty = arena.addType(BoundType{getBuiltins()->numberType});
ToDotOptions opts; ToDotOptions opts;
opts.showPointers = false; opts.showPointers = false;
@ -216,7 +216,7 @@ TEST_CASE_FIXTURE(Fixture, "intersection")
{ {
TypeArena arena; TypeArena arena;
TypeId ty = arena.addType(IntersectionType{{builtinTypes->stringType, builtinTypes->numberType}}); TypeId ty = arena.addType(IntersectionType{{getBuiltins()->stringType, getBuiltins()->numberType}});
ToDotOptions opts; ToDotOptions opts;
opts.showPointers = false; opts.showPointers = false;
@ -322,7 +322,7 @@ n3 [label="TableType 3"];
TEST_CASE_FIXTURE(Fixture, "free") 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; ToDotOptions opts;
opts.showPointers = false; opts.showPointers = false;
CHECK_EQ( CHECK_EQ(
@ -339,7 +339,7 @@ TEST_CASE_FIXTURE(Fixture, "free_with_constraints")
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
}; };
Type type{TypeVariant{FreeType{nullptr, builtinTypes->numberType, builtinTypes->optionalNumberType}}}; Type type{TypeVariant{FreeType{nullptr, getBuiltins()->numberType, getBuiltins()->optionalNumberType}}};
ToDotOptions opts; ToDotOptions opts;
opts.showPointers = false; opts.showPointers = false;
@ -467,7 +467,7 @@ n1 [label="GenericTypePack T"];
TEST_CASE_FIXTURE(Fixture, "bound_pack") TEST_CASE_FIXTURE(Fixture, "bound_pack")
{ {
TypePackVar pack{TypePackVariant{TypePack{{builtinTypes->numberType}, {}}}}; TypePackVar pack{TypePackVariant{TypePack{{getBuiltins()->numberType}, {}}}};
TypePackVar bound{TypePackVariant{BoundTypePack{&pack}}}; TypePackVar bound{TypePackVariant{BoundTypePack{&pack}}};
ToDotOptions opts; ToDotOptions opts;
@ -489,7 +489,7 @@ TEST_CASE_FIXTURE(Fixture, "bound_table")
TypeArena arena; TypeArena arena;
TypeId ty = arena.addType(TableType{}); TypeId ty = arena.addType(TableType{});
getMutable<TableType>(ty)->props["x"] = {builtinTypes->numberType}; getMutable<TableType>(ty)->props["x"] = {getBuiltins()->numberType};
TypeId boundTy = arena.addType(TableType{}); TypeId boundTy = arena.addType(TableType{});
getMutable<TableType>(boundTy)->boundTo = ty; getMutable<TableType>(boundTy)->boundTo = ty;
@ -539,7 +539,7 @@ n5 [label="SingletonType boolean: false"];
TEST_CASE_FIXTURE(Fixture, "negation") TEST_CASE_FIXTURE(Fixture, "negation")
{ {
TypeArena arena; TypeArena arena;
TypeId t = arena.addType(NegationType{builtinTypes->stringType}); TypeId t = arena.addType(NegationType{getBuiltins()->stringType});
ToDotOptions opts; ToDotOptions opts;
opts.showPointers = false; opts.showPointers = false;

View file

@ -14,9 +14,8 @@ using namespace Luau;
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction) LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAttributeSyntax)
LUAU_FASTFLAG(LuauFixEmptyTypePackStringification) LUAU_FASTFLAG(LuauFixEmptyTypePackStringification)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
TEST_SUITE_BEGIN("ToString"); 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") TEST_CASE_FIXTURE(Fixture, "intersection_parenthesized_only_if_needed")
{ {
auto utv = Type{UnionType{{builtinTypes->numberType, builtinTypes->stringType}}}; auto utv = Type{UnionType{{getBuiltins()->numberType, getBuiltins()->stringType}}};
auto itv = Type{IntersectionType{{&utv, builtinTypes->booleanType}}}; auto itv = Type{IntersectionType{{&utv, getBuiltins()->booleanType}}};
CHECK_EQ(toString(&itv), "(number | string) & boolean"); CHECK_EQ(toString(&itv), "(number | string) & boolean");
} }
TEST_CASE_FIXTURE(Fixture, "union_parenthesized_only_if_needed") TEST_CASE_FIXTURE(Fixture, "union_parenthesized_only_if_needed")
{ {
auto itv = Type{IntersectionType{{builtinTypes->numberType, builtinTypes->stringType}}}; auto itv = Type{IntersectionType{{getBuiltins()->numberType, getBuiltins()->stringType}}};
auto utv = Type{UnionType{{&itv, builtinTypes->booleanType}}}; auto utv = Type{UnionType{{&itv, getBuiltins()->booleanType}}};
CHECK_EQ(toString(&utv), "(number & string) | boolean"); CHECK_EQ(toString(&utv), "(number & string) | boolean");
} }
TEST_CASE_FIXTURE(Fixture, "functions_are_always_parenthesized_in_unions_or_intersections") TEST_CASE_FIXTURE(Fixture, "functions_are_always_parenthesized_in_unions_or_intersections")
{ {
auto stringAndNumberPack = TypePackVar{TypePack{{builtinTypes->stringType, builtinTypes->numberType}}}; auto stringAndNumberPack = TypePackVar{TypePack{{getBuiltins()->stringType, getBuiltins()->numberType}}};
auto numberAndStringPack = TypePackVar{TypePack{{builtinTypes->numberType, builtinTypes->stringType}}}; auto numberAndStringPack = TypePackVar{TypePack{{getBuiltins()->numberType, getBuiltins()->stringType}}};
auto sn2ns = Type{FunctionType{&stringAndNumberPack, &numberAndStringPack}}; 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 utv = Type{UnionType{{&ns2sn, &sn2ns}}};
auto itv = Type{IntersectionType{{&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{}; TableType ttv{};
for (char c : std::string("abcdefghijklmno")) 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}; Type tv{ttv};
@ -358,7 +357,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_is_still_capped_when_exhaust
{ {
TableType ttv{}; TableType ttv{};
for (char c : std::string("abcdefg")) 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}; Type tv{ttv};
@ -444,7 +443,7 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table
{ {
TableType ttv{TableState::Sealed, TypeLevel{}}; TableType ttv{TableState::Sealed, TypeLevel{}};
for (char c : std::string("abcdefghij")) 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}; 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") 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<UnionType>(&tv); UnionType* utv = getMutable<UnionType>(&tv);
utv->options.push_back(&tv); utv->options.push_back(&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") TEST_CASE_FIXTURE(Fixture, "stringifying_array_uses_array_syntax")
{ {
TableType ttv{TableState::Sealed, TypeLevel{}}; 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})); CHECK_EQ("{string}", toString(Type{ttv}));
ttv.props["A"] = {builtinTypes->numberType}; ttv.props["A"] = {getBuiltins()->numberType};
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2)
CHECK_EQ("{ [number]: string, A: number }", toString(Type{ttv})); CHECK_EQ("{ [number]: string, A: number }", toString(Type{ttv}));
else else
@ -632,15 +631,15 @@ TEST_CASE_FIXTURE(Fixture, "toString_the_boundTo_table_type_contained_within_a_T
Type tv1{TableType{}}; Type tv1{TableType{}};
TableType* ttv = getMutable<TableType>(&tv1); TableType* ttv = getMutable<TableType>(&tv1);
ttv->state = TableState::Sealed; ttv->state = TableState::Sealed;
ttv->props["hello"] = {builtinTypes->numberType}; ttv->props["hello"] = {getBuiltins()->numberType};
ttv->props["world"] = {builtinTypes->numberType}; ttv->props["world"] = {getBuiltins()->numberType};
TypePackVar tpv1{TypePack{{&tv1}}}; TypePackVar tpv1{TypePack{{&tv1}}};
Type tv2{TableType{}}; Type tv2{TableType{}};
TableType* bttv = getMutable<TableType>(&tv2); TableType* bttv = getMutable<TableType>(&tv2);
bttv->state = TableState::Free; bttv->state = TableState::Free;
bttv->props["hello"] = {builtinTypes->numberType}; bttv->props["hello"] = {getBuiltins()->numberType};
bttv->boundTo = &tv1; bttv->boundTo = &tv1;
TypePackVar tpv2{TypePack{{&tv2}}}; 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") TEST_CASE_FIXTURE(Fixture, "no_parentheses_around_return_type_if_pack_has_an_empty_head_link")
{ {
TypeArena arena; TypeArena arena;
TypePackId realTail = arena.addTypePack({builtinTypes->stringType}); TypePackId realTail = arena.addTypePack({getBuiltins()->stringType});
TypePackId emptyTail = arena.addTypePack({}, realTail); TypePackId emptyTail = arena.addTypePack({}, realTail);
TypePackId argList = arena.addTypePack({builtinTypes->stringType}); TypePackId argList = arena.addTypePack({getBuiltins()->stringType});
TypeId functionType = arena.addType(FunctionType{argList, emptyTail}); 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") TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
@ -954,12 +953,12 @@ TEST_CASE_FIXTURE(Fixture, "cycle_rooted_in_a_pack")
{ {
TypeArena arena; TypeArena arena;
TypePackId thePack = arena.addTypePack({builtinTypes->numberType, builtinTypes->numberType}); TypePackId thePack = arena.addTypePack({getBuiltins()->numberType, getBuiltins()->numberType});
TypePack* packPtr = getMutable<TypePack>(thePack); TypePack* packPtr = getMutable<TypePack>(thePack);
REQUIRE(packPtr); REQUIRE(packPtr);
const TableType::Props theProps = { const TableType::Props theProps = {
{"BaseField", Property::readonly(builtinTypes->unknownType)}, {"BaseField", Property::readonly(getBuiltins()->unknownType)},
{"BaseMethod", Property::readonly(arena.addType(FunctionType{thePack, arena.addTypePack({})}))} {"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}; TypeFunction user{"user", nullptr};
TypeFunctionInstanceType tftt{ TypeFunctionInstanceType tftt{
NotNull{&user}, NotNull{&user},
std::vector<TypeId>{builtinTypes->numberType}, // Type Function Arguments std::vector<TypeId>{getBuiltins()->numberType}, // Type Function Arguments
{}, {},
{AstName{"woohoo"}}, // Type Function Name {AstName{"woohoo"}}, // Type Function Name
{}, {},

View file

@ -14,9 +14,9 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2) LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauErrorSuppressionTypeFunctionArgs) LUAU_FASTFLAG(LuauErrorSuppressionTypeFunctionArgs)
struct TypeFunctionFixture : Fixture struct TypeFunctionFixture : Fixture
@ -55,14 +55,14 @@ struct TypeFunctionFixture : Fixture
} }
}; };
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
TypeId t = frontend.globals.globalTypes.addType(GenericType{"T"}); TypeId t = getFrontend().globals.globalTypes.addType(GenericType{"T"});
GenericTypeDefinition genericT{t}; GenericTypeDefinition genericT{t};
ScopePtr globalScope = frontend.globals.globalScope; ScopePtr globalScope = getFrontend().globals.globalScope;
globalScope->exportedTypeBindings["Swap"] = globalScope->exportedTypeBindings["Swap"] =
TypeFun{{genericT}, frontend.globals.globalTypes.addType(TypeFunctionInstanceType{NotNull{&swapFunction}, {t}, {}})}; TypeFun{{genericT}, getFrontend().globals.globalTypes.addType(TypeFunctionInstanceType{NotNull{&swapFunction}, {t}, {}})};
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
} }
}; };
@ -349,7 +349,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_works")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number } 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) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local metatable = { __index = {w = 1} } 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") TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_string_indexer")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
@ -464,7 +464,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_type_function_common_subset_if_union_o
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number } type MyObject = { x: number, y: number, z: number }
@ -502,7 +502,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_works")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number } type MyObject = { x: number, y: number, z: number }
@ -525,7 +525,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_ignores_metatables")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local metatable = { __index = {w = 1} } local metatable = { __index = {w = 1} }
@ -568,7 +568,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawkeyof_type_function_common_subset_if_unio
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = { x: number, y: number, z: number } 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) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type KeysOfMyObject = keyof<BaseClass> type KeysOfMyObject = keyof<BaseClass>
@ -1005,7 +1005,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_function_works")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = {a: string, b: number, c: boolean} type MyObject = {a: string, b: number, c: boolean}
@ -1306,7 +1306,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_function_works")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type MyObject = {a: string, b: number, c: boolean} 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); LUAU_REQUIRE_NO_ERRORS(result);
const PrimitiveType* stringType = get<PrimitiveType>(builtinTypes->stringType); const PrimitiveType* stringType = get<PrimitiveType>(getBuiltins()->stringType);
REQUIRE(stringType->metatable); REQUIRE(stringType->metatable);
TypeArena arena = TypeArena{}; 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); 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); 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); LUAU_REQUIRE_NO_ERRORS(result);
const PrimitiveType* stringType = get<PrimitiveType>(builtinTypes->stringType); const PrimitiveType* stringType = get<PrimitiveType>(getBuiltins()->stringType);
REQUIRE(stringType->metatable); REQUIRE(stringType->metatable);
std::string expected = toString(*stringType->metatable, {true}); std::string expected = toString(*stringType->metatable, {true});
@ -1712,26 +1712,26 @@ struct TFFixture
NotNull<TypeArena> arena{&arena_}; NotNull<TypeArena> arena{&arena_};
BuiltinTypes builtinTypes_; BuiltinTypes builtinTypes_;
NotNull<BuiltinTypes> builtinTypes{&builtinTypes_}; NotNull<BuiltinTypes> getBuiltins(){ return NotNull{&builtinTypes_};}
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack); ScopePtr globalScope = std::make_shared<Scope>(getBuiltins()->anyTypePack);
InternalErrorReporter ice; InternalErrorReporter ice;
UnifierSharedState unifierState{&ice}; UnifierSharedState unifierState{&ice};
SimplifierPtr simplifier = EqSatSimplification::newSimplifier(arena, builtinTypes); SimplifierPtr simplifier = EqSatSimplification::newSimplifier(arena, getBuiltins());
Normalizer normalizer{arena, builtinTypes, NotNull{&unifierState}}; Normalizer normalizer{arena, getBuiltins(), NotNull{&unifierState}};
TypeCheckLimits limits; TypeCheckLimits limits;
TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}}; TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}};
const ScopedFastFlag sff[1] = { const ScopedFastFlag sff[1] = {
{FFlag::LuauEagerGeneralization3, true}, {FFlag::LuauEagerGeneralization4, true},
}; };
BuiltinTypeFunctions builtinTypeFunctions; BuiltinTypeFunctions builtinTypeFunctions;
TypeFunctionContext tfc{ TypeFunctionContext tfc{
arena, arena,
builtinTypes, getBuiltins(),
NotNull{globalScope.get()}, NotNull{globalScope.get()},
NotNull{simplifier.get()}, NotNull{simplifier.get()},
NotNull{&normalizer}, NotNull{&normalizer},
@ -1745,7 +1745,7 @@ TEST_CASE_FIXTURE(TFFixture, "refine<G, ~(false?)>")
{ {
TypeId g = arena->addType(GenericType{globalScope.get(), Polarity::Negative}); 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); FunctionGraphReductionResult res = reduceTypeFunctions(refineTy, Location{}, tfc);
@ -1758,8 +1758,8 @@ TEST_CASE_FIXTURE(TFFixture, "refine<G, ~(false?)>")
TEST_CASE_FIXTURE(TFFixture, "or<'a, 'b>") TEST_CASE_FIXTURE(TFFixture, "or<'a, 'b>")
{ {
TypeId aType = arena->freshType(builtinTypes, globalScope.get()); TypeId aType = arena->freshType(getBuiltins(), globalScope.get());
TypeId bType = arena->freshType(builtinTypes, globalScope.get()); TypeId bType = arena->freshType(getBuiltins(), globalScope.get());
TypeId orType = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.orFunc, {aType, bType}}); TypeId orType = arena->addType(TypeFunctionInstanceType{builtinTypeFunctions.orFunc, {aType, bType}});

View file

@ -9,9 +9,11 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauUserTypeFunctionAliases) LUAU_FASTFLAG(LuauUserTypeFunctionAliases)
LUAU_FASTFLAG(LuauFollowTypeAlias)
LUAU_FASTFLAG(LuauFollowExistingTypeFunction)
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests"); TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
@ -351,7 +353,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function serialize_union(arg) type function serialize_union(arg)
@ -371,7 +373,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function numberhuh() type function numberhuh()
@ -390,7 +392,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works_on_unions") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works_on_unions")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function foobar() type function foobar()
@ -412,7 +414,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function getunion() type function getunion()
@ -441,7 +443,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function serialize_intersection(arg) type function serialize_intersection(arg)
@ -463,7 +465,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function getintersection() type function getintersection()
@ -498,7 +500,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function getnegation() type function getnegation()
@ -547,7 +549,7 @@ local function notok(idx: fail<number>): never return idx end
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function serialize_table(arg) 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") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function gettable() type function gettable()
@ -606,7 +608,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function getmetatable() type function getmetatable()
@ -656,7 +658,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function getfunction() type function getfunction()
@ -717,7 +719,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_serialization_works2")
TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_methods_works") TEST_CASE_FIXTURE(ExternTypeFixture, "udtf_class_methods_works")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function getclass(arg) 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") TEST_CASE_FIXTURE(ExternTypeFixture, "write_of_readonly_is_nil")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function getclass(arg) type function getclass(arg)
@ -766,7 +768,7 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "write_of_readonly_is_nil")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function checkmut() type function checkmut()
@ -798,7 +800,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function getcopy() 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") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function foo() type function foo()
@ -1005,7 +1007,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other")
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_2") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_2")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function first(arg) 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") TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other_unordered")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function bar() type function bar()
@ -1124,7 +1126,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function optionify(tbl) type function optionify(tbl)
@ -1178,7 +1180,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function foo(tbl) type function foo(tbl)
@ -1243,7 +1245,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strip_indexer")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
type function stripindexer(tbl) type function stripindexer(tbl)
@ -1332,7 +1334,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
@ -1410,7 +1412,7 @@ local a: concat<'first', 'second'>
return {} return {}
)"; )";
CheckResult aResult = frontend.check("game/A"); CheckResult aResult = getFrontend().check("game/A");
LUAU_REQUIRE_NO_ERRORS(aResult); LUAU_REQUIRE_NO_ERRORS(aResult);
CHECK(toString(requireType("game/A", "a")) == R"("firstsecond")"); CHECK(toString(requireType("game/A", "a")) == R"("firstsecond")");
@ -1463,7 +1465,7 @@ local a: concat<'first', 'second'>
return {} return {}
)"; )";
CheckResult aResult = frontend.check("game/A"); CheckResult aResult = getFrontend().check("game/A");
LUAU_REQUIRE_NO_ERRORS(aResult); LUAU_REQUIRE_NO_ERRORS(aResult);
CHECK(toString(requireType("game/A", "a")) == R"("firstsecond")"); CHECK(toString(requireType("game/A", "a")) == R"("firstsecond")");
@ -2002,7 +2004,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_eqsat_opaque")
)"); )");
TypeArena arena; TypeArena arena;
auto ty = requireType("v"); 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); auto simplified = eqSatSimplify(NotNull{simplifier.get()}, ty);
REQUIRE(simplified); REQUIRE(simplified);
CHECK_EQ("t0<number & string>", toString(simplified->result)); // NOLINT(bugprone-unchecked-optional-access) CHECK_EQ("t0<number & string>", 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}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
// FIXME: CLI-151985 // FIXME: CLI-151985
// This test breaks because we can't see that eq<type?, b> is already fully reduced. // This test breaks because we can't see that eq<type?, b> is already fully reduced.
@ -2035,7 +2037,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_string")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
if (FFlag::LuauEagerGeneralization3) if (FFlag::LuauEagerGeneralization4)
{ {
// FIXME: CLI-151985 // FIXME: CLI-151985
// This test breaks because we can't see that eq<type?, b> is already fully reduced. // This test breaks because we can't see that eq<type?, b> is already fully reduced.
@ -2335,7 +2337,7 @@ local x: foo<{ a: number }> = 2
return {} return {}
)"; )";
CheckResult aResult = frontend.check("game/A"); CheckResult aResult = getFrontend().check("game/A");
LUAU_REQUIRE_NO_ERRORS(aResult); LUAU_REQUIRE_NO_ERRORS(aResult);
CHECK(toString(requireType("game/A", "x")) == R"(number)"); CHECK(toString(requireType("game/A", "x")) == R"(number)");
@ -2449,4 +2451,37 @@ local x: foo<boolean>
CHECK(toString(requireType("x"), ToStringOptions{true}) == "boolean | number"); 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 _<t32...,t0...,t0...,t0...>(...):(any)&(any)
end
if _ then
else
export type t110 = ""type--"
function _<t32...,t0...,t0...,t0...>(...):(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<A>()
end
type function t0<A>()
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(); TEST_SUITE_END();

View file

@ -13,7 +13,7 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2) LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2) LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2)
LUAU_FASTFLAG(LuauSkipMalformedTypeAliases) LUAU_FASTFLAG(LuauSkipMalformedTypeAliases)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
TEST_SUITE_BEGIN("TypeAliases"); TEST_SUITE_BEGIN("TypeAliases");
@ -84,8 +84,8 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias")
Location{{1, 21}, {1, 26}}, Location{{1, 21}, {1, 26}},
getMainSourceModule()->name, getMainSourceModule()->name,
TypeMismatch{ TypeMismatch{
builtinTypes->numberType, getBuiltins()->numberType,
builtinTypes->stringType, getBuiltins()->stringType,
}, },
} }
); );
@ -98,8 +98,8 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias")
Location{{1, 8}, {1, 26}}, Location{{1, 8}, {1, 26}},
getMainSourceModule()->name, getMainSourceModule()->name,
TypeMismatch{ TypeMismatch{
builtinTypes->numberType, getBuiltins()->numberType,
builtinTypes->stringType, getBuiltins()->stringType,
}, },
} }
); );
@ -188,7 +188,7 @@ TEST_CASE_FIXTURE(Fixture, "cyclic_types_of_named_table_fields_do_not_expand_whe
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]); TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE_MESSAGE(tm, result.errors[0]); REQUIRE_MESSAGE(tm, result.errors[0]);
CHECK_EQ("Node?", toString(tm->wantedType)); 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") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_aliases")
@ -210,7 +210,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_aliases")
{ {
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
@ -229,7 +229,7 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
{ {
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
@ -274,9 +274,9 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_errors")
LUAU_REQUIRE_ERRORS(result); LUAU_REQUIRE_ERRORS(result);
// We had a UAF in this example caused by not cloning type function arguments // 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); unfreeze(module->interfaceTypes);
copyErrors(module->errors, module->interfaceTypes, builtinTypes); copyErrors(module->errors, module->interfaceTypes, getBuiltins());
freeze(module->interfaceTypes); freeze(module->interfaceTypes);
module->internalTypes.clear(); module->internalTypes.clear();
module->astTypes.clear(); module->astTypes.clear();
@ -329,7 +329,7 @@ TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_typ
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]); TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm); REQUIRE(tm);
CHECK_EQ("Wrapped", toString(tm->wantedType)); 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") 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)); CHECK_EQ("t1 where t1 = ({ a: t1 }) -> string", toString(tm->wantedType));
else else
CHECK_EQ("t1 where t1 = ({| a: t1 |}) -> string", toString(tm->wantedType)); 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 // 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 local b: Bar.myvec3
)"; )";
CheckResult result = frontend.check("workspace/C"); CheckResult result = getFrontend().check("workspace/C");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
TypeId aTypeId = requireType("workspace/C", "a"); TypeId aTypeId = requireType("workspace/C", "a");
@ -527,7 +527,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_import_mutation")
CheckResult result = check("type t10<x> = typeof(table)"); CheckResult result = check("type t10<x> = typeof(table)");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
TypeId ty = getGlobalBinding(frontend.globals, "table"); TypeId ty = getGlobalBinding(getFrontend().globals, "table");
CHECK(toString(ty) == "typeof(table)"); CHECK(toString(ty) == "typeof(table)");
@ -602,7 +602,7 @@ export type X = { a: number, b: X? }
return {} return {}
)"; )";
CheckResult aResult = frontend.check("game/A"); CheckResult aResult = getFrontend().check("game/A");
LUAU_REQUIRE_NO_ERRORS(aResult); LUAU_REQUIRE_NO_ERRORS(aResult);
CheckResult bResult = check(R"( CheckResult bResult = check(R"(
@ -627,7 +627,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_of_an_imported_recursive_generic_
return {} return {}
)"; )";
CheckResult aResult = frontend.check("game/A"); CheckResult aResult = getFrontend().check("game/A");
LUAU_REQUIRE_NO_ERRORS(aResult); LUAU_REQUIRE_NO_ERRORS(aResult);
CheckResult bResult = check(R"( CheckResult bResult = check(R"(
@ -995,7 +995,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_lose_track_of_PendingExpansionTypes_aft
end end
)"; )";
CheckResult result = frontend.check("game/React/React/ReactHooks"); CheckResult result = getFrontend().check("game/React/React/ReactHooks");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
@ -1048,7 +1048,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "alias_expands_to_bare_reference_to_imported_
end end
)"; )";
CheckResult result = frontend.check("game/B"); CheckResult result = getFrontend().check("game/B");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }

View file

@ -181,7 +181,7 @@ TEST_CASE_FIXTURE(Fixture, "function_return_annotations_are_checked")
REQUIRE_EQ(1, tp->head.size()); 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") 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); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK( 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 else
{ {
CHECK_EQ(*builtinTypes->numberType, *requireType("a")); CHECK_EQ(*getBuiltins()->numberType, *requireType("a"));
CHECK_EQ(*builtinTypes->numberType, *requireType("b")); CHECK_EQ(*getBuiltins()->numberType, *requireType("b"));
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ( CHECK_EQ(
result.errors[0], 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"); TypeId fType = requireType("aa");
REQUIRE(follow(fType) == builtinTypes->numberType); REQUIRE(follow(fType) == getBuiltins()->numberType);
LUAU_REQUIRE_NO_ERRORS(result); 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"]; const TypeFun& a = mod.exportedTypeBindings["A"];
CHECK(isInArena(a.type, mod.interfaceTypes)); CHECK(isInArena(a.type, mod.interfaceTypes));
CHECK(!isInArena(a.type, frontend.globals.globalTypes)); CHECK(!isInArena(a.type, getFrontend().globals.globalTypes));
std::optional<TypeId> exportsType = first(mod.returnType); std::optional<TypeId> exportsType = first(mod.returnType);
REQUIRE(exportsType); 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") 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"( fileResolver.source["Modules/Main"] = R"(
--!strict --!strict
@ -692,14 +692,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "use_type_required_from_another_file")
return {} return {}
)"; )";
CheckResult result = frontend.check("Modules/Main"); CheckResult result = getFrontend().check("Modules/Main");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "cannot_use_nonexported_type") 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"( fileResolver.source["Modules/Main"] = R"(
--!strict --!strict
@ -718,14 +718,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cannot_use_nonexported_type")
return {} return {}
)"; )";
CheckResult result = frontend.check("Modules/Main"); CheckResult result = getFrontend().check("Modules/Main");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_are_not_exported") 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"( fileResolver.source["Modules/Main"] = R"(
--!strict --!strict
@ -742,7 +742,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_are_not_exported")
return {} return {}
)"; )";
CheckResult result = frontend.check("Modules/Main"); CheckResult result = getFrontend().check("Modules/Main");
LUAU_REQUIRE_ERROR_COUNT(1, result); 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; bool caught = false;
frontend.iceHandler.onInternalError = [&](const char*) getFrontend().iceHandler.onInternalError = [&](const char*)
{ {
caught = true; 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") 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<std::string> output; static std::vector<std::string> output;
output.clear(); output.clear();
Luau::setPrintLine( Luau::setPrintLine(

View file

@ -46,7 +46,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any")
} }
} }
else else
CHECK(builtinTypes->anyType == requireType("a")); CHECK(getBuiltins()->anyType == requireType("a"));
} }
TEST_CASE_FIXTURE(Fixture, "for_in_loop_iterator_returns_any2") 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); LUAU_REQUIRE_NO_ERRORS(result);
TypeId aType = requireType("A"); TypeId aType = requireType("A");
CHECK_EQ(aType, builtinTypes->anyType); CHECK_EQ(aType, getBuiltins()->anyType);
} }
TEST_CASE_FIXTURE(Fixture, "calling_error_type_yields_error") TEST_CASE_FIXTURE(Fixture, "calling_error_type_yields_error")

View file

@ -11,9 +11,10 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTableCloneClonesType3) LUAU_FASTFLAG(LuauTableCloneClonesType3)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauStringFormatImprovements) LUAU_FASTFLAG(LuauStringFormatImprovements)
LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling)
TEST_SUITE_BEGIN("BuiltinTests"); TEST_SUITE_BEGIN("BuiltinTests");
@ -110,7 +111,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_concat_returns_string")
)"); )");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(*builtinTypes->stringType, *requireType("r")); CHECK_EQ(*getBuiltins()->stringType, *requireType("r"));
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "sort") TEST_CASE_FIXTURE(BuiltinsFixture, "sort")
@ -170,7 +171,7 @@ TEST_CASE_FIXTURE(Fixture, "strings_have_methods")
)LUA"); )LUA");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(*builtinTypes->stringType, *requireType("s")); CHECK_EQ(*getBuiltins()->stringType, *requireType("s"));
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "math_max_variatic") TEST_CASE_FIXTURE(BuiltinsFixture, "math_max_variatic")
@ -180,7 +181,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "math_max_variatic")
)"); )");
LUAU_REQUIRE_NO_ERRORS(result); 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") 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); 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") 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); LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(*builtinTypes->numberType, *requireType("n")); CHECK_EQ(*getBuiltins()->numberType, *requireType("n"));
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "getfenv") TEST_CASE_FIXTURE(BuiltinsFixture, "getfenv")
@ -490,9 +491,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "os_time_takes_optional_date_table")
)"); )");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(*builtinTypes->numberType, *requireType("n1")); CHECK_EQ(*getBuiltins()->numberType, *requireType("n1"));
CHECK_EQ(*builtinTypes->numberType, *requireType("n2")); CHECK_EQ(*getBuiltins()->numberType, *requireType("n2"));
CHECK_EQ(*builtinTypes->numberType, *requireType("n3")); CHECK_EQ(*getBuiltins()->numberType, *requireType("n3"));
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "thread_is_a_type") 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); LUAU_REQUIRE_ERROR_COUNT(1, result);
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]); TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm); REQUIRE(tm);
CHECK_EQ(tm->wantedType, builtinTypes->stringType); CHECK_EQ(tm->wantedType, getBuiltins()->stringType);
CHECK_EQ(tm->givenType, builtinTypes->numberType); CHECK_EQ(tm->givenType, getBuiltins()->numberType);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_tostring_specifier") TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_tostring_specifier")
@ -809,8 +810,8 @@ TEST_CASE_FIXTURE(Fixture, "string_format_as_method")
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]); TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm); REQUIRE(tm);
CHECK_EQ(tm->wantedType, builtinTypes->stringType); CHECK_EQ(tm->wantedType, getBuiltins()->stringType);
CHECK_EQ(tm->givenType, builtinTypes->numberType); CHECK_EQ(tm->givenType, getBuiltins()->numberType);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_trivial_arity") 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) string.format("%s%d%s", 1, "hello", true)
)"); )");
TypeId stringType = builtinTypes->stringType; TypeId stringType = getBuiltins()->stringType;
TypeId numberType = builtinTypes->numberType; TypeId numberType = getBuiltins()->numberType;
TypeId booleanType = builtinTypes->booleanType; TypeId booleanType = getBuiltins()->booleanType;
LUAU_REQUIRE_ERROR_COUNT(6, result); 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") TEST_CASE_FIXTURE(BuiltinsFixture, "no_persistent_typelevel_change")
{ {
TypeId mathTy = requireType(frontend.globals.globalScope, "math"); TypeId mathTy = requireType(getFrontend().globals.globalScope, "math");
REQUIRE(mathTy); REQUIRE(mathTy);
TableType* ttv = getMutable<TableType>(mathTy); TableType* ttv = getMutable<TableType>(mathTy);
REQUIRE(ttv); REQUIRE(ttv);
@ -1694,8 +1695,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_singleton_types
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]); TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm); REQUIRE(tm);
CHECK_EQ(tm->wantedType, builtinTypes->stringType); CHECK_EQ(tm->wantedType, getBuiltins()->stringType);
CHECK_EQ(tm->givenType, builtinTypes->numberType); CHECK_EQ(tm->givenType, getBuiltins()->numberType);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_string_is_dynamic") 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(); TEST_SUITE_END();

View file

@ -766,7 +766,7 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties")
{ {
ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopedFastFlag sff{FFlag::LuauSolverV2, true};
TypeArena& arena = frontend.globals.globalTypes; TypeArena& arena = getFrontend().globals.globalTypes;
unfreeze(arena); unfreeze(arena);
@ -782,7 +782,7 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties")
TypeId partType = arena.addType(ExternType{ TypeId partType = arena.addType(ExternType{
"Part", "Part",
{{"BrickColor", Property::rw(builtinTypes->stringType)}, {"Parent", Property::rw(workspaceType, instanceType)}}, {{"BrickColor", Property::rw(getBuiltins()->stringType)}, {"Parent", Property::rw(workspaceType, instanceType)}},
instanceType, instanceType,
nullopt, nullopt,
{}, {},
@ -793,7 +793,7 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties")
getMutable<ExternType>(workspaceType)->props = {{"Script", Property::readonly(scriptType)}, {"Part", Property::readonly(partType)}}; getMutable<ExternType>(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); freeze(arena);
@ -807,8 +807,8 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties")
CHECK(Location{{1, 40}, {1, 48}} == result.errors[0].location); CHECK(Location{{1, 40}, {1, 48}} == result.errors[0].location);
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]); TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm); REQUIRE(tm);
CHECK(builtinTypes->stringType == tm->wantedType); CHECK(getBuiltins()->stringType == tm->wantedType);
CHECK(builtinTypes->numberType == tm->givenType); CHECK(getBuiltins()->numberType == tm->givenType);
} }
TEST_CASE_FIXTURE(ExternTypeFixture, "cannot_index_a_class_with_no_indexer") 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] "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") TEST_CASE_FIXTURE(ExternTypeFixture, "cyclic_tables_are_assumed_to_be_compatible_with_extern_types")

View file

@ -22,13 +22,13 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_simple")
declare foo2: typeof(foo) declare foo2: typeof(foo)
)"); )");
TypeId globalFooTy = getGlobalBinding(frontend.globals, "foo"); TypeId globalFooTy = getGlobalBinding(getFrontend().globals, "foo");
CHECK_EQ(toString(globalFooTy), "number"); CHECK_EQ(toString(globalFooTy), "number");
TypeId globalBarTy = getGlobalBinding(frontend.globals, "bar"); TypeId globalBarTy = getGlobalBinding(getFrontend().globals, "bar");
CHECK_EQ(toString(globalBarTy), "(number) -> string"); CHECK_EQ(toString(globalBarTy), "(number) -> string");
TypeId globalFoo2Ty = getGlobalBinding(frontend.globals, "foo2"); TypeId globalFoo2Ty = getGlobalBinding(getFrontend().globals, "foo2");
CHECK_EQ(toString(globalFoo2Ty), "number"); CHECK_EQ(toString(globalFoo2Ty), "number");
CheckResult result = check(R"( CheckResult result = check(R"(
@ -51,20 +51,20 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_loading")
declare function var(...: any): string declare function var(...: any): string
)"); )");
TypeId globalFooTy = getGlobalBinding(frontend.globals, "foo"); TypeId globalFooTy = getGlobalBinding(getFrontend().globals, "foo");
CHECK_EQ(toString(globalFooTy), "number"); CHECK_EQ(toString(globalFooTy), "number");
std::optional<TypeFun> globalAsdfTy = frontend.globals.globalScope->lookupType("Asdf"); std::optional<TypeFun> globalAsdfTy = getFrontend().globals.globalScope->lookupType("Asdf");
REQUIRE(bool(globalAsdfTy)); REQUIRE(bool(globalAsdfTy));
CHECK_EQ(toString(globalAsdfTy->type), "number | string"); 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"); CHECK_EQ(toString(globalBarTy), "(number) -> string");
TypeId globalFoo2Ty = getGlobalBinding(frontend.globals, "foo2"); TypeId globalFoo2Ty = getGlobalBinding(getFrontend().globals, "foo2");
CHECK_EQ(toString(globalFoo2Ty), "number"); CHECK_EQ(toString(globalFoo2Ty), "number");
TypeId globalVarTy = getGlobalBinding(frontend.globals, "var"); TypeId globalVarTy = getGlobalBinding(getFrontend().globals, "var");
CHECK_EQ(toString(globalVarTy), "(...any) -> string"); 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") TEST_CASE_FIXTURE(Fixture, "load_definition_file_errors_do_not_pollute_global_scope")
{ {
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
LoadDefinitionFileResult parseFailResult = frontend.loadDefinitionFile( LoadDefinitionFileResult parseFailResult = getFrontend().loadDefinitionFile(
frontend.globals, getFrontend().globals,
frontend.globals.globalScope, getFrontend().globals.globalScope,
R"( R"(
declare foo declare foo
)", )",
"@test", "@test",
/* captureComments */ false /* captureComments */ false
); );
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
REQUIRE(!parseFailResult.success); REQUIRE(!parseFailResult.success);
std::optional<Binding> fooTy = tryGetGlobalBinding(frontend.globals, "foo"); std::optional<Binding> fooTy = tryGetGlobalBinding(getFrontend().globals, "foo");
CHECK(!fooTy.has_value()); CHECK(!fooTy.has_value());
LoadDefinitionFileResult checkFailResult = frontend.loadDefinitionFile( LoadDefinitionFileResult checkFailResult = getFrontend().loadDefinitionFile(
frontend.globals, getFrontend().globals,
frontend.globals.globalScope, getFrontend().globals.globalScope,
R"( R"(
local foo: string = 123 local foo: string = 123
declare bar: typeof(foo) declare bar: typeof(foo)
@ -108,7 +108,7 @@ TEST_CASE_FIXTURE(Fixture, "load_definition_file_errors_do_not_pollute_global_sc
); );
REQUIRE(!checkFailResult.success); REQUIRE(!checkFailResult.success);
std::optional<Binding> barTy = tryGetGlobalBinding(frontend.globals, "bar"); std::optional<Binding> barTy = tryGetGlobalBinding(getFrontend().globals, "bar");
CHECK(!barTy.has_value()); 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") TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function")
{ {
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
LoadDefinitionFileResult result = frontend.loadDefinitionFile( LoadDefinitionFileResult result = getFrontend().loadDefinitionFile(
frontend.globals, getFrontend().globals,
frontend.globals.globalScope, getFrontend().globals.globalScope,
R"( R"(
declare class A declare class A
X: number X: number
@ -165,7 +165,7 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function")
"@test", "@test",
/* captureComments */ false /* captureComments */ false
); );
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
REQUIRE(!result.success); REQUIRE(!result.success);
CHECK_EQ(result.parseResult.errors.size(), 0); 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") TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_extend_non_class")
{ {
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
LoadDefinitionFileResult result = frontend.loadDefinitionFile( LoadDefinitionFileResult result = getFrontend().loadDefinitionFile(
frontend.globals, getFrontend().globals,
frontend.globals.globalScope, getFrontend().globals.globalScope,
R"( R"(
type NotAClass = {} type NotAClass = {}
@ -191,7 +191,7 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_extend_non_class")
"@test", "@test",
/* captureComments */ false /* captureComments */ false
); );
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
REQUIRE(!result.success); REQUIRE(!result.success);
CHECK_EQ(result.parseResult.errors.size(), 0); 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") TEST_CASE_FIXTURE(Fixture, "no_cyclic_defined_extern_types")
{ {
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
LoadDefinitionFileResult result = frontend.loadDefinitionFile( LoadDefinitionFileResult result = getFrontend().loadDefinitionFile(
frontend.globals, getFrontend().globals,
frontend.globals.globalScope, getFrontend().globals.globalScope,
R"( R"(
declare class Foo extends Bar declare class Foo extends Bar
end end
@ -218,7 +218,7 @@ TEST_CASE_FIXTURE(Fixture, "no_cyclic_defined_extern_types")
"@test", "@test",
/* captureComments */ false /* captureComments */ false
); );
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
REQUIRE(!result.success); REQUIRE(!result.success);
} }
@ -317,16 +317,16 @@ TEST_CASE_FIXTURE(Fixture, "definitions_documentation_symbols")
} }
)"); )");
std::optional<Binding> xBinding = frontend.globals.globalScope->linearSearchForBinding("x"); std::optional<Binding> xBinding = getFrontend().globals.globalScope->linearSearchForBinding("x");
REQUIRE(bool(xBinding)); REQUIRE(bool(xBinding));
// note: loadDefinition uses the @test package name. // note: loadDefinition uses the @test package name.
CHECK_EQ(xBinding->documentationSymbol, "@test/global/x"); CHECK_EQ(xBinding->documentationSymbol, "@test/global/x");
std::optional<TypeFun> fooTy = frontend.globals.globalScope->lookupType("Foo"); std::optional<TypeFun> fooTy = getFrontend().globals.globalScope->lookupType("Foo");
REQUIRE(bool(fooTy)); REQUIRE(bool(fooTy));
CHECK_EQ(fooTy->type->documentationSymbol, "@test/globaltype/Foo"); CHECK_EQ(fooTy->type->documentationSymbol, "@test/globaltype/Foo");
std::optional<TypeFun> barTy = frontend.globals.globalScope->lookupType("Bar"); std::optional<TypeFun> barTy = getFrontend().globals.globalScope->lookupType("Bar");
REQUIRE(bool(barTy)); REQUIRE(bool(barTy));
CHECK_EQ(barTy->type->documentationSymbol, "@test/globaltype/Bar"); 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); REQUIRE_EQ(barClass->props.count("prop"), 1);
CHECK_EQ(barClass->props["prop"].documentationSymbol, "@test/globaltype/Bar.prop"); CHECK_EQ(barClass->props["prop"].documentationSymbol, "@test/globaltype/Bar.prop");
std::optional<Binding> yBinding = frontend.globals.globalScope->linearSearchForBinding("y"); std::optional<Binding> yBinding = getFrontend().globals.globalScope->linearSearchForBinding("y");
REQUIRE(bool(yBinding)); REQUIRE(bool(yBinding));
CHECK_EQ(yBinding->documentationSymbol, "@test/global/y"); 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 declare function myFunc(): MyClass
)"); )");
std::optional<TypeFun> myClassTy = frontend.globals.globalScope->lookupType("MyClass"); std::optional<TypeFun> myClassTy = getFrontend().globals.globalScope->lookupType("MyClass");
REQUIRE(bool(myClassTy)); REQUIRE(bool(myClassTy));
CHECK_EQ(myClassTy->type->documentationSymbol, "@test/globaltype/MyClass"); 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 export type Evil = string
)"); )");
std::optional<TypeFun> ty = frontend.globals.globalScope->lookupType("Evil"); std::optional<TypeFun> ty = getFrontend().globals.globalScope->lookupType("Evil");
REQUIRE(bool(ty)); REQUIRE(bool(ty));
CHECK_EQ(ty->type->documentationSymbol, std::nullopt); 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") TEST_CASE_FIXTURE(Fixture, "class_definition_malformed_string")
{ {
unfreeze(frontend.globals.globalTypes); unfreeze(getFrontend().globals.globalTypes);
LoadDefinitionFileResult result = frontend.loadDefinitionFile( LoadDefinitionFileResult result = getFrontend().loadDefinitionFile(
frontend.globals, getFrontend().globals,
frontend.globals.globalScope, getFrontend().globals.globalScope,
R"( R"(
declare class Foo declare class Foo
["a\0property"]: string ["a\0property"]: string
@ -460,7 +460,7 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_malformed_string")
"@test", "@test",
/* captureComments */ false /* captureComments */ false
); );
freeze(frontend.globals.globalTypes); freeze(getFrontend().globals.globalTypes);
REQUIRE(!result.success); REQUIRE(!result.success);
REQUIRE_EQ(result.parseResult.errors.size(), 1); REQUIRE_EQ(result.parseResult.errors.size(), 1);
@ -487,8 +487,8 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_indexer")
REQUIRE(bool(etv->indexer)); REQUIRE(bool(etv->indexer));
CHECK_EQ(*etv->indexer->indexType, *builtinTypes->numberType); CHECK_EQ(*etv->indexer->indexType, *getBuiltins()->numberType);
CHECK_EQ(*etv->indexer->indexResultType, *builtinTypes->stringType); CHECK_EQ(*etv->indexer->indexResultType, *getBuiltins()->stringType);
CHECK_EQ(toString(requireType("y")), "string"); 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.name, "@test");
CHECK_EQ(result.sourceModule.humanReadableName, "@test"); CHECK_EQ(result.sourceModule.humanReadableName, "@test");
std::optional<TypeFun> fooTy = frontend.globals.globalScope->lookupType("Foo"); std::optional<TypeFun> fooTy = getFrontend().globals.globalScope->lookupType("Foo");
REQUIRE(fooTy); REQUIRE(fooTy);
const ExternType* etv = get<ExternType>(fooTy->type); const ExternType* etv = get<ExternType>(fooTy->type);

View file

@ -22,12 +22,12 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauFormatUseLastPosition) LUAU_FASTFLAG(LuauFormatUseLastPosition)
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2) LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck)
TEST_SUITE_BEGIN("TypeInferFunctions"); TEST_SUITE_BEGIN("TypeInferFunctions");
@ -77,7 +77,7 @@ TEST_CASE_FIXTURE(Fixture, "tc_function")
TEST_CASE_FIXTURE(Fixture, "check_function_bodies") TEST_CASE_FIXTURE(Fixture, "check_function_bodies")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
function myFunction(): number function myFunction(): number
@ -103,8 +103,8 @@ TEST_CASE_FIXTURE(Fixture, "check_function_bodies")
(TypeError{ (TypeError{
Location{Position{3, 16}, Position{3, 20}}, Location{Position{3, 16}, Position{3, 20}},
TypeMismatch{ TypeMismatch{
builtinTypes->numberType, getBuiltins()->numberType,
builtinTypes->booleanType, getBuiltins()->booleanType,
} }
}) })
); );
@ -149,7 +149,7 @@ TEST_CASE_FIXTURE(Fixture, "infer_return_type")
std::vector<TypeId> retVec = flatten(takeFiveType->retTypes).first; std::vector<TypeId> retVec = flatten(takeFiveType->retTypes).first;
REQUIRE(!retVec.empty()); 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") 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()"); CheckResult result = check("function take_five() return 5 end local five = take_five()");
LUAU_REQUIRE_NO_ERRORS(result); 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") 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); 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") 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<TypeMismatch>(result.errors[0]); TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm); REQUIRE(tm);
CHECK_EQ(builtinTypes->numberType, tm->wantedType); CHECK_EQ(getBuiltins()->numberType, tm->wantedType);
CHECK_EQ(builtinTypes->stringType, tm->givenType); CHECK_EQ(getBuiltins()->stringType, tm->givenType);
} }
ExtraInformation* ei = get<ExtraInformation>(result.errors[1]); ExtraInformation* ei = get<ExtraInformation>(result.errors[1]);
@ -300,8 +300,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_give_other_overloads_message_if_only_one_argume
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]); TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm); REQUIRE(tm);
CHECK_EQ(builtinTypes->numberType, tm->wantedType); CHECK_EQ(getBuiltins()->numberType, tm->wantedType);
CHECK_EQ(builtinTypes->stringType, tm->givenType); CHECK_EQ(getBuiltins()->stringType, tm->givenType);
} }
TEST_CASE_FIXTURE(Fixture, "infer_return_type_from_selected_overload") 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{ (TypeError{
Location{Position{3, 12}, Position{3, 18}}, Location{Position{3, 12}, Position{3, 18}},
TypeMismatch{ TypeMismatch{
builtinTypes->numberType, getBuiltins()->numberType,
builtinTypes->stringType, getBuiltins()->stringType,
} }
}) })
); );
@ -1002,8 +1002,8 @@ TEST_CASE_FIXTURE(Fixture, "calling_function_with_incorrect_argument_type_yields
(TypeError{ (TypeError{
Location{Position{3, 20}, Position{3, 23}}, Location{Position{3, 20}, Position{3, 23}},
TypeMismatch{ TypeMismatch{
builtinTypes->stringType, getBuiltins()->stringType,
builtinTypes->numberType, 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") TEST_CASE_FIXTURE(Fixture, "infer_return_value_type")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local function f(): {string|number} local function f(): {string|number}
@ -1661,7 +1661,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "function_decl_non_self_sealed_overwrite")
LUAU_REQUIRE_NO_ERRORS(result); 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 // 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"( CheckResult result2 = check(R"(
print(string.len('hello')) print(string.len('hello'))
@ -1686,7 +1686,7 @@ t.f = function(x)
end end
)"); )");
if (FFlag::LuauEagerGeneralization3 && FFlag::LuauSolverV2) if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2)
{ {
// FIXME CLI-151985 // FIXME CLI-151985
LUAU_CHECK_ERROR_COUNT(3, result); LUAU_CHECK_ERROR_COUNT(3, result);
@ -1771,7 +1771,7 @@ t.f = function(x)
end end
)"); )");
if (FFlag::LuauEagerGeneralization3 && FFlag::LuauSolverV2) if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2)
{ {
// FIXME CLI-151985 // FIXME CLI-151985
LUAU_CHECK_ERROR_COUNT(2, result); 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) -> a", toString(requireType("f"))); CHECK_EQ("<a>(a) -> a", toString(requireType("f")));
if (FFlag::LuauEagerGeneralization3 && FFlag::LuauSolverV2) if (FFlag::LuauEagerGeneralization4 && FFlag::LuauSolverV2)
{ {
LUAU_CHECK_NO_ERRORS(result); LUAU_CHECK_NO_ERRORS(result);
CHECK("<a>({ read p: { read q: a } }) -> (a & ~(false?))?" == toString(requireType("g"))); CHECK("<a>({ 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") TEST_CASE_FIXTURE(Fixture, "function_is_supertype_of_concrete_functions")
{ {
registerHiddenTypes(&frontend); registerHiddenTypes(getFrontend());
CheckResult result = check(R"( CheckResult result = check(R"(
function foo(f: fun) end 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") TEST_CASE_FIXTURE(Fixture, "concrete_functions_are_not_supertypes_of_function")
{ {
registerHiddenTypes(&frontend); registerHiddenTypes(getFrontend());
CheckResult result = check(R"( CheckResult result = check(R"(
local a: fun = function() end 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") TEST_CASE_FIXTURE(Fixture, "other_things_are_not_related_to_function")
{ {
registerHiddenTypes(&frontend); registerHiddenTypes(getFrontend());
CheckResult result = check(R"( CheckResult result = check(R"(
local a: fun = function() end 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") TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
function num() 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") TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_after_num_or_str")
{ {
ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}; ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true};
CheckResult result = check(R"( CheckResult result = check(R"(
local function num_or_str() local function num_or_str()
@ -2643,7 +2643,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_arg_type_2")
return; return;
// Make sure the error types are cloned to module interface // Make sure the error types are cloned to module interface
frontend.options.retainFullTypeGraphs = false; getFrontend().options.retainFullTypeGraphs = false;
CheckResult result = check(R"( CheckResult result = check(R"(
local function escape_fslash(pre) local function escape_fslash(pre)
@ -2912,7 +2912,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSimplifyOutOfLine2, true}, {FFlag::LuauSimplifyOutOfLine2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true}, {FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
}; };
CheckResult result = check(R"( 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. // 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); LUAU_REQUIRE_ERROR_COUNT(2, result);
auto tm1 = get<TypeMismatch>(result.errors[0]); auto tm1 = get<TypeMismatch>(result.errors[0]);
@ -3062,7 +3062,7 @@ end
local u,v = id(3), id(id(44)) 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); LUAU_REQUIRE_NO_ERRORS(result);
} }

Some files were not shown because too many files have changed in this diff Show more