Merge branch 'upstream' into merge

This commit is contained in:
Arseny Kapoulkine 2022-04-28 18:05:07 -07:00
commit 5444d06708
40 changed files with 527 additions and 641 deletions

View File

@ -24,7 +24,7 @@ LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
LUAU_FASTFLAGVARIABLE(LuauSeparateTypechecks, false) LUAU_FASTFLAGVARIABLE(LuauSeparateTypechecks, false)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteDynamicLimits, false) LUAU_FASTFLAGVARIABLE(LuauAutocompleteDynamicLimits, false)
LUAU_FASTFLAGVARIABLE(LuauDirtySourceModule, false) LUAU_FASTFLAGVARIABLE(LuauDirtySourceModule, false)
LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 0) LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 100)
namespace Luau namespace Luau
{ {

View File

@ -2653,12 +2653,12 @@ static void lintComments(LintContext& context, const std::vector<HotComment>& ho
} }
else else
{ {
std::string::size_type space = hc.content.find_first_of(" \t"); size_t space = hc.content.find_first_of(" \t");
std::string_view first = std::string_view(hc.content).substr(0, space); std::string_view first = std::string_view(hc.content).substr(0, space);
if (first == "nolint") if (first == "nolint")
{ {
std::string::size_type notspace = hc.content.find_first_not_of(" \t", space); size_t notspace = hc.content.find_first_not_of(" \t", space);
if (space == std::string::npos || notspace == std::string::npos) if (space == std::string::npos || notspace == std::string::npos)
{ {
@ -2827,7 +2827,7 @@ uint64_t LintWarning::parseMask(const std::vector<HotComment>& hotcomments)
if (hc.content.compare(0, 6, "nolint") != 0) if (hc.content.compare(0, 6, "nolint") != 0)
continue; continue;
std::string::size_type name = hc.content.find_first_not_of(" \t", 6); size_t name = hc.content.find_first_not_of(" \t", 6);
// --!nolint disables everything // --!nolint disables everything
if (name == std::string::npos) if (name == std::string::npos)

View File

@ -8,7 +8,7 @@
#include <stdexcept> #include <stdexcept>
LUAU_FASTFLAG(LuauLowerBoundsCalculation) LUAU_FASTFLAG(LuauLowerBoundsCalculation)
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 1000) LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
LUAU_FASTFLAG(LuauTypecheckOptPass) LUAU_FASTFLAG(LuauTypecheckOptPass)
LUAU_FASTFLAGVARIABLE(LuauSubstituteFollowNewTypes, false) LUAU_FASTFLAGVARIABLE(LuauSubstituteFollowNewTypes, false)
LUAU_FASTFLAGVARIABLE(LuauSubstituteFollowPossibleMutations, false) LUAU_FASTFLAGVARIABLE(LuauSubstituteFollowPossibleMutations, false)

View File

@ -22,29 +22,25 @@
#include <iterator> #include <iterator>
LUAU_FASTFLAGVARIABLE(DebugLuauMagicTypes, false) LUAU_FASTFLAGVARIABLE(DebugLuauMagicTypes, false)
LUAU_FASTINTVARIABLE(LuauTypeInferRecursionLimit, 500) LUAU_FASTINTVARIABLE(LuauTypeInferRecursionLimit, 165)
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 2000) LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 20000)
LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000) LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000)
LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 500) LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 300)
LUAU_FASTFLAG(LuauKnowsTheDataModel3) LUAU_FASTFLAG(LuauKnowsTheDataModel3)
LUAU_FASTFLAG(LuauSeparateTypechecks) LUAU_FASTFLAG(LuauSeparateTypechecks)
LUAU_FASTFLAG(LuauAutocompleteDynamicLimits) LUAU_FASTFLAG(LuauAutocompleteDynamicLimits)
LUAU_FASTFLAG(LuauAutocompleteSingletonTypes) LUAU_FASTFLAG(LuauAutocompleteSingletonTypes)
LUAU_FASTFLAGVARIABLE(LuauCyclicModuleTypeSurface, false) LUAU_FASTFLAGVARIABLE(LuauCyclicModuleTypeSurface, false)
LUAU_FASTFLAGVARIABLE(LuauDoNotRelyOnNextBinding, false)
LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false) LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false)
LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false. LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false.
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false) LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false)
LUAU_FASTFLAGVARIABLE(LuauInferStatFunction, false) LUAU_FASTFLAGVARIABLE(LuauInferStatFunction, false)
LUAU_FASTFLAGVARIABLE(LuauSealExports, false) LUAU_FASTFLAGVARIABLE(LuauInstantiateFollows, false)
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix, false) LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix, false)
LUAU_FASTFLAGVARIABLE(LuauDiscriminableUnions2, false) LUAU_FASTFLAGVARIABLE(LuauDiscriminableUnions2, false)
LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false) LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false)
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify4, false) LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify4, false)
LUAU_FASTFLAGVARIABLE(LuauTypecheckOptPass, false) LUAU_FASTFLAGVARIABLE(LuauTypecheckOptPass, false)
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false) LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
@ -54,12 +50,9 @@ LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as
LUAU_FASTFLAG(LuauWidenIfSupertypeIsFree2) LUAU_FASTFLAG(LuauWidenIfSupertypeIsFree2)
LUAU_FASTFLAGVARIABLE(LuauDoNotTryToReduce, false) LUAU_FASTFLAGVARIABLE(LuauDoNotTryToReduce, false)
LUAU_FASTFLAGVARIABLE(LuauDoNotAccidentallyDependOnPointerOrdering, false) LUAU_FASTFLAGVARIABLE(LuauDoNotAccidentallyDependOnPointerOrdering, false)
LUAU_FASTFLAGVARIABLE(LuauFixArgumentCountMismatchAmountWithGenericTypes, false)
LUAU_FASTFLAGVARIABLE(LuauFixIncorrectLineNumberDuplicateType, false)
LUAU_FASTFLAGVARIABLE(LuauCheckImplicitNumbericKeys, false) LUAU_FASTFLAGVARIABLE(LuauCheckImplicitNumbericKeys, false)
LUAU_FASTFLAG(LuauAnyInIsOptionalIsOptional) LUAU_FASTFLAG(LuauAnyInIsOptionalIsOptional)
LUAU_FASTFLAGVARIABLE(LuauDecoupleOperatorInferenceFromUnifiedTypeInference, false) LUAU_FASTFLAGVARIABLE(LuauDecoupleOperatorInferenceFromUnifiedTypeInference, false)
LUAU_FASTFLAGVARIABLE(LuauArgCountMismatchSaysAtLeastWhenVariadic, false)
LUAU_FASTFLAGVARIABLE(LuauTableUseCounterInstead, false) LUAU_FASTFLAGVARIABLE(LuauTableUseCounterInstead, false)
LUAU_FASTFLAGVARIABLE(LuauReturnTypeInferenceInNonstrict, false) LUAU_FASTFLAGVARIABLE(LuauReturnTypeInferenceInNonstrict, false)
LUAU_FASTFLAGVARIABLE(LuauRecursionLimitException, false); LUAU_FASTFLAGVARIABLE(LuauRecursionLimitException, false);
@ -1160,7 +1153,10 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
} }
else else
{ {
iterTy = follow(instantiate(scope, checkExpr(scope, *firstValue).type, firstValue->location)); if (FFlag::LuauInstantiateFollows)
iterTy = instantiate(scope, checkExpr(scope, *firstValue).type, firstValue->location);
else
iterTy = follow(instantiate(scope, checkExpr(scope, *firstValue).type, firstValue->location));
} }
const FunctionTypeVar* iterFunc = get<FunctionTypeVar>(iterTy); const FunctionTypeVar* iterFunc = get<FunctionTypeVar>(iterTy);
@ -1172,7 +1168,12 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
unify(varTy, var, forin.location); unify(varTy, var, forin.location);
if (!get<ErrorTypeVar>(iterTy) && !get<AnyTypeVar>(iterTy) && !get<FreeTypeVar>(iterTy)) if (!get<ErrorTypeVar>(iterTy) && !get<AnyTypeVar>(iterTy) && !get<FreeTypeVar>(iterTy))
reportError(TypeError{firstValue->location, TypeMismatch{globalScope->bindings[AstName{"next"}].typeId, iterTy}}); {
if (FFlag::LuauDoNotRelyOnNextBinding)
reportError(firstValue->location, CannotCallNonFunction{iterTy});
else
reportError(TypeError{firstValue->location, TypeMismatch{globalScope->bindings[AstName{"next"}].typeId, iterTy}});
}
return check(loopScope, *forin.body); return check(loopScope, *forin.body);
} }
@ -1427,8 +1428,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
ftv->forwardedTypeAlias = true; ftv->forwardedTypeAlias = true;
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty}; bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
if (FFlag::LuauFixIncorrectLineNumberDuplicateType) scope->typeAliasLocations[name] = typealias.location;
scope->typeAliasLocations[name] = typealias.location;
} }
} }
else else
@ -2217,7 +2217,7 @@ TypeId TypeChecker::checkExprTable(
if (isNonstrictMode() && !getTableType(exprType) && !get<FunctionTypeVar>(exprType)) if (isNonstrictMode() && !getTableType(exprType) && !get<FunctionTypeVar>(exprType))
exprType = anyType; exprType = anyType;
if (FFlag::LuauPropertiesGetExpectedType && expectedTable) if (expectedTable)
{ {
auto it = expectedTable->props.find(key->value.data); auto it = expectedTable->props.find(key->value.data);
if (it != expectedTable->props.end()) if (it != expectedTable->props.end())
@ -2309,9 +2309,8 @@ ExprResult<TypeId> TypeChecker::checkExpr_(const ScopePtr& scope, const AstExprT
} }
} }
} }
else if (FFlag::LuauExpectedTypesOfProperties) else if (const UnionTypeVar* utv = get<UnionTypeVar>(follow(*expectedType)))
if (const UnionTypeVar* utv = get<UnionTypeVar>(follow(*expectedType))) expectedUnion = utv;
expectedUnion = utv;
} }
for (size_t i = 0; i < expr.items.size; ++i) for (size_t i = 0; i < expr.items.size; ++i)
@ -2334,7 +2333,7 @@ ExprResult<TypeId> TypeChecker::checkExpr_(const ScopePtr& scope, const AstExprT
if (auto prop = expectedTable->props.find(key->value.data); prop != expectedTable->props.end()) if (auto prop = expectedTable->props.find(key->value.data); prop != expectedTable->props.end())
expectedResultType = prop->second.type; expectedResultType = prop->second.type;
} }
else if (FFlag::LuauExpectedTypesOfProperties && expectedUnion) else if (expectedUnion)
{ {
std::vector<TypeId> expectedResultTypes; std::vector<TypeId> expectedResultTypes;
for (TypeId expectedOption : expectedUnion) for (TypeId expectedOption : expectedUnion)
@ -2713,8 +2712,6 @@ TypeId TypeChecker::checkBinaryOperation(
{ {
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, name, CannotInferBinaryOperation::Operation});
if (!FFlag::LuauErrorRecoveryType)
return errorRecoveryType(scope);
} }
} }
@ -2754,7 +2751,7 @@ TypeId TypeChecker::checkBinaryOperation(
reportErrors(state.errors); reportErrors(state.errors);
bool hasErrors = !state.errors.empty(); bool hasErrors = !state.errors.empty();
if (FFlag::LuauErrorRecoveryType && hasErrors) if (hasErrors)
{ {
// If there are unification errors, the return type may still be unknown // If there are unification errors, the return type may still be unknown
// so we loosen the argument types to see if that helps. // so we loosen the argument types to see if that helps.
@ -2768,8 +2765,7 @@ TypeId TypeChecker::checkBinaryOperation(
if (state.errors.empty()) if (state.errors.empty())
state.log.commit(); state.log.commit();
} }
else
if (!hasErrors)
{ {
state.log.commit(); state.log.commit();
} }
@ -3196,16 +3192,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName, T
} }
else else
{ {
if (!ttv) if (!ttv || lhsType->persistent || ttv->state == TableState::Sealed)
{
if (!FFlag::LuauErrorRecoveryType && !isTableIntersection(lhsType))
// This error now gets reported when we check the function body.
reportError(TypeError{funName.location, OnlyTablesCanHaveMethods{lhsType}});
return errorRecoveryType(scope);
}
if (lhsType->persistent || ttv->state == TableState::Sealed)
return errorRecoveryType(scope); return errorRecoveryType(scope);
} }
@ -3532,32 +3519,6 @@ ExprResult<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const A
} }
// Returns the minimum number of arguments the argument list can accept. // Returns the minimum number of arguments the argument list can accept.
static size_t getMinParameterCount_DEPRECATED(TypePackId tp)
{
size_t minCount = 0;
size_t optionalCount = 0;
auto it = begin(tp);
auto endIter = end(tp);
while (it != endIter)
{
TypeId ty = *it;
if (isOptional(ty))
++optionalCount;
else
{
minCount += optionalCount;
optionalCount = 0;
minCount++;
}
++it;
}
return minCount;
}
static size_t getMinParameterCount(TxnLog* log, TypePackId tp) static size_t getMinParameterCount(TxnLog* log, TypePackId tp)
{ {
size_t minCount = 0; size_t minCount = 0;
@ -3597,19 +3558,14 @@ void TypeChecker::checkArgumentList(
size_t paramIndex = 0; size_t paramIndex = 0;
size_t minParams = FFlag::LuauFixIncorrectLineNumberDuplicateType ? 0 : getMinParameterCount_DEPRECATED(paramPack); auto reportCountMismatchError = [&state, &argLocations, paramPack, argPack]() {
auto reportCountMismatchError = [&state, &argLocations, minParams, paramPack, argPack]() {
// For this case, we want the error span to cover every errant extra parameter // For this case, we want the error span to cover every errant extra parameter
Location location = state.location; Location location = state.location;
if (!argLocations.empty()) if (!argLocations.empty())
location = {state.location.begin, argLocations.back().end}; location = {state.location.begin, argLocations.back().end};
size_t mp = minParams; size_t minParams = getMinParameterCount(&state.log, paramPack);
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes) state.reportError(TypeError{location, CountMismatch{minParams, std::distance(begin(argPack), end(argPack))}});
mp = getMinParameterCount(&state.log, paramPack);
state.reportError(TypeError{location, CountMismatch{mp, std::distance(begin(argPack), end(argPack))}});
}; };
while (true) while (true)
@ -3707,16 +3663,10 @@ void TypeChecker::checkArgumentList(
} // ok } // ok
else else
{ {
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes) size_t minParams = getMinParameterCount(&state.log, paramPack);
minParams = getMinParameterCount(&state.log, paramPack);
bool isVariadic = false; std::optional<TypePackId> tail = flatten(paramPack, state.log).second;
if (FFlag::LuauArgCountMismatchSaysAtLeastWhenVariadic) bool isVariadic = tail && Luau::isVariadic(*tail);
{
std::optional<TypePackId> tail = flatten(paramPack, state.log).second;
if (tail)
isVariadic = Luau::isVariadic(*tail);
}
state.reportError(TypeError{state.location, CountMismatch{minParams, paramIndex, CountMismatch::Context::Arg, isVariadic}}); state.reportError(TypeError{state.location, CountMismatch{minParams, paramIndex, CountMismatch::Context::Arg, isVariadic}});
return; return;
@ -3863,7 +3813,8 @@ ExprResult<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const A
actualFunctionType = instantiate(scope, functionType, expr.func->location); actualFunctionType = instantiate(scope, functionType, expr.func->location);
} }
actualFunctionType = follow(actualFunctionType); if (!FFlag::LuauInstantiateFollows)
actualFunctionType = follow(actualFunctionType);
TypePackId retPack; TypePackId retPack;
if (FFlag::LuauLowerBoundsCalculation || !FFlag::LuauWidenIfSupertypeIsFree2) if (FFlag::LuauLowerBoundsCalculation || !FFlag::LuauWidenIfSupertypeIsFree2)
@ -3930,16 +3881,13 @@ ExprResult<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const A
reportOverloadResolutionError(scope, expr, retPack, argPack, argLocations, overloads, overloadsThatMatchArgCount, errors); reportOverloadResolutionError(scope, expr, retPack, argPack, argLocations, overloads, overloadsThatMatchArgCount, errors);
if (FFlag::LuauErrorRecoveryType) const FunctionTypeVar* overload = nullptr;
{ if (!overloadsThatMatchArgCount.empty())
const FunctionTypeVar* overload = nullptr; overload = get<FunctionTypeVar>(overloadsThatMatchArgCount[0]);
if (!overloadsThatMatchArgCount.empty()) if (!overload && !overloadsThatDont.empty())
overload = get<FunctionTypeVar>(overloadsThatMatchArgCount[0]); overload = get<FunctionTypeVar>(overloadsThatDont[0]);
if (!overload && !overloadsThatDont.empty()) if (overload)
overload = get<FunctionTypeVar>(overloadsThatDont[0]); return {errorRecoveryTypePack(overload->retType)};
if (overload)
return {errorRecoveryTypePack(overload->retType)};
}
return {errorRecoveryTypePack(retPack)}; return {errorRecoveryTypePack(retPack)};
} }
@ -4129,7 +4077,7 @@ std::optional<ExprResult<TypePackId>> TypeChecker::checkCallOverload(const Scope
if (!argMismatch) if (!argMismatch)
overloadsThatMatchArgCount.push_back(fn); overloadsThatMatchArgCount.push_back(fn);
else if (FFlag::LuauErrorRecoveryType) else
overloadsThatDont.push_back(fn); overloadsThatDont.push_back(fn);
errors.emplace_back(std::move(state.errors), args->head, ftv); errors.emplace_back(std::move(state.errors), args->head, ftv);
@ -4715,7 +4663,7 @@ bool Anyification::isDirty(TypeId ty)
return false; return false;
if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty)) if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
return (ttv->state == TableState::Free || (FFlag::LuauSealExports && ttv->state == TableState::Unsealed)); return (ttv->state == TableState::Free || ttv->state == TableState::Unsealed);
else if (log->getMutable<FreeTypeVar>(ty)) else if (log->getMutable<FreeTypeVar>(ty))
return true; return true;
else if (get<ConstrainedTypeVar>(ty)) else if (get<ConstrainedTypeVar>(ty))
@ -4743,12 +4691,9 @@ TypeId Anyification::clean(TypeId ty)
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, ttv->level, TableState::Sealed}; TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, ttv->level, TableState::Sealed};
clone.methodDefinitionLocations = ttv->methodDefinitionLocations; clone.methodDefinitionLocations = ttv->methodDefinitionLocations;
clone.definitionModuleName = ttv->definitionModuleName; clone.definitionModuleName = ttv->definitionModuleName;
if (FFlag::LuauSealExports) clone.name = ttv->name;
{ clone.syntheticName = ttv->syntheticName;
clone.name = ttv->name; clone.tags = ttv->tags;
clone.syntheticName = ttv->syntheticName;
clone.tags = ttv->tags;
}
TypeId res = addType(std::move(clone)); TypeId res = addType(std::move(clone));
asMutable(res)->normal = ty->normal; asMutable(res)->normal = ty->normal;
return res; return res;
@ -4791,9 +4736,12 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log) TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log)
{ {
if (FFlag::LuauInstantiateFollows)
ty = follow(ty);
if (FFlag::LuauTypecheckOptPass) if (FFlag::LuauTypecheckOptPass)
{ {
const FunctionTypeVar* ftv = get<FunctionTypeVar>(follow(ty)); const FunctionTypeVar* ftv = get<FunctionTypeVar>(FFlag::LuauInstantiateFollows ? ty : follow(ty));
if (ftv && ftv->hasNoGenerics) if (ftv && ftv->hasNoGenerics)
return ty; return ty;
} }
@ -5175,8 +5123,6 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
{ {
reportError(TypeError{annotation.location, GenericError{"Type parameter list is required"}}); reportError(TypeError{annotation.location, GenericError{"Type parameter list is required"}});
parameterCountErrorReported = true; parameterCountErrorReported = true;
if (!FFlag::LuauErrorRecoveryType)
return errorRecoveryType(scope);
} }
} }
@ -5294,33 +5240,25 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
reportError( reportError(
TypeError{annotation.location, IncorrectGenericParameterCount{lit->name.value, *tf, typeParams.size(), typePackParams.size()}}); TypeError{annotation.location, IncorrectGenericParameterCount{lit->name.value, *tf, typeParams.size(), typePackParams.size()}});
if (FFlag::LuauErrorRecoveryType) // Pad the types out with error recovery types
{ while (typeParams.size() < tf->typeParams.size())
// Pad the types out with error recovery types typeParams.push_back(errorRecoveryType(scope));
while (typeParams.size() < tf->typeParams.size()) while (typePackParams.size() < tf->typePackParams.size())
typeParams.push_back(errorRecoveryType(scope)); typePackParams.push_back(errorRecoveryTypePack(scope));
while (typePackParams.size() < tf->typePackParams.size())
typePackParams.push_back(errorRecoveryTypePack(scope));
}
else
return errorRecoveryType(scope);
} }
if (FFlag::LuauRecursiveTypeParameterRestriction) bool sameTys = std::equal(typeParams.begin(), typeParams.end(), tf->typeParams.begin(), tf->typeParams.end(), [](auto&& itp, auto&& tp) {
{ return itp == tp.ty;
bool sameTys = std::equal(typeParams.begin(), typeParams.end(), tf->typeParams.begin(), tf->typeParams.end(), [](auto&& itp, auto&& tp) { });
return itp == tp.ty; bool sameTps = std::equal(
typePackParams.begin(), typePackParams.end(), tf->typePackParams.begin(), tf->typePackParams.end(), [](auto&& itpp, auto&& tpp) {
return itpp == tpp.tp;
}); });
bool sameTps = std::equal(
typePackParams.begin(), typePackParams.end(), tf->typePackParams.begin(), tf->typePackParams.end(), [](auto&& itpp, auto&& tpp) {
return itpp == tpp.tp;
});
// If the generic parameters and the type arguments are the same, we are about to // If the generic parameters and the type arguments are the same, we are about to
// perform an identity substitution, which we can just short-circuit. // perform an identity substitution, which we can just short-circuit.
if (sameTys && sameTps) if (sameTys && sameTps)
return tf->type; return tf->type;
}
return instantiateTypeFun(scope, *tf, typeParams, typePackParams, annotation.location); return instantiateTypeFun(scope, *tf, typeParams, typePackParams, annotation.location);
} }
@ -5483,7 +5421,7 @@ bool ApplyTypeFunction::isDirty(TypeId ty)
return true; return true;
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty)) else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
{ {
if (FFlag::LuauRecursiveTypeParameterRestriction && ftv->forwardedTypeAlias) if (ftv->forwardedTypeAlias)
encounteredForwardedType = true; encounteredForwardedType = true;
return false; return false;
} }
@ -5562,7 +5500,7 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf,
reportError(location, UnificationTooComplex{}); reportError(location, UnificationTooComplex{});
return errorRecoveryType(scope); return errorRecoveryType(scope);
} }
if (FFlag::LuauRecursiveTypeParameterRestriction && applyTypeFunction.encounteredForwardedType) if (applyTypeFunction.encounteredForwardedType)
{ {
reportError(TypeError{location, GenericError{"Recursive type being used with different parameters"}}); reportError(TypeError{location, GenericError{"Recursive type being used with different parameters"}});
return errorRecoveryType(scope); return errorRecoveryType(scope);
@ -5632,7 +5570,7 @@ GenericTypeDefinitions TypeChecker::createGenericTypes(const ScopePtr& scope, st
} }
TypeId g; TypeId g;
if (FFlag::LuauRecursiveTypeParameterRestriction && (!FFlag::LuauGenericFunctionsDontCacheTypeParams || useCache)) if (useCache)
{ {
TypeId& cached = scope->parent->typeAliasTypeParameters[n]; TypeId& cached = scope->parent->typeAliasTypeParameters[n];
if (!cached) if (!cached)
@ -5667,21 +5605,12 @@ GenericTypeDefinitions TypeChecker::createGenericTypes(const ScopePtr& scope, st
reportError(TypeError{node.location, DuplicateGenericParameter{n}}); reportError(TypeError{node.location, DuplicateGenericParameter{n}});
} }
TypePackId g; TypePackId& cached = scope->parent->typeAliasTypePackParameters[n];
if (FFlag::LuauRecursiveTypeParameterRestriction) if (!cached)
{ cached = addTypePack(TypePackVar{Unifiable::Generic{level, n}});
TypePackId& cached = scope->parent->typeAliasTypePackParameters[n];
if (!cached)
cached = addTypePack(TypePackVar{Unifiable::Generic{level, n}});
g = cached;
}
else
{
g = addTypePack(TypePackVar{Unifiable::Generic{level, n}});
}
genericPacks.push_back({g, defaultValue}); genericPacks.push_back({cached, defaultValue});
scope->privateTypePackBindings[n] = g; scope->privateTypePackBindings[n] = cached;
} }
return {generics, genericPacks}; return {generics, genericPacks};

