Sync to upstream/release/525 (#467)

This commit is contained in:
Arseny Kapoulkine 2022-04-28 18:24:24 -07:00 committed by GitHub
parent 74c84815a0
commit bd6d44f5e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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(LuauAutocompleteDynamicLimits, false)
LUAU_FASTFLAGVARIABLE(LuauDirtySourceModule, false)
LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 0)
LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 100)
namespace Luau
{

View File

@ -2653,12 +2653,12 @@ static void lintComments(LintContext& context, const std::vector<HotComment>& ho
}
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);
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)
{
@ -2827,7 +2827,7 @@ uint64_t LintWarning::parseMask(const std::vector<HotComment>& hotcomments)
if (hc.content.compare(0, 6, "nolint") != 0)
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
if (name == std::string::npos)

View File

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

View File

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

View File

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

View File

@ -23,10 +23,7 @@ LUAU_FASTFLAG(LuauErrorRecoveryType);
LUAU_FASTFLAGVARIABLE(LuauSubtypingAddOptPropsToUnsealedTables, false)
LUAU_FASTFLAGVARIABLE(LuauWidenIfSupertypeIsFree2, false)
LUAU_FASTFLAGVARIABLE(LuauDifferentOrderOfUnificationDoesntMatter, false)
LUAU_FASTFLAGVARIABLE(LuauTxnLogSeesTypePacks2, false)
LUAU_FASTFLAGVARIABLE(LuauTxnLogCheckForInvalidation, false)
LUAU_FASTFLAGVARIABLE(LuauTxnLogRefreshFunctionPointers, false)
LUAU_FASTFLAGVARIABLE(LuauTxnLogDontRetryForIndexers, false)
LUAU_FASTFLAG(LuauAnyInIsOptionalIsOptional)
LUAU_FASTFLAG(LuauTypecheckOptPass)
@ -1021,7 +1018,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
if (superTp == subTp)
return;
if (FFlag::LuauTxnLogSeesTypePacks2 && log.haveSeen(superTp, subTp))
if (log.haveSeen(superTp, subTp))
return;
if (log.getMutable<Unifiable::Free>(superTp))
@ -1265,13 +1262,10 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
log.pushSeen(superFunction->generics[i], subFunction->generics[i]);
}
if (FFlag::LuauTxnLogSeesTypePacks2)
{
for (size_t i = 0; i < numGenericPacks; i++)
{
log.pushSeen(superFunction->genericPacks[i], subFunction->genericPacks[i]);
}
}
CountMismatch::Context context = ctx;
@ -1330,13 +1324,10 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
ctx = context;
if (FFlag::LuauTxnLogSeesTypePacks2)
{
for (int i = int(numGenericPacks) - 1; 0 <= i; i--)
{
log.popSeen(superFunction->genericPacks[i], subFunction->genericPacks[i]);
}
}
for (int i = int(numGenerics) - 1; 0 <= i; i--)
{
@ -1499,8 +1490,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
else
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.
@ -1514,7 +1503,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
return;
}
}
}
for (const auto& [name, prop] : subTable->props)
{
@ -1570,8 +1558,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
else
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.
@ -1585,7 +1571,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
return;
}
}
}
// Unify indexers
if (superTable->indexer && subTable->indexer)
@ -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);
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())
{

View File

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

View File

@ -240,7 +240,7 @@ std::optional<std::string> getParentPath(const std::string& path)
return std::nullopt;
#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)
return "/";
@ -253,7 +253,7 @@ std::optional<std::string> getParentPath(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] != '.')
return "";

View File

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

View File

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

View File

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

View File

@ -2206,9 +2206,15 @@ struct Compiler
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;
if (tripCount > thresholdBase * thresholdMaxBoost / 100)
if (tripCount > thresholdBase)
{
bytecode.addDebugRemark("loop unroll failed: too many iterations (%d)", tripCount);
return false;

View File

@ -249,7 +249,7 @@ int computeCost(uint64_t model, const bool* varsConst, size_t varCount)
return cost;
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;
}

View File

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

View File

@ -1323,7 +1323,7 @@ void lua_unref(lua_State* L, int ref)
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);
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 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;

View File