View File

@ -23,7 +23,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500) LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0) LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauErrorRecoveryType)
LUAU_FASTFLAG(LuauSubtypingAddOptPropsToUnsealedTables) LUAU_FASTFLAG(LuauSubtypingAddOptPropsToUnsealedTables)
LUAU_FASTFLAG(LuauDiscriminableUnions2) LUAU_FASTFLAG(LuauDiscriminableUnions2)
LUAU_FASTFLAGVARIABLE(LuauAnyInIsOptionalIsOptional, false) LUAU_FASTFLAGVARIABLE(LuauAnyInIsOptionalIsOptional, false)
@ -775,18 +774,12 @@ TypePackId SingletonTypes::errorRecoveryTypePack()
TypeId SingletonTypes::errorRecoveryType(TypeId guess) TypeId SingletonTypes::errorRecoveryType(TypeId guess)
{ {
if (FFlag::LuauErrorRecoveryType) return guess;
return guess;
else
return &errorType_;
} }
TypePackId SingletonTypes::errorRecoveryTypePack(TypePackId guess) TypePackId SingletonTypes::errorRecoveryTypePack(TypePackId guess)
{ {
if (FFlag::LuauErrorRecoveryType) return guess;
return guess;
else
return &errorTypePack_;
} }
SingletonTypes& getSingletonTypes() SingletonTypes& getSingletonTypes()