@ -33,7 +33,6 @@
#include <string.h>
LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false)
LUAU_FASTFLAGVARIABLE(LuauTableNewBoundary2, false)
// max size of both array and hash part is 2^MAXBITS
@ -399,18 +398,11 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
for (int i = nasize; i < oldasize; i++)
{
if (!ttisnil(&t->array[i]))
{
if (FFlag::LuauTableRehashRework)
{
TValue ok;
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 */
luaM_reallocarray(L, t->array, oldasize, nasize, TValue, t->memcat);
@ -418,8 +410,6 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
/* used for the migration check at the end */
TValue* anew = t->array;
/* re-insert elements from hash part */
if (FFlag::LuauTableRehashRework)
{
for (int i = twoto(oldhsize) - 1; i >= 0; i--)
{
LuaNode* old = nold + i;
@ -430,20 +420,6 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
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));
}
}
}
/* make sure we haven't recursively rehashed during element migration */
LUAU_ASSERT(nnew == t->node);
@ -559,7 +535,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
{
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);
}
@ -571,16 +547,9 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
{ /* cannot find a free place? */
rehash(L, t, key); /* grow table */
if (!FFlag::LuauTableRehashRework)
{
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
/* 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);
TValue mk;
getnodekey(L, &mk, mp);

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 (*dtor)(void*) = nullptr;
if (u->tag < LUA_UTAG_LIMIT)
{
void (*dtor)(lua_State*, void*) = nullptr;
dtor = L->global->udatagc[u->tag];
if (dtor)
dtor(L, u->data);
}
else if (u->tag == UTAG_IDTOR)
{
void (*dtor)(void*) = nullptr;
memcpy(&dtor, &u->data + u->len - sizeof(dtor), sizeof(dtor));
if (dtor)
dtor(u->data);
}
luaM_freegco(L, u, sizeudata(u->len), u->memcat, page);
}

View File

@ -137,6 +137,21 @@ int registerTypes(Luau::TypeChecker& env)
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
{
std::optional<Luau::SourceCode> readSource(const Luau::ModuleName& name) override
@ -238,19 +253,11 @@ DEFINE_PROTO_FUZZER(const luau::ModuleSet& message)
if (kFuzzTypeck)
{
static FuzzFileResolver fileResolver;
static Luau::NullConfigResolver configResolver;
static FuzzConfigResolver configResolver;
static Luau::FrontendOptions options{true, true};
static Luau::Frontend frontend(&fileResolver, &configResolver, options);
static int once = registerTypes(frontend.typeChecker);
(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");
};
static int once = (setupFrontend(frontend), 0);
// restart
frontend.clear();

View File

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

View File

@ -2698,16 +2698,22 @@ TEST_CASE("DebugRemarks")
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.endFunction(0, 0);
bcb.endFunction(1, 0);
bcb.setMainFunction(fid);
bcb.finalize();
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
)");
}
@ -4332,7 +4338,7 @@ RETURN R0 1
// loops with body that's long but has a high boost factor due to constant folding
CHECK_EQ("\n" + compileFunction(R"(
local t = {}
for i=1,30 do
for i=1,25 do
t[i] = i * i * i
end
return t
@ -4390,16 +4396,6 @@ LOADN R1 13824
SETTABLEN R1 R0 24
LOADN R1 15625
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
)");
@ -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();

View File

@ -1056,7 +1056,7 @@ TEST_CASE("UserdataApi")
lua_State* L = globalState.get();
// 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;
});

View File

@ -975,8 +975,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "typecheck_twice_for_ast_types")
TEST_CASE_FIXTURE(FrontendFixture, "imported_table_modification_2")
{
ScopedFastFlag sffs("LuauSealExports", true);
frontend.options.retainFullTypeGraphs = false;
fileResolver.source["Module/A"] = R"(
@ -1035,4 +1033,20 @@ return false;
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();

View File

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

View File

@ -283,7 +283,6 @@ TEST_CASE_FIXTURE(Fixture, "inconsistent_module_return_types_are_ok")
ScopedFastFlag sff[]{
{"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true},
{"LuauSealExports", true},
};
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")
{
ScopedFastFlag luauParseLocationIgnoreCommentSkip{"LuauParseLocationIgnoreCommentSkip", true};
AstStatBlock* block = parse(R"(
type F = number
--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")
{
ScopedFastFlag luauParseLocationIgnoreCommentSkip{"LuauParseLocationIgnoreCommentSkip", true};
ScopedFastFlag luauParseLocationIgnoreCommentSkipInCapture{"LuauParseLocationIgnoreCommentSkipInCapture", true};
// 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) || defined(_DEBUG)
ScopedFastInt LuauTypeInferRecursionLimit{"LuauTypeInferRecursionLimit", 100};
#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;
LUAU_FASTFLAG(LuauFixIncorrectLineNumberDuplicateType)
TEST_SUITE_BEGIN("TypeAliases");
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]);
REQUIRE(dtd);
CHECK_EQ(dtd->name, "B");
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")
@ -495,8 +489,6 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_ok")
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1")
{
ScopedFastFlag sff{"LuauRecursiveTypeParameterRestriction", true};
CheckResult result = check(R"(
-- OK because forwarded types are used with their parameters.
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")
{
ScopedFastFlag sff{"LuauRecursiveTypeParameterRestriction", true};
CheckResult result = check(R"(
-- Not OK because forwarded types are used with different types than their parameters.
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")
{
ScopedFastFlag sff{"LuauRecursiveTypeParameterRestriction", true};
CheckResult result = check(R"(
type Tree1<T,U> = { data: T, children: {Tree2<U,T>} }
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[] = {
{"LuauTwoPassAliasDefinitionFix", true},
// We also force this flag because it surfaced an unfortunate interaction.
{"LuauErrorRecoveryType", true},
};
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")
{
ScopedFastFlag sff{"LuauRecursiveTypeParameterRestriction", true};
CheckResult result = check(R"(
-- this would be an infinite type if we allowed it
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")
{
ScopedFastFlag sff2{"LuauErrorRecoveryType", true};
CheckResult result = check(R"(
local a = 55 :: string
)");
@ -407,8 +405,6 @@ TEST_CASE_FIXTURE(Fixture, "typeof_expr")
TEST_CASE_FIXTURE(Fixture, "corecursive_types_error_on_tight_loop")
{
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"(
type A = B
type B = A

View File

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

View File

@ -11,8 +11,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauFixArgumentCountMismatchAmountWithGenericTypes)
TEST_SUITE_BEGIN("GenericsTests");
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")
{
ScopedFastFlag sff{"LuauGenericFunctionsDontCacheTypeParams", true};
CheckResult result = check(R"(
-- See https://github.com/Roblox/luau/issues/332
-- 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[] = {
{"LuauTableSubtypingVariance2", true},
{"LuauUnsealedTableLiteral", true},
{"LuauPropertiesGetExpectedType", true},
{"LuauRecursiveTypeParameterRestriction", true},
};
CheckResult result = check(R"(
@ -733,8 +727,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification1")
{
ScopedFastFlag sff{"LuauTxnLogSeesTypePacks2", true};
CheckResult result = check(R"(
--!strict
type Dispatcher = {
@ -753,8 +745,6 @@ local TheDispatcher: Dispatcher = {
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification2")
{
ScopedFastFlag sff{"LuauTxnLogSeesTypePacks2", true};
CheckResult result = check(R"(
--!strict
type Dispatcher = {
@ -773,8 +763,6 @@ local TheDispatcher: Dispatcher = {
TEST_CASE_FIXTURE(Fixture, "generic_type_pack_unification3")
{
ScopedFastFlag sff{"LuauTxnLogSeesTypePacks2", true};
CheckResult result = check(R"(
--!strict
type Dispatcher = {
@ -805,11 +793,7 @@ wrapper(test)
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
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")
@ -826,11 +810,7 @@ wrapper(test2, 1, "", 3)
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
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")

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")
{
ScopedFastFlag luauDoNotRelyOnNextBinding{"LuauDoNotRelyOnNextBinding", true};
CheckResult result = check(R"(
local foo = "bar"
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);
CHECK_EQ("Cannot call non-function string", toString(result.errors[0]));
}
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);
}
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();

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")
{
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"(
--!strict
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")
{
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"(
--!strict
local Vec3 = {}

View File

@ -85,8 +85,6 @@ TEST_CASE_FIXTURE(Fixture, "string_function_other")
TEST_CASE_FIXTURE(Fixture, "CheckMethodsOfNumber")
{
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"(
local x: number = 9999
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
// It's unsound to instantiate tables containing generic methods,
// 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")
{
ScopedFastFlag sffs[] = {
{"LuauExpectedTypesOfProperties", true},
};
CheckResult result = check(R"(
type Dog = { tag: "Dog", howls: 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")
{
ScopedFastFlag sffs[] = {
{"LuauExpectedTypesOfProperties", true},
};
CheckResult result = check(R"(
type Cat = { tag: 'cat', catfood: string }
type Dog = { tag: 'dog', dogfood: string }
@ -302,10 +294,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_bool")
{
ScopedFastFlag sffs[] = {
{"LuauExpectedTypesOfProperties", true},
};
CheckResult result = check(R"(
type Good = { success: true, result: string }
type Bad = { success: false, error: string }
@ -323,10 +311,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "if_then_else_expression_singleton_options")
{
ScopedFastFlag sffs[] = {
{"LuauExpectedTypesOfProperties", true},
};
CheckResult result = check(R"(
type Cat = { tag: 'cat', catfood: string }
type Dog = { tag: 'dog', dogfood: string }

View File

@ -2122,8 +2122,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table")
{
ScopedFastFlag sffs[]{
{"LuauPropertiesGetExpectedType", true},
{"LuauExpectedTypesOfProperties", true},
{"LuauTableSubtypingVariance2", true},
};
@ -2143,8 +2141,6 @@ a.p = { x = 9 }
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_error")
{
ScopedFastFlag sffs[]{
{"LuauPropertiesGetExpectedType", true},
{"LuauExpectedTypesOfProperties", true},
{"LuauTableSubtypingVariance2", true},
{"LuauUnsealedTableLiteral", true},
};
@ -2171,8 +2167,6 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
{
ScopedFastFlag sffs[]{
{"LuauPropertiesGetExpectedType", true},
{"LuauExpectedTypesOfProperties", 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")
{
ScopedFastFlag sff{"LuauTxnLogCheckForInvalidation", true};
CheckResult result = check(R"(
-- 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.)
@ -2409,8 +2401,6 @@ end
TEST_CASE_FIXTURE(Fixture, "unifying_tables_shouldnt_uaf2")
{
ScopedFastFlag sff{"LuauTxnLogCheckForInvalidation", true};
CheckResult result = check(R"(
-- Another example that UAFd, this time found by fuzzing.
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")
{
ScopedFastFlag sff{"LuauErrorRecoveryType", true};
CheckResult result = check(R"(
function f(arg: number) return arg end
local a

View File

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