View File

@ -23,10 +23,7 @@ LUAU_FASTFLAG(LuauErrorRecoveryType);
LUAU_FASTFLAGVARIABLE(LuauSubtypingAddOptPropsToUnsealedTables, false) LUAU_FASTFLAGVARIABLE(LuauSubtypingAddOptPropsToUnsealedTables, false)
LUAU_FASTFLAGVARIABLE(LuauWidenIfSupertypeIsFree2, false) LUAU_FASTFLAGVARIABLE(LuauWidenIfSupertypeIsFree2, false)
LUAU_FASTFLAGVARIABLE(LuauDifferentOrderOfUnificationDoesntMatter, false) LUAU_FASTFLAGVARIABLE(LuauDifferentOrderOfUnificationDoesntMatter, false)
LUAU_FASTFLAGVARIABLE(LuauTxnLogSeesTypePacks2, false)
LUAU_FASTFLAGVARIABLE(LuauTxnLogCheckForInvalidation, false)
LUAU_FASTFLAGVARIABLE(LuauTxnLogRefreshFunctionPointers, false) LUAU_FASTFLAGVARIABLE(LuauTxnLogRefreshFunctionPointers, false)
LUAU_FASTFLAGVARIABLE(LuauTxnLogDontRetryForIndexers, false)
LUAU_FASTFLAG(LuauAnyInIsOptionalIsOptional) LUAU_FASTFLAG(LuauAnyInIsOptionalIsOptional)
LUAU_FASTFLAG(LuauTypecheckOptPass) LUAU_FASTFLAG(LuauTypecheckOptPass)
@ -1021,7 +1018,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
if (superTp == subTp) if (superTp == subTp)
return; return;
if (FFlag::LuauTxnLogSeesTypePacks2 && log.haveSeen(superTp, subTp)) if (log.haveSeen(superTp, subTp))
return; return;
if (log.getMutable<Unifiable::Free>(superTp)) if (log.getMutable<Unifiable::Free>(superTp))
@ -1265,12 +1262,9 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
log.pushSeen(superFunction->generics[i], subFunction->generics[i]); log.pushSeen(superFunction->generics[i], subFunction->generics[i]);
} }
if (FFlag::LuauTxnLogSeesTypePacks2) for (size_t i = 0; i < numGenericPacks; i++)
{ {
for (size_t i = 0; i < numGenericPacks; i++) log.pushSeen(superFunction->genericPacks[i], subFunction->genericPacks[i]);
{
log.pushSeen(superFunction->genericPacks[i], subFunction->genericPacks[i]);
}
} }
CountMismatch::Context context = ctx; CountMismatch::Context context = ctx;
@ -1330,12 +1324,9 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
ctx = context; ctx = context;
if (FFlag::LuauTxnLogSeesTypePacks2) for (int i = int(numGenericPacks) - 1; 0 <= i; i--)
{ {
for (int i = int(numGenericPacks) - 1; 0 <= i; i--) log.popSeen(superFunction->genericPacks[i], subFunction->genericPacks[i]);
{
log.popSeen(superFunction->genericPacks[i], subFunction->genericPacks[i]);
}
} }
for (int i = int(numGenerics) - 1; 0 <= i; i--) for (int i = int(numGenerics) - 1; 0 <= i; i--)
@ -1499,20 +1490,17 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
else else
missingProperties.push_back(name); missingProperties.push_back(name);
if (FFlag::LuauTxnLogCheckForInvalidation) // Recursive unification can change the txn log, and invalidate the old
// table. If we detect that this has happened, we start over, with the updated
// txn log.
TableTypeVar* newSuperTable = log.getMutable<TableTypeVar>(superTy);
TableTypeVar* newSubTable = log.getMutable<TableTypeVar>(subTy);
if (superTable != newSuperTable || subTable != newSubTable)
{ {
// Recursive unification can change the txn log, and invalidate the old if (errors.empty())
// table. If we detect that this has happened, we start over, with the updated return tryUnifyTables(subTy, superTy, isIntersection);
// txn log. else
TableTypeVar* newSuperTable = log.getMutable<TableTypeVar>(superTy); return;
TableTypeVar* newSubTable = log.getMutable<TableTypeVar>(subTy);
if (superTable != newSuperTable || subTable != newSubTable)
{
if (errors.empty())
return tryUnifyTables(subTy, superTy, isIntersection);
else
return;
}
} }
} }
@ -1570,20 +1558,17 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
else else
extraProperties.push_back(name); extraProperties.push_back(name);
if (FFlag::LuauTxnLogCheckForInvalidation) // Recursive unification can change the txn log, and invalidate the old
// table. If we detect that this has happened, we start over, with the updated
// txn log.
TableTypeVar* newSuperTable = log.getMutable<TableTypeVar>(superTy);
TableTypeVar* newSubTable = log.getMutable<TableTypeVar>(subTy);
if (superTable != newSuperTable || subTable != newSubTable)
{ {
// Recursive unification can change the txn log, and invalidate the old if (errors.empty())
// table. If we detect that this has happened, we start over, with the updated return tryUnifyTables(subTy, superTy, isIntersection);
// txn log. else
TableTypeVar* newSuperTable = log.getMutable<TableTypeVar>(superTy); return;
TableTypeVar* newSubTable = log.getMutable<TableTypeVar>(subTy);
if (superTable != newSuperTable || subTable != newSubTable)
{
if (errors.empty())
return tryUnifyTables(subTy, superTy, isIntersection);
else
return;
}
} }
} }
@ -1630,27 +1615,9 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
} }
} }
if (FFlag::LuauTxnLogDontRetryForIndexers) // Changing the indexer can invalidate the table pointers.
{ superTable = log.getMutable<TableTypeVar>(superTy);
// Changing the indexer can invalidate the table pointers. subTable = log.getMutable<TableTypeVar>(subTy);
superTable = log.getMutable<TableTypeVar>(superTy);
subTable = log.getMutable<TableTypeVar>(subTy);
}
else if (FFlag::LuauTxnLogCheckForInvalidation)
{
// Recursive unification can change the txn log, and invalidate the old
// table. If we detect that this has happened, we start over, with the updated
// txn log.
TableTypeVar* newSuperTable = log.getMutable<TableTypeVar>(superTy);
TableTypeVar* newSubTable = log.getMutable<TableTypeVar>(subTy);
if (superTable != newSuperTable || subTable != newSubTable)
{
if (errors.empty())
return tryUnifyTables(subTy, superTy, isIntersection);
else
return;
}
}
if (!missingProperties.empty()) if (!missingProperties.empty())
{ {

View File

@ -6,8 +6,6 @@
#include <limits.h> #include <limits.h>
LUAU_FASTFLAGVARIABLE(LuauParseLocationIgnoreCommentSkip, false)
namespace Luau namespace Luau
{ {
@ -361,7 +359,7 @@ const Lexeme& Lexer::next(bool skipComments, bool updatePrevLocation)
while (isSpace(peekch())) while (isSpace(peekch()))
consume(); consume();
if (!FFlag::LuauParseLocationIgnoreCommentSkip || updatePrevLocation) if (updatePrevLocation)
prevLocation = lexeme.location; prevLocation = lexeme.location;
lexeme = readNext(); lexeme = readNext();

View File

@ -240,7 +240,7 @@ std::optional<std::string> getParentPath(const std::string& path)
return std::nullopt; return std::nullopt;
#endif #endif
std::string::size_type slash = path.find_last_of("\\/", path.size() - 1); size_t slash = path.find_last_of("\\/", path.size() - 1);
if (slash == 0) if (slash == 0)
return "/"; return "/";
@ -253,7 +253,7 @@ std::optional<std::string> getParentPath(const std::string& path)
static std::string getExtension(const std::string& path) static std::string getExtension(const std::string& path)
{ {
std::string::size_type dot = path.find_last_of(".\\/"); size_t dot = path.find_last_of(".\\/");
if (dot == std::string::npos || path[dot] != '.') if (dot == std::string::npos || path[dot] != '.')
return ""; return "";

View File

@ -34,7 +34,8 @@ enum class CliMode
enum class CompileFormat enum class CompileFormat
{ {
Text, Text,
Binary Binary,
Null
}; };
constexpr int MaxTraversalLimit = 50; constexpr int MaxTraversalLimit = 50;
@ -594,6 +595,8 @@ static bool compileFile(const char* name, CompileFormat format)
case CompileFormat::Binary: case CompileFormat::Binary:
fwrite(bcb.getBytecode().data(), 1, bcb.getBytecode().size(), stdout); fwrite(bcb.getBytecode().data(), 1, bcb.getBytecode().size(), stdout);
break; break;
case CompileFormat::Null:
break;
} }
return true; return true;
@ -716,6 +719,10 @@ int replMain(int argc, char** argv)
{ {
compileFormat = CompileFormat::Text; compileFormat = CompileFormat::Text;
} }
else if (strcmp(argv[1], "--compile=null") == 0)
{
compileFormat = CompileFormat::Null;
}
else else
{ {
fprintf(stderr, "Error: Unrecognized value for '--compile' specified.\n"); fprintf(stderr, "Error: Unrecognized value for '--compile' specified.\n");

View File

@ -232,7 +232,7 @@ private:
DenseHashMap<StringRef, unsigned int, StringRefHash> stringTable; DenseHashMap<StringRef, unsigned int, StringRefHash> stringTable;
DenseHashMap<uint32_t, uint32_t> debugRemarks; std::vector<std::pair<uint32_t, uint32_t>> debugRemarks;
std::string debugRemarkBuffer; std::string debugRemarkBuffer;
BytecodeEncoder* encoder = nullptr; BytecodeEncoder* encoder = nullptr;

View File

@ -181,7 +181,6 @@ BytecodeBuilder::BytecodeBuilder(BytecodeEncoder* encoder)
: constantMap({Constant::Type_Nil, ~0ull}) : constantMap({Constant::Type_Nil, ~0ull})
, tableShapeMap(TableShape()) , tableShapeMap(TableShape())
, stringTable({nullptr, 0}) , stringTable({nullptr, 0})
, debugRemarks(~0u)
, encoder(encoder) , encoder(encoder)
{ {
LUAU_ASSERT(stringTable.find(StringRef{"", 0}) == nullptr); LUAU_ASSERT(stringTable.find(StringRef{"", 0}) == nullptr);
@ -257,6 +256,8 @@ void BytecodeBuilder::endFunction(uint8_t maxstacksize, uint8_t numupvalues)
void BytecodeBuilder::setMainFunction(uint32_t fid) void BytecodeBuilder::setMainFunction(uint32_t fid)
{ {
LUAU_ASSERT(fid < functions.size());
mainFunction = fid; mainFunction = fid;
} }
@ -531,7 +532,7 @@ void BytecodeBuilder::addDebugRemark(const char* format, ...)
// we null-terminate all remarks to avoid storing remark length // we null-terminate all remarks to avoid storing remark length
debugRemarkBuffer += '\0'; debugRemarkBuffer += '\0';
debugRemarks[uint32_t(insns.size())] = uint32_t(offset); debugRemarks.emplace_back(uint32_t(insns.size()), uint32_t(offset));
} }
void BytecodeBuilder::finalize() void BytecodeBuilder::finalize()
@ -1719,6 +1720,7 @@ std::string BytecodeBuilder::dumpCurrentFunction() const
const uint32_t* codeEnd = insns.data() + insns.size(); const uint32_t* codeEnd = insns.data() + insns.size();
int lastLine = -1; int lastLine = -1;
size_t nextRemark = 0;
std::string result; std::string result;
@ -1741,6 +1743,7 @@ std::string BytecodeBuilder::dumpCurrentFunction() const
while (code != codeEnd) while (code != codeEnd)
{ {
uint8_t op = LUAU_INSN_OP(*code); uint8_t op = LUAU_INSN_OP(*code);
uint32_t pc = uint32_t(code - insns.data());
if (op == LOP_PREPVARARGS) if (op == LOP_PREPVARARGS)
{ {
@ -1751,15 +1754,16 @@ std::string BytecodeBuilder::dumpCurrentFunction() const
if (dumpFlags & Dump_Remarks) if (dumpFlags & Dump_Remarks)
{ {
const uint32_t* remark = debugRemarks.find(uint32_t(code - insns.data())); while (nextRemark < debugRemarks.size() && debugRemarks[nextRemark].first == pc)
{
if (remark) formatAppend(result, "REMARK %s\n", debugRemarkBuffer.c_str() + debugRemarks[nextRemark].second);
formatAppend(result, "REMARK %s\n", debugRemarkBuffer.c_str() + *remark); nextRemark++;
}
} }
if (dumpFlags & Dump_Source) if (dumpFlags & Dump_Source)
{ {
int line = lines[code - insns.data()]; int line = lines[pc];
if (line > 0 && line != lastLine) if (line > 0 && line != lastLine)
{ {
@ -1771,7 +1775,7 @@ std::string BytecodeBuilder::dumpCurrentFunction() const
if (dumpFlags & Dump_Lines) if (dumpFlags & Dump_Lines)
{ {
formatAppend(result, "%d: ", lines[code - insns.data()]); formatAppend(result, "%d: ", lines[pc]);
} }
code = dumpInstruction(code, result); code = dumpInstruction(code, result);
@ -1784,11 +1788,11 @@ void BytecodeBuilder::setDumpSource(const std::string& source)
{ {
dumpSource.clear(); dumpSource.clear();
std::string::size_type pos = 0; size_t pos = 0;
while (pos != std::string::npos) while (pos != std::string::npos)
{ {
std::string::size_type next = source.find('\n', pos); size_t next = source.find('\n', pos);
if (next == std::string::npos) if (next == std::string::npos)
{ {

View File

@ -2206,9 +2206,15 @@ struct Compiler
return false; return false;
} }
if (Variable* lv = variables.find(stat->var); lv && lv->written)
{
bytecode.addDebugRemark("loop unroll failed: mutable loop variable");
return false;
}
int tripCount = (to - from) / step + 1; int tripCount = (to - from) / step + 1;
if (tripCount > thresholdBase * thresholdMaxBoost / 100) if (tripCount > thresholdBase)
{ {
bytecode.addDebugRemark("loop unroll failed: too many iterations (%d)", tripCount); bytecode.addDebugRemark("loop unroll failed: too many iterations (%d)", tripCount);
return false; return false;

View File

@ -249,7 +249,7 @@ int computeCost(uint64_t model, const bool* varsConst, size_t varCount)
return cost; return cost;
for (size_t i = 0; i < varCount && i < 7; ++i) for (size_t i = 0; i < varCount && i < 7; ++i)
cost -= int((model >> (8 * i + 8)) & 0x7f) * varsConst[i]; cost -= int((model >> (i * 8 + 8)) & 0x7f) * varsConst[i];
return cost; return cost;
} }

View File

@ -220,8 +220,8 @@ if(TARGET Luau.UnitTest)
tests/Autocomplete.test.cpp tests/Autocomplete.test.cpp
tests/BuiltinDefinitions.test.cpp tests/BuiltinDefinitions.test.cpp
tests/Compiler.test.cpp tests/Compiler.test.cpp
tests/CostModel.test.cpp
tests/Config.test.cpp tests/Config.test.cpp
tests/CostModel.test.cpp
tests/Error.test.cpp tests/Error.test.cpp
tests/Frontend.test.cpp tests/Frontend.test.cpp
tests/JsonEncoder.test.cpp tests/JsonEncoder.test.cpp
@ -232,6 +232,7 @@ if(TARGET Luau.UnitTest)
tests/Normalize.test.cpp tests/Normalize.test.cpp
tests/Parser.test.cpp tests/Parser.test.cpp
tests/RequireTracer.test.cpp tests/RequireTracer.test.cpp
tests/RuntimeLimits.test.cpp
tests/StringUtils.test.cpp tests/StringUtils.test.cpp
tests/Symbol.test.cpp tests/Symbol.test.cpp
tests/ToDot.test.cpp tests/ToDot.test.cpp

View File

@ -299,7 +299,7 @@ LUA_API uintptr_t lua_encodepointer(lua_State* L, uintptr_t p);
LUA_API double lua_clock(); LUA_API double lua_clock();
LUA_API void lua_setuserdatadtor(lua_State* L, int tag, void (*dtor)(void*)); LUA_API void lua_setuserdatadtor(lua_State* L, int tag, void (*dtor)(lua_State*, void*));
LUA_API void lua_clonefunction(lua_State* L, int idx); LUA_API void lua_clonefunction(lua_State* L, int idx);

View File

@ -1323,7 +1323,7 @@ void lua_unref(lua_State* L, int ref)
return; return;
} }
void lua_setuserdatadtor(lua_State* L, int tag, void (*dtor)(void*)) void lua_setuserdatadtor(lua_State* L, int tag, void (*dtor)(lua_State*, void*))
{ {
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT); api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
L->global->udatagc[tag] = dtor; L->global->udatagc[tag] = dtor;

View File

@ -200,7 +200,7 @@ typedef struct global_State
uint64_t rngstate; /* PCG random number generator state */ uint64_t rngstate; /* PCG random number generator state */
uint64_t ptrenckey[4]; /* pointer encoding key for display */ uint64_t ptrenckey[4]; /* pointer encoding key for display */
void (*udatagc[LUA_UTAG_LIMIT])(void*); /* for each userdata tag, a gc callback to be called immediately before freeing memory */ void (*udatagc[LUA_UTAG_LIMIT])(lua_State*, void*); /* for each userdata tag, a gc callback to be called immediately before freeing memory */
lua_Callbacks cb; lua_Callbacks cb;

View File

@ -33,7 +33,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false)
LUAU_FASTFLAGVARIABLE(LuauTableNewBoundary2, false) LUAU_FASTFLAGVARIABLE(LuauTableNewBoundary2, false)
// max size of both array and hash part is 2^MAXBITS // max size of both array and hash part is 2^MAXBITS
@ -400,16 +399,9 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
{ {
if (!ttisnil(&t->array[i])) if (!ttisnil(&t->array[i]))
{ {
if (FFlag::LuauTableRehashRework) TValue ok;
{ setnvalue(&ok, cast_num(i + 1));
TValue ok; setobjt2t(L, newkey(L, t, &ok), &t->array[i]);
setnvalue(&ok, cast_num(i + 1));
setobjt2t(L, newkey(L, t, &ok), &t->array[i]);
}
else
{
setobjt2t(L, luaH_setnum(L, t, i + 1), &t->array[i]);
}
} }
} }
/* shrink array */ /* shrink array */
@ -418,30 +410,14 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
/* used for the migration check at the end */ /* used for the migration check at the end */
TValue* anew = t->array; TValue* anew = t->array;
/* re-insert elements from hash part */ /* re-insert elements from hash part */
if (FFlag::LuauTableRehashRework) for (int i = twoto(oldhsize) - 1; i >= 0; i--)
{ {
for (int i = twoto(oldhsize) - 1; i >= 0; i--) LuaNode* old = nold + i;
if (!ttisnil(gval(old)))
{ {
LuaNode* old = nold + i; TValue ok;
if (!ttisnil(gval(old))) getnodekey(L, &ok, old);
{ setobjt2t(L, arrayornewkey(L, t, &ok), gval(old));
TValue ok;
getnodekey(L, &ok, old);
setobjt2t(L, arrayornewkey(L, t, &ok), gval(old));
}
}
}
else
{
for (int i = twoto(oldhsize) - 1; i >= 0; i--)
{
LuaNode* old = nold + i;
if (!ttisnil(gval(old)))
{
TValue ok;
getnodekey(L, &ok, old);
setobjt2t(L, luaH_set(L, t, &ok), gval(old));
}
} }
} }
@ -559,7 +535,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
{ {
rehash(L, t, key); /* grow table */ rehash(L, t, key); /* grow table */
// after rehash, numeric keys might be located in the new array part, but won't be found in the node part /* after rehash, numeric keys might be located in the new array part, but won't be found in the node part */
return arrayornewkey(L, t, key); return arrayornewkey(L, t, key);
} }
@ -571,15 +547,8 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
{ /* cannot find a free place? */ { /* cannot find a free place? */
rehash(L, t, key); /* grow table */ rehash(L, t, key); /* grow table */
if (!FFlag::LuauTableRehashRework) /* after rehash, numeric keys might be located in the new array part, but won't be found in the node part */
{ return arrayornewkey(L, t, key);
return luaH_set(L, t, key); /* re-insert key into grown table */
}
else
{
// after rehash, numeric keys might be located in the new array part, but won't be found in the node part
return arrayornewkey(L, t, key);
}
} }
LUAU_ASSERT(n != dummynode); LUAU_ASSERT(n != dummynode);
TValue mk; TValue mk;

View File

@ -22,14 +22,21 @@ Udata* luaU_newudata(lua_State* L, size_t s, int tag)
void luaU_freeudata(lua_State* L, Udata* u, lua_Page* page) void luaU_freeudata(lua_State* L, Udata* u, lua_Page* page)
{ {
void (*dtor)(void*) = nullptr;
if (u->tag < LUA_UTAG_LIMIT) if (u->tag < LUA_UTAG_LIMIT)
{
void (*dtor)(lua_State*, void*) = nullptr;
dtor = L->global->udatagc[u->tag]; dtor = L->global->udatagc[u->tag];
if (dtor)
dtor(L, u->data);
}
else if (u->tag == UTAG_IDTOR) else if (u->tag == UTAG_IDTOR)
{
void (*dtor)(void*) = nullptr;
memcpy(&dtor, &u->data + u->len - sizeof(dtor), sizeof(dtor)); memcpy(&dtor, &u->data + u->len - sizeof(dtor), sizeof(dtor));
if (dtor)
dtor(u->data);
}
if (dtor)
dtor(u->data);
luaM_freegco(L, u, sizeudata(u->len), u->memcat, page); luaM_freegco(L, u, sizeudata(u->len), u->memcat, page);
} }

View File

@ -137,6 +137,21 @@ int registerTypes(Luau::TypeChecker& env)
return 0; return 0;
} }
static void setupFrontend(Luau::Frontend& frontend)
{
registerTypes(frontend.typeChecker);
Luau::freeze(frontend.typeChecker.globalTypes);
registerTypes(frontend.typeCheckerForAutocomplete);
Luau::freeze(frontend.typeCheckerForAutocomplete.globalTypes);
frontend.iceHandler.onInternalError = [](const char* error) {
printf("ICE: %s\n", error);
LUAU_ASSERT(!"ICE");
};
}
struct FuzzFileResolver : Luau::FileResolver struct FuzzFileResolver : Luau::FileResolver
{ {
std::optional<Luau::SourceCode> readSource(const Luau::ModuleName& name) override std::optional<Luau::SourceCode> readSource(const Luau::ModuleName& name) override
@ -238,19 +253,11 @@ DEFINE_PROTO_FUZZER(const luau::ModuleSet& message)
if (kFuzzTypeck) if (kFuzzTypeck)
{ {
static FuzzFileResolver fileResolver; static FuzzFileResolver fileResolver;
static Luau::NullConfigResolver configResolver; static FuzzConfigResolver configResolver;
static Luau::FrontendOptions options{true, true}; static Luau::FrontendOptions options{true, true};
static Luau::Frontend frontend(&fileResolver, &configResolver, options); static Luau::Frontend frontend(&fileResolver, &configResolver, options);
static int once = registerTypes(frontend.typeChecker); static int once = (setupFrontend(frontend), 0);
(void)once;
static int once2 = (Luau::freeze(frontend.typeChecker.globalTypes), 0);
(void)once2;
frontend.iceHandler.onInternalError = [](const char* error) {
printf("ICE: %s\n", error);
LUAU_ASSERT(!"ICE");
};
// restart // restart
frontend.clear(); frontend.clear();

View File

@ -2761,7 +2761,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_on_string_singletons")
TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons") TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons")
{ {
ScopedFastFlag luauAutocompleteSingletonTypes{"LuauAutocompleteSingletonTypes", true}; ScopedFastFlag luauAutocompleteSingletonTypes{"LuauAutocompleteSingletonTypes", true};
ScopedFastFlag luauExpectedTypesOfProperties{"LuauExpectedTypesOfProperties", true};
check(R"( check(R"(
type tag = "cat" | "dog" type tag = "cat" | "dog"

View File

@ -2698,16 +2698,22 @@ TEST_CASE("DebugRemarks")
uint32_t fid = bcb.beginFunction(0); uint32_t fid = bcb.beginFunction(0);
bcb.addDebugRemark("test remark #%d", 42); bcb.addDebugRemark("test remark #%d", 1);
bcb.emitABC(LOP_LOADNIL, 0, 0, 0);
bcb.addDebugRemark("test remark #%d", 2);
bcb.addDebugRemark("test remark #%d", 3);
bcb.emitABC(LOP_RETURN, 0, 1, 0); bcb.emitABC(LOP_RETURN, 0, 1, 0);
bcb.endFunction(0, 0); bcb.endFunction(1, 0);
bcb.setMainFunction(fid); bcb.setMainFunction(fid);
bcb.finalize(); bcb.finalize();
CHECK_EQ("\n" + bcb.dumpFunction(0), R"( CHECK_EQ("\n" + bcb.dumpFunction(0), R"(
REMARK test remark #42 REMARK test remark #1
LOADNIL R0
REMARK test remark #2
REMARK test remark #3
RETURN R0 0 RETURN R0 0
)"); )");
} }
@ -4332,7 +4338,7 @@ RETURN R0 1
// loops with body that's long but has a high boost factor due to constant folding // loops with body that's long but has a high boost factor due to constant folding
CHECK_EQ("\n" + compileFunction(R"( CHECK_EQ("\n" + compileFunction(R"(
local t = {} local t = {}
for i=1,30 do for i=1,25 do
t[i] = i * i * i t[i] = i * i * i
end end
return t return t
@ -4390,16 +4396,6 @@ LOADN R1 13824
SETTABLEN R1 R0 24 SETTABLEN R1 R0 24
LOADN R1 15625 LOADN R1 15625
SETTABLEN R1 R0 25 SETTABLEN R1 R0 25
LOADN R1 17576
SETTABLEN R1 R0 26
LOADN R1 19683
SETTABLEN R1 R0 27
LOADN R1 21952
SETTABLEN R1 R0 28
LOADN R1 24389
SETTABLEN R1 R0 29
LOADN R1 27000
SETTABLEN R1 R0 30
RETURN R0 1 RETURN R0 1
)"); )");
@ -4431,4 +4427,30 @@ RETURN R0 1
)"); )");
} }
TEST_CASE("LoopUnrollMutable")
{
// can't unroll loops that mutate iteration variable
CHECK_EQ("\n" + compileFunction(R"(
for i=1,3 do
i = 3
print(i) -- should print 3 three times in a row
end
)",
0, 2),
R"(
LOADN R2 1
LOADN R0 3
LOADN R1 1
FORNPREP R0 +7
MOVE R3 R2
LOADN R3 3
GETIMPORT R4 1
MOVE R5 R3
CALL R4 1 0
FORNLOOP R0 -7
RETURN R0 0
)");
}
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -1056,7 +1056,7 @@ TEST_CASE("UserdataApi")
lua_State* L = globalState.get(); lua_State* L = globalState.get();
// setup dtor for tag 42 (created later) // setup dtor for tag 42 (created later)
lua_setuserdatadtor(L, 42, [](void* data) { lua_setuserdatadtor(L, 42, [](lua_State* l, void* data) {
dtorhits += *(int*)data; dtorhits += *(int*)data;
}); });

View File

@ -975,8 +975,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "typecheck_twice_for_ast_types")
TEST_CASE_FIXTURE(FrontendFixture, "imported_table_modification_2") TEST_CASE_FIXTURE(FrontendFixture, "imported_table_modification_2")
{ {
ScopedFastFlag sffs("LuauSealExports", true);
frontend.options.retainFullTypeGraphs = false; frontend.options.retainFullTypeGraphs = false;
fileResolver.source["Module/A"] = R"( fileResolver.source["Module/A"] = R"(
@ -1035,4 +1033,20 @@ return false;
fix.frontend.check("Module/B"); fix.frontend.check("Module/B");
} }
TEST_CASE("check_without_builtin_next")
{
ScopedFastFlag luauDoNotRelyOnNextBinding{"LuauDoNotRelyOnNextBinding", true};
TestFileResolver fileResolver;
TestConfigResolver configResolver;
Frontend frontend(&fileResolver, &configResolver);
fileResolver.source["Module/A"] = "for k,v in 2 do end";
fileResolver.source["Module/B"] = "return next";
// We don't care about the result. That we haven't crashed is enough.
frontend.check("Module/A");
frontend.check("Module/B");
}
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -199,7 +199,6 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
TEST_CASE_FIXTURE(Fixture, "clone_free_types") TEST_CASE_FIXTURE(Fixture, "clone_free_types")
{ {
ScopedFastFlag sff[]{ ScopedFastFlag sff[]{
{"LuauErrorRecoveryType", true},
{"LuauLosslessClone", true}, {"LuauLosslessClone", true},
}; };

View File

@ -283,7 +283,6 @@ TEST_CASE_FIXTURE(Fixture, "inconsistent_module_return_types_are_ok")
ScopedFastFlag sff[]{ ScopedFastFlag sff[]{
{"LuauReturnTypeInferenceInNonstrict", true}, {"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true}, {"LuauLowerBoundsCalculation", true},
{"LuauSealExports", true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(

View File

@ -1606,8 +1606,6 @@ TEST_CASE_FIXTURE(Fixture, "end_extent_of_functions_unions_and_intersections")
TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments") TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments")
{ {
ScopedFastFlag luauParseLocationIgnoreCommentSkip{"LuauParseLocationIgnoreCommentSkip", true};
AstStatBlock* block = parse(R"( AstStatBlock* block = parse(R"(
type F = number type F = number
--comment --comment
@ -1620,7 +1618,6 @@ TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments")
TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments_even_with_capture") TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments_even_with_capture")
{ {
ScopedFastFlag luauParseLocationIgnoreCommentSkip{"LuauParseLocationIgnoreCommentSkip", true};
ScopedFastFlag luauParseLocationIgnoreCommentSkipInCapture{"LuauParseLocationIgnoreCommentSkipInCapture", true}; ScopedFastFlag luauParseLocationIgnoreCommentSkipInCapture{"LuauParseLocationIgnoreCommentSkipInCapture", true};
// Same should hold when comments are captured // Same should hold when comments are captured

View File

@ -0,0 +1,270 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
/* Tests in this source file are meant to be a bellwether to verify that the numeric limits we've set are sufficient for
* most real-world scripts.
*
* If a change breaks a test in this source file, please don't adjust the flag values set in the fixture. Instead,
* consider it a latent performance problem by default.
*
* We should periodically revisit this to retest the limits.
*/
#include "Fixture.h"
#include "doctest.h"
using namespace Luau;
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
struct LimitFixture : Fixture
{
#if defined(_NOOPT)
ScopedFastInt LuauTypeInferRecursionLimit{"LuauTypeInferRecursionLimit", 150};
#endif
ScopedFastFlag LuauJustOneCallFrameForHaveSeen{"LuauJustOneCallFrameForHaveSeen", true};
};
template <typename T>
bool hasError(const CheckResult& result, T* = nullptr)
{
auto it = std::find_if(result.errors.begin(), result.errors.end(), [](const TypeError& a) {
return nullptr != get<T>(a);
});
return it != result.errors.end();
}
TEST_SUITE_BEGIN("RuntimeLimitTests");
TEST_CASE_FIXTURE(LimitFixture, "bail_early_on_typescript_port_of_Result_type" * doctest::timeout(1.0))
{
constexpr const char* src = R"LUA(
--!strict
local TS = _G[script]
local lazyGet = TS.import(script, script.Parent.Parent, "util", "lazyLoad").lazyGet
local unit = TS.import(script, script.Parent.Parent, "util", "Unit").unit
local Iterator
lazyGet("Iterator", function(c)
Iterator = c
end)
local Option
lazyGet("Option", function(c)
Option = c
end)
local Vec
lazyGet("Vec", function(c)
Vec = c
end)
local Result
do
Result = setmetatable({}, {
__tostring = function()
return "Result"
end,
})
Result.__index = Result
function Result.new(...)
local self = setmetatable({}, Result)
self:constructor(...)
return self
end
function Result:constructor(okValue, errValue)
self.okValue = okValue
self.errValue = errValue
end
function Result:ok(val)
return Result.new(val, nil)
end
function Result:err(val)
return Result.new(nil, val)
end
function Result:fromCallback(c)
local _0 = c
local _1, _2 = pcall(_0)
local result = _1 and {
success = true,
value = _2,
} or {
success = false,
error = _2,
}
return result.success and Result:ok(result.value) or Result:err(Option:wrap(result.error))
end
function Result:fromVoidCallback(c)
local _0 = c
local _1, _2 = pcall(_0)
local result = _1 and {
success = true,
value = _2,
} or {
success = false,
error = _2,
}
return result.success and Result:ok(unit()) or Result:err(Option:wrap(result.error))
end
Result.fromPromise = TS.async(function(self, p)
local _0, _1 = TS.try(function()
return TS.TRY_RETURN, { Result:ok(TS.await(p)) }
end, function(e)
return TS.TRY_RETURN, { Result:err(Option:wrap(e)) }
end)
if _0 then
return unpack(_1)
end
end)
Result.fromVoidPromise = TS.async(function(self, p)
local _0, _1 = TS.try(function()
TS.await(p)
return TS.TRY_RETURN, { Result:ok(unit()) }
end, function(e)
return TS.TRY_RETURN, { Result:err(Option:wrap(e)) }
end)
if _0 then
return unpack(_1)
end
end)
function Result:isOk()
return self.okValue ~= nil
end
function Result:isErr()
return self.errValue ~= nil
end
function Result:contains(x)
return self.okValue == x
end
function Result:containsErr(x)
return self.errValue == x
end
function Result:okOption()
return Option:wrap(self.okValue)
end
function Result:errOption()
return Option:wrap(self.errValue)
end
function Result:map(func)
return self:isOk() and Result:ok(func(self.okValue)) or Result:err(self.errValue)
end
function Result:mapOr(def, func)
local _0
if self:isOk() then
_0 = func(self.okValue)
else
_0 = def
end
return _0
end
function Result:mapOrElse(def, func)
local _0
if self:isOk() then
_0 = func(self.okValue)
else
_0 = def(self.errValue)
end
return _0
end
function Result:mapErr(func)
return self:isErr() and Result:err(func(self.errValue)) or Result:ok(self.okValue)
end
Result["and"] = function(self, other)
return self:isErr() and Result:err(self.errValue) or other
end
function Result:andThen(func)
return self:isErr() and Result:err(self.errValue) or func(self.okValue)
end
Result["or"] = function(self, other)
return self:isOk() and Result:ok(self.okValue) or other
end
function Result:orElse(other)
return self:isOk() and Result:ok(self.okValue) or other(self.errValue)
end
function Result:expect(msg)
if self:isOk() then
return self.okValue
else
error(msg)
end
end
function Result:unwrap()
return self:expect("called `Result.unwrap()` on an `Err` value: " .. tostring(self.errValue))
end
function Result:unwrapOr(def)
local _0
if self:isOk() then
_0 = self.okValue
else
_0 = def
end
return _0
end
function Result:unwrapOrElse(gen)
local _0
if self:isOk() then
_0 = self.okValue
else
_0 = gen(self.errValue)
end
return _0
end
function Result:expectErr(msg)
if self:isErr() then
return self.errValue
else
error(msg)
end
end
function Result:unwrapErr()
return self:expectErr("called `Result.unwrapErr()` on an `Ok` value: " .. tostring(self.okValue))
end
function Result:transpose()
return self:isOk() and self.okValue:map(function(some)
return Result:ok(some)
end) or Option:some(Result:err(self.errValue))
end
function Result:flatten()
return self:isOk() and Result.new(self.okValue.okValue, self.okValue.errValue) or Result:err(self.errValue)
end
function Result:match(ifOk, ifErr)
local _0
if self:isOk() then
_0 = ifOk(self.okValue)
else
_0 = ifErr(self.errValue)
end
return _0
end
function Result:asPtr()
local _0 = (self.okValue)
if _0 == nil then
_0 = (self.errValue)
end
return _0
end
end
local resultMeta = Result
resultMeta.__eq = function(a, b)
return b:match(function(ok)
return a:contains(ok)
end, function(err)
return a:containsErr(err)
end)
end
resultMeta.__tostring = function(result)
return result:match(function(ok)
return "Result.ok(" .. tostring(ok) .. ")"
end, function(err)
return "Result.err(" .. tostring(err) .. ")"
end)
end
return {
Result = Result,
}
)LUA";
if (FFlag::LuauLowerBoundsCalculation)
(void)check(src);
else
CHECK_THROWS_AS(check(src), std::exception);
}
TEST_SUITE_END();

View File

@ -7,8 +7,6 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauFixIncorrectLineNumberDuplicateType)
TEST_SUITE_BEGIN("TypeAliases"); TEST_SUITE_BEGIN("TypeAliases");
TEST_CASE_FIXTURE(Fixture, "cyclic_function_type_in_type_alias") TEST_CASE_FIXTURE(Fixture, "cyclic_function_type_in_type_alias")
@ -257,11 +255,7 @@ TEST_CASE_FIXTURE(Fixture, "reported_location_is_correct_when_type_alias_are_dup
auto dtd = get<DuplicateTypeDefinition>(result.errors[0]); auto dtd = get<DuplicateTypeDefinition>(result.errors[0]);
REQUIRE(dtd); REQUIRE(dtd);
CHECK_EQ(dtd->name, "B"); CHECK_EQ(dtd->name, "B");
CHECK_EQ(dtd->previousLocation.begin.line + 1, 3);
if (FFlag::LuauFixIncorrectLineNumberDuplicateType)
CHECK_EQ(dtd->previousLocation.begin.line + 1, 3);
else
CHECK_EQ(dtd->previousLocation.begin.line + 1, 1);
} }
TEST_CASE_FIXTURE(Fixture, "stringify_optional_parameterized_alias") TEST_CASE_FIXTURE(Fixture, "stringify_optional_parameterized_alias")
@ -495,8 +489,6 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_ok")
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1")
{ {
ScopedFastFlag sff{"LuauRecursiveTypeParameterRestriction", true};
CheckResult result = check(R"( CheckResult result = check(R"(
-- OK because forwarded types are used with their parameters. -- OK because forwarded types are used with their parameters.
type Tree<T> = { data: T, children: Forest<T> } type Tree<T> = { data: T, children: Forest<T> }
@ -508,8 +500,6 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1")
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_2") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_2")
{ {
ScopedFastFlag sff{"LuauRecursiveTypeParameterRestriction", true};
CheckResult result = check(R"( CheckResult result = check(R"(
-- Not OK because forwarded types are used with different types than their parameters. -- Not OK because forwarded types are used with different types than their parameters.
type Forest<T> = {Tree<{T}>} type Forest<T> = {Tree<{T}>}
@ -531,8 +521,6 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_ok")
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_not_ok") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_not_ok")
{ {
ScopedFastFlag sff{"LuauRecursiveTypeParameterRestriction", true};
CheckResult result = check(R"( CheckResult result = check(R"(
type Tree1<T,U> = { data: T, children: {Tree2<U,T>} } type Tree1<T,U> = { data: T, children: {Tree2<U,T>} }
type Tree2<T,U> = { data: U, children: {Tree1<T,U>} } type Tree2<T,U> = { data: U, children: {Tree1<T,U>} }
@ -647,9 +635,6 @@ TEST_CASE_FIXTURE(Fixture, "forward_declared_alias_is_not_clobbered_by_prior_uni
{ {
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{"LuauTwoPassAliasDefinitionFix", true}, {"LuauTwoPassAliasDefinitionFix", true},
// We also force this flag because it surfaced an unfortunate interaction.
{"LuauErrorRecoveryType", true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
@ -687,8 +672,6 @@ TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_ok")
TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok") TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok")
{ {
ScopedFastFlag sff{"LuauRecursiveTypeParameterRestriction", true};
CheckResult result = check(R"( CheckResult result = check(R"(
-- this would be an infinite type if we allowed it -- this would be an infinite type if we allowed it
type Tree<T> = { data: T, children: {Tree<{T}>} } type Tree<T> = { data: T, children: {Tree<{T}>} }

View File

@ -221,8 +221,6 @@ TEST_CASE_FIXTURE(Fixture, "as_expr_is_bidirectional")
TEST_CASE_FIXTURE(Fixture, "as_expr_warns_on_unrelated_cast") TEST_CASE_FIXTURE(Fixture, "as_expr_warns_on_unrelated_cast")
{ {
ScopedFastFlag sff2{"LuauErrorRecoveryType", true};
CheckResult result = check(R"( CheckResult result = check(R"(
local a = 55 :: string local a = 55 :: string
)"); )");
@ -407,8 +405,6 @@ TEST_CASE_FIXTURE(Fixture, "typeof_expr")
TEST_CASE_FIXTURE(Fixture, "corecursive_types_error_on_tight_loop") TEST_CASE_FIXTURE(Fixture, "corecursive_types_error_on_tight_loop")
{ {
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"( CheckResult result = check(R"(
type A = B type A = B
type B = A type B = A

View File

@ -951,8 +951,6 @@ TEST_CASE_FIXTURE(Fixture, "record_matching_overload")
TEST_CASE_FIXTURE(Fixture, "return_type_by_overload") TEST_CASE_FIXTURE(Fixture, "return_type_by_overload")
{ {
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"( CheckResult result = check(R"(
type Overload = ((string) -> string) & ((number, number) -> number) type Overload = ((string) -> string) & ((number, number) -> number)
local abc: Overload local abc: Overload
@ -1538,7 +1536,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic") TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")
{ {
ScopedFastFlag sff{"LuauArgCountMismatchSaysAtLeastWhenVariadic", true};
CheckResult result = check(R"( CheckResult result = check(R"(
function test(a: number, b: string, ...) function test(a: number, b: string, ...)
end end
@ -1560,8 +1557,6 @@ TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic_generic") TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic_generic")
{ {
ScopedFastFlag sff1{"LuauArgCountMismatchSaysAtLeastWhenVariadic", true};
ScopedFastFlag sff2{"LuauFixArgumentCountMismatchAmountWithGenericTypes", true};
CheckResult result = check(R"( CheckResult result = check(R"(
function test(a: number, b: string, ...) function test(a: number, b: string, ...)
return 1 return 1
@ -1587,8 +1582,6 @@ wrapper(test)
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic_generic2") TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic_generic2")
{ {
ScopedFastFlag sff1{"LuauArgCountMismatchSaysAtLeastWhenVariadic", true};
ScopedFastFlag sff2{"LuauFixArgumentCountMismatchAmountWithGenericTypes", true};
CheckResult result = check(R"( CheckResult result = check(R"(
function test(a: number, b: string, ...) function test(a: number, b: string, ...)
return 1 return 1

View File

@ -11,8 +11,6 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauFixArgumentCountMismatchAmountWithGenericTypes)
TEST_SUITE_BEGIN("GenericsTests"); TEST_SUITE_BEGIN("GenericsTests");
TEST_CASE_FIXTURE(Fixture, "check_generic_function") TEST_CASE_FIXTURE(Fixture, "check_generic_function")
@ -679,8 +677,6 @@ local d: D = c
TEST_CASE_FIXTURE(Fixture, "generic_functions_dont_cache_type_parameters") TEST_CASE_FIXTURE(Fixture, "generic_functions_dont_cache_type_parameters")
{ {
ScopedFastFlag sff{"LuauGenericFunctionsDontCacheTypeParams", true};
CheckResult result = check(R"( CheckResult result = check(R"(
-- See https://github.com/Roblox/luau/issues/332 -- See https://github.com/Roblox/luau/issues/332
-- This function has a type parameter with the same name as clones, -- This function has a type parameter with the same name as clones,
@ -707,8 +703,6 @@ TEST_CASE_FIXTURE(Fixture, "generic_functions_should_be_memory_safe")
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{"LuauTableSubtypingVariance2", true}, {"LuauTableSubtypingVariance2", true},
{"LuauUnsealedTableLiteral", true}, {"LuauUnsealedTableLiteral", true},
{"LuauPropertiesGetExpectedType", true},
{"LuauRecursiveTypeParameterRestriction", true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
@ -733,8 +727,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification1") TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification1")
{ {
ScopedFastFlag sff{"LuauTxnLogSeesTypePacks2", true};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
type Dispatcher = { type Dispatcher = {
@ -753,8 +745,6 @@ local TheDispatcher: Dispatcher = {
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification2") TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification2")
{ {
ScopedFastFlag sff{"LuauTxnLogSeesTypePacks2", true};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
type Dispatcher = { type Dispatcher = {
@ -773,8 +763,6 @@ local TheDispatcher: Dispatcher = {
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification3") TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification3")
{ {
ScopedFastFlag sff{"LuauTxnLogSeesTypePacks2", true};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
type Dispatcher = { type Dispatcher = {
@ -805,11 +793,7 @@ wrapper(test)
)"); )");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 2 arguments, but only 1 is specified)");
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes)
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 2 arguments, but only 1 is specified)");
else
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 1 argument, but 1 is specified)");
} }
TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_many") TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_many")
@ -826,11 +810,7 @@ wrapper(test2, 1, "", 3)
)"); )");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 3 arguments, but 4 are specified)");
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes)
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 3 arguments, but 4 are specified)");
else
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 1 argument, but 4 are specified)");
} }
TEST_CASE_FIXTURE(Fixture, "generic_function") TEST_CASE_FIXTURE(Fixture, "generic_function")

View File

@ -78,6 +78,8 @@ TEST_CASE_FIXTURE(Fixture, "for_in_with_an_iterator_of_type_any")
TEST_CASE_FIXTURE(Fixture, "for_in_loop_should_fail_with_non_function_iterator") TEST_CASE_FIXTURE(Fixture, "for_in_loop_should_fail_with_non_function_iterator")
{ {
ScopedFastFlag luauDoNotRelyOnNextBinding{"LuauDoNotRelyOnNextBinding", true};
CheckResult result = check(R"( CheckResult result = check(R"(
local foo = "bar" local foo = "bar"
for i, v in foo do for i, v in foo do
@ -85,6 +87,7 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_should_fail_with_non_function_iterator")
)"); )");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("Cannot call non-function string", toString(result.errors[0]));
} }
TEST_CASE_FIXTURE(Fixture, "for_in_with_just_one_iterator_is_ok") TEST_CASE_FIXTURE(Fixture, "for_in_with_just_one_iterator_is_ok")
@ -470,4 +473,19 @@ TEST_CASE_FIXTURE(Fixture, "loop_typecheck_crash_on_empty_optional")
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
} }
TEST_CASE_FIXTURE(Fixture, "fuzz_fail_missing_instantitation_follow")
{
ScopedFastFlag luauInstantiateFollows{"LuauInstantiateFollows", true};
// Just check that this doesn't assert
check(R"(
--!nonstrict
function _(l0:number)
return _
end
for _ in _(8) do
end
)");
}
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -142,8 +142,6 @@ TEST_CASE_FIXTURE(Fixture, "some_primitive_binary_ops")
TEST_CASE_FIXTURE(Fixture, "typecheck_overloaded_multiply_that_is_an_intersection") TEST_CASE_FIXTURE(Fixture, "typecheck_overloaded_multiply_that_is_an_intersection")
{ {
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
local Vec3 = {} local Vec3 = {}
@ -178,8 +176,6 @@ TEST_CASE_FIXTURE(Fixture, "typecheck_overloaded_multiply_that_is_an_intersectio
TEST_CASE_FIXTURE(Fixture, "typecheck_overloaded_multiply_that_is_an_intersection_on_rhs") TEST_CASE_FIXTURE(Fixture, "typecheck_overloaded_multiply_that_is_an_intersection_on_rhs")
{ {
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
local Vec3 = {} local Vec3 = {}

View File

@ -85,8 +85,6 @@ TEST_CASE_FIXTURE(Fixture, "string_function_other")
TEST_CASE_FIXTURE(Fixture, "CheckMethodsOfNumber") TEST_CASE_FIXTURE(Fixture, "CheckMethodsOfNumber")
{ {
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"( CheckResult result = check(R"(
local x: number = 9999 local x: number = 9999
function x:y(z: number) function x:y(z: number)

View File

@ -268,242 +268,6 @@ TEST_CASE_FIXTURE(Fixture, "bail_early_if_unification_is_too_complicated" * doct
} }
} }
TEST_CASE_FIXTURE(Fixture, "bail_early_on_typescript_port_of_Result_type" * doctest::timeout(1.0))
{
ScopedFastInt sffi{"LuauTarjanChildLimit", 400};
CheckResult result = check(R"LUA(
--!strict
local TS = _G[script]
local lazyGet = TS.import(script, script.Parent.Parent, "util", "lazyLoad").lazyGet
local unit = TS.import(script, script.Parent.Parent, "util", "Unit").unit
local Iterator
lazyGet("Iterator", function(c)
Iterator = c
end)
local Option
lazyGet("Option", function(c)
Option = c
end)
local Vec
lazyGet("Vec", function(c)
Vec = c
end)
local Result
do
Result = setmetatable({}, {
__tostring = function()
return "Result"
end,
})
Result.__index = Result
function Result.new(...)
local self = setmetatable({}, Result)
self:constructor(...)
return self
end
function Result:constructor(okValue, errValue)
self.okValue = okValue
self.errValue = errValue
end
function Result:ok(val)
return Result.new(val, nil)
end
function Result:err(val)
return Result.new(nil, val)
end
function Result:fromCallback(c)
local _0 = c
local _1, _2 = pcall(_0)
local result = _1 and {
success = true,
value = _2,
} or {
success = false,
error = _2,
}
return result.success and Result:ok(result.value) or Result:err(Option:wrap(result.error))
end
function Result:fromVoidCallback(c)
local _0 = c
local _1, _2 = pcall(_0)
local result = _1 and {
success = true,
value = _2,
} or {
success = false,
error = _2,
}
return result.success and Result:ok(unit()) or Result:err(Option:wrap(result.error))
end
Result.fromPromise = TS.async(function(self, p)
local _0, _1 = TS.try(function()
return TS.TRY_RETURN, { Result:ok(TS.await(p)) }
end, function(e)
return TS.TRY_RETURN, { Result:err(Option:wrap(e)) }
end)
if _0 then
return unpack(_1)
end
end)
Result.fromVoidPromise = TS.async(function(self, p)
local _0, _1 = TS.try(function()
TS.await(p)
return TS.TRY_RETURN, { Result:ok(unit()) }
end, function(e)
return TS.TRY_RETURN, { Result:err(Option:wrap(e)) }
end)
if _0 then
return unpack(_1)
end
end)
function Result:isOk()
return self.okValue ~= nil
end
function Result:isErr()
return self.errValue ~= nil
end
function Result:contains(x)
return self.okValue == x
end
function Result:containsErr(x)
return self.errValue == x
end
function Result:okOption()
return Option:wrap(self.okValue)
end
function Result:errOption()
return Option:wrap(self.errValue)
end
function Result:map(func)
return self:isOk() and Result:ok(func(self.okValue)) or Result:err(self.errValue)
end
function Result:mapOr(def, func)
local _0
if self:isOk() then
_0 = func(self.okValue)
else
_0 = def
end
return _0
end
function Result:mapOrElse(def, func)
local _0
if self:isOk() then
_0 = func(self.okValue)
else
_0 = def(self.errValue)
end
return _0
end
function Result:mapErr(func)
return self:isErr() and Result:err(func(self.errValue)) or Result:ok(self.okValue)
end
Result["and"] = function(self, other)
return self:isErr() and Result:err(self.errValue) or other
end
function Result:andThen(func)
return self:isErr() and Result:err(self.errValue) or func(self.okValue)
end
Result["or"] = function(self, other)
return self:isOk() and Result:ok(self.okValue) or other
end
function Result:orElse(other)
return self:isOk() and Result:ok(self.okValue) or other(self.errValue)
end
function Result:expect(msg)
if self:isOk() then
return self.okValue
else
error(msg)
end
end
function Result:unwrap()
return self:expect("called `Result.unwrap()` on an `Err` value: " .. tostring(self.errValue))
end
function Result:unwrapOr(def)
local _0
if self:isOk() then
_0 = self.okValue
else
_0 = def
end
return _0
end
function Result:unwrapOrElse(gen)
local _0
if self:isOk() then
_0 = self.okValue
else
_0 = gen(self.errValue)
end
return _0
end
function Result:expectErr(msg)
if self:isErr() then
return self.errValue
else
error(msg)
end
end
function Result:unwrapErr()
return self:expectErr("called `Result.unwrapErr()` on an `Ok` value: " .. tostring(self.okValue))
end
function Result:transpose()
return self:isOk() and self.okValue:map(function(some)
return Result:ok(some)
end) or Option:some(Result:err(self.errValue))
end
function Result:flatten()
return self:isOk() and Result.new(self.okValue.okValue, self.okValue.errValue) or Result:err(self.errValue)
end
function Result:match(ifOk, ifErr)
local _0
if self:isOk() then
_0 = ifOk(self.okValue)
else
_0 = ifErr(self.errValue)
end
return _0
end
function Result:asPtr()
local _0 = (self.okValue)
if _0 == nil then
_0 = (self.errValue)
end
return _0
end
end
local resultMeta = Result
resultMeta.__eq = function(a, b)
return b:match(function(ok)
return a:contains(ok)
end, function(err)
return a:containsErr(err)
end)
end
resultMeta.__tostring = function(result)
return result:match(function(ok)
return "Result.ok(" .. tostring(ok) .. ")"
end, function(err)
return "Result.err(" .. tostring(err) .. ")"
end)
end
return {
Result = Result,
}
)LUA");
auto it = std::find_if(result.errors.begin(), result.errors.end(), [](TypeError& a) {
return nullptr != get<UnificationTooComplex>(a);
});
if (it == result.errors.end())
{
dumpErrors(result);
FAIL("Expected a UnificationTooComplex error");
}
}
// Should be in TypeInfer.tables.test.cpp // Should be in TypeInfer.tables.test.cpp
// It's unsound to instantiate tables containing generic methods, // It's unsound to instantiate tables containing generic methods,
// since mutating properties means table properties should be invariant. // since mutating properties means table properties should be invariant.

View File

@ -164,10 +164,6 @@ TEST_CASE_FIXTURE(Fixture, "enums_using_singletons_subtyping")
TEST_CASE_FIXTURE(Fixture, "tagged_unions_using_singletons") TEST_CASE_FIXTURE(Fixture, "tagged_unions_using_singletons")
{ {
ScopedFastFlag sffs[] = {
{"LuauExpectedTypesOfProperties", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
type Dog = { tag: "Dog", howls: boolean } type Dog = { tag: "Dog", howls: boolean }
type Cat = { tag: "Cat", meows: boolean } type Cat = { tag: "Cat", meows: boolean }
@ -281,10 +277,6 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_string") TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_string")
{ {
ScopedFastFlag sffs[] = {
{"LuauExpectedTypesOfProperties", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
type Cat = { tag: 'cat', catfood: string } type Cat = { tag: 'cat', catfood: string }
type Dog = { tag: 'dog', dogfood: string } type Dog = { tag: 'dog', dogfood: string }
@ -302,10 +294,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_bool") TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_bool")
{ {
ScopedFastFlag sffs[] = {
{"LuauExpectedTypesOfProperties", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
type Good = { success: true, result: string } type Good = { success: true, result: string }
type Bad = { success: false, error: string } type Bad = { success: false, error: string }
@ -323,10 +311,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "if_then_else_expression_singleton_options") TEST_CASE_FIXTURE(Fixture, "if_then_else_expression_singleton_options")
{ {
ScopedFastFlag sffs[] = {
{"LuauExpectedTypesOfProperties", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
type Cat = { tag: 'cat', catfood: string } type Cat = { tag: 'cat', catfood: string }
type Dog = { tag: 'dog', dogfood: string } type Dog = { tag: 'dog', dogfood: string }

View File

@ -2122,8 +2122,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table") TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table")
{ {
ScopedFastFlag sffs[]{ ScopedFastFlag sffs[]{
{"LuauPropertiesGetExpectedType", true},
{"LuauExpectedTypesOfProperties", true},
{"LuauTableSubtypingVariance2", true}, {"LuauTableSubtypingVariance2", true},
}; };
@ -2143,8 +2141,6 @@ a.p = { x = 9 }
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_error") TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_error")
{ {
ScopedFastFlag sffs[]{ ScopedFastFlag sffs[]{
{"LuauPropertiesGetExpectedType", true},
{"LuauExpectedTypesOfProperties", true},
{"LuauTableSubtypingVariance2", true}, {"LuauTableSubtypingVariance2", true},
{"LuauUnsealedTableLiteral", true}, {"LuauUnsealedTableLiteral", true},
}; };
@ -2171,8 +2167,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer") TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
{ {
ScopedFastFlag sffs[]{ ScopedFastFlag sffs[]{
{"LuauPropertiesGetExpectedType", true},
{"LuauExpectedTypesOfProperties", true},
{"LuauTableSubtypingVariance2", true}, {"LuauTableSubtypingVariance2", true},
}; };
@ -2377,8 +2371,6 @@ TEST_CASE_FIXTURE(Fixture, "pass_a_union_of_tables_to_a_function_that_requires_a
TEST_CASE_FIXTURE(Fixture, "unifying_tables_shouldnt_uaf1") TEST_CASE_FIXTURE(Fixture, "unifying_tables_shouldnt_uaf1")
{ {
ScopedFastFlag sff{"LuauTxnLogCheckForInvalidation", true};
CheckResult result = check(R"( CheckResult result = check(R"(
-- This example produced a UAF at one point, caused by pointers to table types becoming -- This example produced a UAF at one point, caused by pointers to table types becoming
-- invalidated by child unifiers. (Calling log.concat can cause pointers to become invalid.) -- invalidated by child unifiers. (Calling log.concat can cause pointers to become invalid.)
@ -2409,8 +2401,6 @@ end
TEST_CASE_FIXTURE(Fixture, "unifying_tables_shouldnt_uaf2") TEST_CASE_FIXTURE(Fixture, "unifying_tables_shouldnt_uaf2")
{ {
ScopedFastFlag sff{"LuauTxnLogCheckForInvalidation", true};
CheckResult result = check(R"( CheckResult result = check(R"(
-- Another example that UAFd, this time found by fuzzing. -- Another example that UAFd, this time found by fuzzing.
local _ local _

View File

@ -126,8 +126,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "members_of_failed_typepack_unification_are_u
TEST_CASE_FIXTURE(TryUnifyFixture, "result_of_failed_typepack_unification_is_constrained") TEST_CASE_FIXTURE(TryUnifyFixture, "result_of_failed_typepack_unification_is_constrained")
{ {
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"( CheckResult result = check(R"(
function f(arg: number) return arg end function f(arg: number) return arg end
local a local a

View File

@ -184,8 +184,6 @@ TEST_CASE_FIXTURE(Fixture, "UnionTypeVarIterator_with_empty_union")
TEST_CASE_FIXTURE(Fixture, "substitution_skip_failure") TEST_CASE_FIXTURE(Fixture, "substitution_skip_failure")
{ {
ScopedFastFlag sff{"LuauSealExports", true};
TypeVar ftv11{FreeTypeVar{TypeLevel{}}}; TypeVar ftv11{FreeTypeVar{TypeLevel{}}};
TypePackVar tp24{TypePack{{&ftv11}}}; TypePackVar tp24{TypePack{{&ftv11}}};