mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 06:15:44 +08:00
Sync to upstream/release/639 (#1368)
# What's Changed? - Variety of bugfixes in the new solver ## New Solver - Fix an issue where we would hit a recursion limit when applying long chains of type refinements. - Weaken the types of `table.freeze` and `table.clone` in the new solver so we can accept common code patterns like `local a = table.freeze({x=5, x=0})` at the expense of accepting code like `table.freeze(true)`. - Don't warn when the # operator is used on a value of type never ## VM - Fix a bug in lua_resume where too many values might be removed from stack when resume throws an error --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
This commit is contained in:
parent
1788e237fc
commit
25f91aa8b8
@ -8,8 +8,6 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauDeclarationExtraPropData)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
@ -898,19 +896,11 @@ struct AstJsonEncoder : public AstVisitor
|
||||
{
|
||||
// TODO: attributes
|
||||
PROP(name);
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
PROP(nameLocation);
|
||||
|
||||
PROP(params);
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
{
|
||||
PROP(paramNames);
|
||||
PROP(vararg);
|
||||
PROP(varargLocation);
|
||||
}
|
||||
|
||||
PROP(retTypes);
|
||||
PROP(generics);
|
||||
PROP(genericPacks);
|
||||
@ -926,10 +916,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||
[&]()
|
||||
{
|
||||
PROP(name);
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
PROP(nameLocation);
|
||||
|
||||
PROP(type);
|
||||
}
|
||||
);
|
||||
@ -940,16 +927,10 @@ struct AstJsonEncoder : public AstVisitor
|
||||
writeRaw("{");
|
||||
bool c = pushComma();
|
||||
write("name", prop.name);
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
write("nameLocation", prop.nameLocation);
|
||||
|
||||
writeType("AstDeclaredClassProp");
|
||||
write("luauType", prop.ty);
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
write("location", prop.location);
|
||||
|
||||
popComma(c);
|
||||
writeRaw("}");
|
||||
}
|
||||
|
@ -373,10 +373,26 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
|
||||
attachDcrMagicFunction(getGlobalBinding(globals, "select"), dcrMagicFunctionSelect);
|
||||
|
||||
if (TableType* ttv = getMutable<TableType>(getGlobalBinding(globals, "table")))
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
// CLI-114044 - The new solver does not yet support generic tables,
|
||||
// which act, in an odd way, like generics that are constrained to
|
||||
// the top table type. We do the best we can by modelling these
|
||||
// functions using unconstrained generics. It's not quite right,
|
||||
// but it'll be ok for now.
|
||||
TypeId genericTy = arena.addType(GenericType{"T"});
|
||||
TypePackId thePack = arena.addTypePack({genericTy});
|
||||
TypeId idTy = arena.addType(FunctionType{{genericTy}, {}, thePack, thePack});
|
||||
ttv->props["freeze"] = makeProperty(idTy, "@luau/global/table.freeze");
|
||||
ttv->props["clone"] = makeProperty(idTy, "@luau/global/table.clone");
|
||||
}
|
||||
else
|
||||
{
|
||||
// tabTy is a generic table type which we can't express via declaration syntax yet
|
||||
ttv->props["freeze"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.freeze");
|
||||
ttv->props["clone"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.clone");
|
||||
}
|
||||
|
||||
ttv->props["getn"].deprecated = true;
|
||||
ttv->props["getn"].deprecatedSuggestion = "#";
|
||||
|
@ -29,7 +29,6 @@
|
||||
LUAU_FASTINT(LuauCheckRecursionLimit);
|
||||
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
||||
LUAU_FASTFLAG(LuauDeclarationExtraPropData);
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -550,6 +549,13 @@ bool mustDeferIntersection(TypeId ty)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
enum RefinementsOpKind
|
||||
{
|
||||
Intersect,
|
||||
Refine,
|
||||
None
|
||||
};
|
||||
|
||||
void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location location, RefinementId refinement)
|
||||
{
|
||||
if (!refinement)
|
||||
@ -558,6 +564,23 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
|
||||
RefinementContext refinements;
|
||||
std::vector<ConstraintV> constraints;
|
||||
computeRefinement(scope, location, refinement, &refinements, /*sense*/ true, /*eq*/ false, &constraints);
|
||||
auto flushConstraints = [this, &scope, &location](RefinementsOpKind kind, TypeId ty, std::vector<TypeId>& discriminants)
|
||||
{
|
||||
if (discriminants.empty())
|
||||
return ty;
|
||||
if (kind == RefinementsOpKind::None)
|
||||
{
|
||||
LUAU_ASSERT(false);
|
||||
return ty;
|
||||
}
|
||||
std::vector<TypeId> args = {ty};
|
||||
const TypeFunction& func = kind == RefinementsOpKind::Intersect ? builtinTypeFunctions().intersectFunc : builtinTypeFunctions().refineFunc;
|
||||
LUAU_ASSERT(!func.name.empty());
|
||||
args.insert(args.end(), discriminants.begin(), discriminants.end());
|
||||
TypeId resultType = createTypeFunctionInstance(func, args, {}, scope, location);
|
||||
discriminants.clear();
|
||||
return resultType;
|
||||
};
|
||||
|
||||
for (auto& [def, partition] : refinements)
|
||||
{
|
||||
@ -566,41 +589,52 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
|
||||
TypeId ty = *defTy;
|
||||
if (partition.shouldAppendNilType)
|
||||
ty = arena->addType(UnionType{{ty, builtinTypes->nilType}});
|
||||
|
||||
// Intersect ty with every discriminant type. If either type is not
|
||||
// sufficiently solved, we queue the intersection up via an
|
||||
// IntersectConstraint.
|
||||
|
||||
// For each discriminant ty, we accumulated it onto ty, creating a longer and longer
|
||||
// sequence of refine constraints. On every loop of this we called mustDeferIntersection.
|
||||
// For sufficiently large types, we would blow the stack.
|
||||
// Instead, we record all the discriminant types in sequence
|
||||
// and then dispatch a single refine constraint with multiple arguments. This helps us avoid
|
||||
// the potentially expensive check on mustDeferIntersection
|
||||
std::vector<TypeId> discriminants;
|
||||
RefinementsOpKind kind = RefinementsOpKind::None;
|
||||
bool mustDefer = mustDeferIntersection(ty);
|
||||
for (TypeId dt : partition.discriminantTypes)
|
||||
{
|
||||
if (mustDeferIntersection(ty) || mustDeferIntersection(dt))
|
||||
mustDefer = mustDefer || mustDeferIntersection(dt);
|
||||
if (mustDefer)
|
||||
{
|
||||
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().refineFunc, {ty, dt}, {}, scope, location);
|
||||
if (kind == RefinementsOpKind::Intersect)
|
||||
ty = flushConstraints(kind, ty, discriminants);
|
||||
kind = RefinementsOpKind::Refine;
|
||||
|
||||
ty = resultType;
|
||||
discriminants.push_back(dt);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (shouldSuppressErrors(normalizer, ty))
|
||||
{
|
||||
case ErrorSuppression::DoNotSuppress:
|
||||
{
|
||||
if (!get<NeverType>(follow(ty)))
|
||||
ty = makeIntersect(scope, location, ty, dt);
|
||||
break;
|
||||
}
|
||||
case ErrorSuppression::Suppress:
|
||||
ty = makeIntersect(scope, location, ty, dt);
|
||||
ty = makeUnion(scope, location, ty, builtinTypes->errorType);
|
||||
break;
|
||||
case ErrorSuppression::NormalizationFailed:
|
||||
ErrorSuppression status = shouldSuppressErrors(normalizer, ty);
|
||||
if (status == ErrorSuppression::NormalizationFailed)
|
||||
reportError(location, NormalizationTooComplex{});
|
||||
ty = makeIntersect(scope, location, ty, dt);
|
||||
break;
|
||||
if (kind == RefinementsOpKind::Refine)
|
||||
ty = flushConstraints(kind, ty, discriminants);
|
||||
kind = RefinementsOpKind::Intersect;
|
||||
|
||||
discriminants.push_back(dt);
|
||||
|
||||
if (status == ErrorSuppression::Suppress)
|
||||
{
|
||||
ty = flushConstraints(kind, ty, discriminants);
|
||||
ty = makeUnion(scope, location, ty, builtinTypes->errorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize - if there are any discriminants left, make one big constraint for refining them
|
||||
if (kind != RefinementsOpKind::None)
|
||||
ty = flushConstraints(kind, ty, discriminants);
|
||||
|
||||
scope->rvalueRefinements[def] = ty;
|
||||
}
|
||||
}
|
||||
@ -1532,8 +1566,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||
|
||||
ftv->hasSelf = true;
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
{
|
||||
FunctionDefinition defn;
|
||||
|
||||
defn.definitionModuleName = module->name;
|
||||
@ -1544,18 +1576,14 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||
ftv->definition = defn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TableType::Props& props = assignToMetatable ? metatable->props : ctv->props;
|
||||
|
||||
if (props.count(propName) == 0)
|
||||
{
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
props[propName] = {propTy, /*deprecated*/ false, /*deprecatedSuggestion*/ "", prop.location};
|
||||
else
|
||||
props[propName] = {propTy};
|
||||
}
|
||||
else if (FFlag::LuauDeclarationExtraPropData)
|
||||
else
|
||||
{
|
||||
Luau::Property& prop = props[propName];
|
||||
TypeId currentTy = prop.type();
|
||||
@ -1583,31 +1611,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||
reportError(declaredClass->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeId currentTy = props[propName].type();
|
||||
|
||||
// We special-case this logic to keep the intersection flat; otherwise we
|
||||
// would create a ton of nested intersection types.
|
||||
if (const IntersectionType* itv = get<IntersectionType>(currentTy))
|
||||
{
|
||||
std::vector<TypeId> options = itv->parts;
|
||||
options.push_back(propTy);
|
||||
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
|
||||
|
||||
props[propName] = {newItv};
|
||||
}
|
||||
else if (get<FunctionType>(currentTy))
|
||||
{
|
||||
TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}});
|
||||
|
||||
props[propName] = {intersection};
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(declaredClass->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ControlFlow::None;
|
||||
@ -1641,13 +1644,10 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc
|
||||
|
||||
FunctionDefinition defn;
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
{
|
||||
defn.definitionModuleName = module->name;
|
||||
defn.definitionLocation = global->location;
|
||||
defn.varargLocation = global->vararg ? std::make_optional(global->varargLocation) : std::nullopt;
|
||||
defn.originalNameLocation = global->nameLocation;
|
||||
}
|
||||
|
||||
TypeId fnType = arena->addType(FunctionType{TypeLevel{}, funScope.get(), std::move(genericTys), std::move(genericTps), paramPack, retPack, defn});
|
||||
FunctionType* ftv = getMutable<FunctionType>(fnType);
|
||||
@ -1989,7 +1989,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExpr* expr, std::
|
||||
Inference result;
|
||||
|
||||
if (auto group = expr->as<AstExprGroup>())
|
||||
result = check(scope, group->expr, expectedType, forceSingleton);
|
||||
result = check(scope, group->expr, expectedType, forceSingleton, generalize);
|
||||
else if (auto stringExpr = expr->as<AstExprConstantString>())
|
||||
result = check(scope, stringExpr, expectedType, forceSingleton);
|
||||
else if (expr->is<AstExprConstantNumber>())
|
||||
@ -2188,6 +2188,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprIndexExpr* in
|
||||
{
|
||||
if (auto constantString = indexExpr->index->as<AstExprConstantString>())
|
||||
{
|
||||
module->astTypes[indexExpr->index] = builtinTypes->stringType;
|
||||
const RefinementKey* key = dfg->getRefinementKey(indexExpr);
|
||||
return checkIndexName(scope, key, indexExpr->expr, constantString->value.data, indexExpr->location);
|
||||
}
|
||||
@ -3005,7 +3006,13 @@ TypeId ConstraintGenerator::resolveType(const ScopePtr& scope, AstType* ty, bool
|
||||
}
|
||||
else if (p.typePack)
|
||||
{
|
||||
packParameters.push_back(resolveTypePack(scope, p.typePack, /* inTypeArguments */ true));
|
||||
TypePackId tp = resolveTypePack(scope, p.typePack, /*inTypeArguments*/ true);
|
||||
|
||||
// If we need more regular types, we can use single element type packs to fill those in
|
||||
if (parameters.size() < alias->typeParams.size() && size(tp) == 1 && finite(tp) && first(tp))
|
||||
parameters.push_back(*first(tp));
|
||||
else
|
||||
packParameters.push_back(tp);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1271,6 +1271,8 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||
|
||||
if (occursCheckPassed && c.callSite)
|
||||
(*c.astOverloadResolvedTypes)[c.callSite] = inferredTy;
|
||||
else if (!occursCheckPassed)
|
||||
reportError(OccursCheckFailed{}, constraint->location);
|
||||
|
||||
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
|
||||
queuer.traverse(overloadToUse);
|
||||
@ -1281,6 +1283,14 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||
return true;
|
||||
}
|
||||
|
||||
static AstExpr* unwrapGroup(AstExpr* expr)
|
||||
{
|
||||
while (auto group = expr->as<AstExprGroup>())
|
||||
expr = group->expr;
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint)
|
||||
{
|
||||
TypeId fn = follow(c.fn);
|
||||
@ -1354,7 +1364,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
||||
{
|
||||
const TypeId expectedArgTy = follow(expectedArgs[i + typeOffset]);
|
||||
const TypeId actualArgTy = follow(argPackHead[i + typeOffset]);
|
||||
const AstExpr* expr = c.callSite->args.data[i];
|
||||
const AstExpr* expr = unwrapGroup(c.callSite->args.data[i]);
|
||||
|
||||
(*c.astExpectedTypes)[expr] = expectedArgTy;
|
||||
|
||||
@ -1697,7 +1707,10 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
||||
{
|
||||
const Property* prop = lookupClassProp(lhsClass, propName);
|
||||
if (!prop || !prop->writeTy.has_value())
|
||||
{
|
||||
bind(constraint, c.propType, builtinTypes->anyType);
|
||||
return true;
|
||||
}
|
||||
|
||||
bind(constraint, c.propType, *prop->writeTy);
|
||||
unify(constraint, rhsType, *prop->writeTy);
|
||||
|
@ -37,7 +37,6 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCancelFromProgress, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreCommentsForDefinitionFiles, false)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false)
|
||||
@ -744,17 +743,10 @@ std::vector<ModuleName> Frontend::checkQueuedModules(
|
||||
}
|
||||
|
||||
if (progress)
|
||||
{
|
||||
if (FFlag::LuauCancelFromProgress)
|
||||
{
|
||||
if (!progress(buildQueueItems.size() - remaining, buildQueueItems.size()))
|
||||
cancelled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
progress(buildQueueItems.size() - remaining, buildQueueItems.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Items cannot be submitted while holding the lock
|
||||
for (size_t i : nextItems)
|
||||
|
@ -11,15 +11,12 @@
|
||||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
|
||||
LUAU_FASTFLAG(LuauReusableSubstitutions)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
void Instantiation::resetState(const TxnLog* log, TypeArena* arena, NotNull<BuiltinTypes> builtinTypes, TypeLevel level, Scope* scope)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauReusableSubstitutions);
|
||||
|
||||
Substitution::resetState(log, arena);
|
||||
|
||||
this->builtinTypes = builtinTypes;
|
||||
@ -71,8 +68,6 @@ TypeId Instantiation::clean(TypeId ty)
|
||||
clone.argNames = ftv->argNames;
|
||||
TypeId result = addType(std::move(clone));
|
||||
|
||||
if (FFlag::LuauReusableSubstitutions)
|
||||
{
|
||||
// Annoyingly, we have to do this even if there are no generics,
|
||||
// to replace any generic tables.
|
||||
reusableReplaceGenerics.resetState(log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks);
|
||||
@ -80,17 +75,6 @@ TypeId Instantiation::clean(TypeId ty)
|
||||
// TODO: What to do if this returns nullopt?
|
||||
// We don't have access to the error-reporting machinery
|
||||
result = reusableReplaceGenerics.substitute(result).value_or(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Annoyingly, we have to do this even if there are no generics,
|
||||
// to replace any generic tables.
|
||||
ReplaceGenerics replaceGenerics{log, arena, builtinTypes, level, scope, ftv->generics, ftv->genericPacks};
|
||||
|
||||
// TODO: What to do if this returns nullopt?
|
||||
// We don't have access to the error-reporting machinery
|
||||
result = replaceGenerics.substitute(result).value_or(result);
|
||||
}
|
||||
|
||||
asMutable(result)->documentationSymbol = ty->documentationSymbol;
|
||||
return result;
|
||||
@ -112,8 +96,6 @@ void ReplaceGenerics::resetState(
|
||||
const std::vector<TypePackId>& genericPacks
|
||||
)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauReusableSubstitutions);
|
||||
|
||||
Substitution::resetState(log, arena);
|
||||
|
||||
this->builtinTypes = builtinTypes;
|
||||
|
@ -231,14 +231,17 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
||||
// is ok.
|
||||
|
||||
const size_t firstUnsatisfiedArgument = args->head.size();
|
||||
const auto [requiredHead, _requiredTail] = flatten(fn->argTypes);
|
||||
const auto [requiredHead, requiredTail] = flatten(fn->argTypes);
|
||||
|
||||
bool isVariadic = requiredTail && Luau::isVariadic(*requiredTail);
|
||||
|
||||
// If too many arguments were supplied, this overload
|
||||
// definitely does not match.
|
||||
if (args->head.size() > requiredHead.size())
|
||||
{
|
||||
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, false}};
|
||||
|
||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
||||
|
||||
return {Analysis::ArityMismatch, {error}};
|
||||
}
|
||||
@ -250,7 +253,7 @@ std::pair<OverloadResolver::Analysis, ErrorVec> OverloadResolver::checkOverload_
|
||||
if (!subtyping.isSubtype(builtinTypes->nilType, requiredHead[i]).isSubtype)
|
||||
{
|
||||
auto [minParams, optMaxParams] = getParameterExtents(TxnLog::empty(), fn->argTypes);
|
||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, false}};
|
||||
TypeError error{fnExpr->location, CountMismatch{minParams, optMaxParams, args->head.size(), CountMismatch::Arg, isVariadic}};
|
||||
|
||||
return {Analysis::ArityMismatch, {error}};
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256);
|
||||
LUAU_FASTFLAG(LuauReusableSubstitutions)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -148,8 +147,8 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
|
||||
}
|
||||
|
||||
Tarjan::Tarjan()
|
||||
: typeToIndex(nullptr, FFlag::LuauReusableSubstitutions ? FInt::LuauTarjanPreallocationSize : 0)
|
||||
, packToIndex(nullptr, FFlag::LuauReusableSubstitutions ? FInt::LuauTarjanPreallocationSize : 0)
|
||||
: typeToIndex(nullptr, FInt::LuauTarjanPreallocationSize)
|
||||
, packToIndex(nullptr, FInt::LuauTarjanPreallocationSize)
|
||||
{
|
||||
nodes.reserve(FInt::LuauTarjanPreallocationSize);
|
||||
stack.reserve(FInt::LuauTarjanPreallocationSize);
|
||||
@ -451,29 +450,18 @@ TarjanResult Tarjan::visitRoot(TypePackId tp)
|
||||
}
|
||||
|
||||
void Tarjan::clearTarjan(const TxnLog* log)
|
||||
{
|
||||
if (FFlag::LuauReusableSubstitutions)
|
||||
{
|
||||
typeToIndex.clear(~0u);
|
||||
packToIndex.clear(~0u);
|
||||
}
|
||||
else
|
||||
{
|
||||
typeToIndex.clear();
|
||||
packToIndex.clear();
|
||||
}
|
||||
|
||||
nodes.clear();
|
||||
|
||||
stack.clear();
|
||||
|
||||
if (FFlag::LuauReusableSubstitutions)
|
||||
{
|
||||
childCount = 0;
|
||||
// childLimit setting stays the same
|
||||
|
||||
this->log = log;
|
||||
}
|
||||
|
||||
edgesTy.clear();
|
||||
edgesTp.clear();
|
||||
@ -629,8 +617,6 @@ std::optional<TypePackId> Substitution::substitute(TypePackId tp)
|
||||
|
||||
void Substitution::resetState(const TxnLog* log, TypeArena* arena)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauReusableSubstitutions);
|
||||
|
||||
clearTarjan(log);
|
||||
|
||||
this->arena = arena;
|
||||
|
@ -627,6 +627,11 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
||||
result = isCovariantWith(env, p);
|
||||
else if (auto p = get2<SingletonType, SingletonType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
else if (auto p = get2<FunctionType, PrimitiveType>(subTy, superTy))
|
||||
{
|
||||
auto [subFunction, superPrimitive] = p;
|
||||
result.isSubtype = superPrimitive->type == PrimitiveType::Function;
|
||||
}
|
||||
else if (auto p = get2<FunctionType, FunctionType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p);
|
||||
else if (auto p = get2<TableType, TableType>(subTy, superTy))
|
||||
|
@ -389,6 +389,10 @@ TypeId matchLiteralType(
|
||||
TypeId tProp = follow(*propTy);
|
||||
if (get<BlockedType>(tProp))
|
||||
toBlock.push_back(tProp);
|
||||
|
||||
// Populate expected types for non-string keys declared with [] (the code below will handle the case where they are strings)
|
||||
if (!item.key->as<AstExprConstantString>() && expectedTableTy->indexer)
|
||||
(*astExpectedTypes)[item.key] = expectedTableTy->indexer->indexType;
|
||||
}
|
||||
else
|
||||
LUAU_ASSERT(!"Unexpected");
|
||||
|
@ -1445,8 +1445,9 @@ struct TypeChecker2
|
||||
else
|
||||
LUAU_ASSERT(!"Generating the best possible error from this function call resolution was inexhaustive?");
|
||||
|
||||
if (resolver.arityMismatches.size() > 1 || resolver.nonviableOverloads.size() > 1)
|
||||
{
|
||||
if (resolver.nonviableOverloads.size() <= 1 && resolver.arityMismatches.size() <= 1)
|
||||
return;
|
||||
|
||||
std::string s = "Available overloads: ";
|
||||
|
||||
std::vector<TypeId> overloads;
|
||||
@ -1466,6 +1467,9 @@ struct TypeChecker2
|
||||
overloads.push_back(ty);
|
||||
}
|
||||
|
||||
if (overloads.size() <= 1)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < overloads.size(); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
@ -1476,7 +1480,6 @@ struct TypeChecker2
|
||||
|
||||
reportError(ExtraInformation{std::move(s)}, call->func->location);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstExprCall* call)
|
||||
{
|
||||
@ -1756,7 +1759,7 @@ struct TypeChecker2
|
||||
if (get<TypeFunctionInstanceType>(follow(retTy)))
|
||||
{
|
||||
TypeFunctionReductionGuessResult result = guesser.guessTypeFunctionReductionForFunctionExpr(*fn, inferredFtv, retTy);
|
||||
if (result.shouldRecommendAnnotation)
|
||||
if (result.shouldRecommendAnnotation && !get<UnknownType>(result.guessedReturnType))
|
||||
reportError(
|
||||
ExplicitFunctionAnnotationRecommended{std::move(result.guessedFunctionAnnotations), result.guessedReturnType},
|
||||
fn->location
|
||||
@ -1838,6 +1841,17 @@ struct TypeChecker2
|
||||
if (nty && nty->shouldSuppressErrors())
|
||||
return;
|
||||
|
||||
switch (normalizer.isInhabited(nty.get()))
|
||||
{
|
||||
case NormalizationResult::True:
|
||||
break;
|
||||
case NormalizationResult::False:
|
||||
return;
|
||||
case NormalizationResult::HitLimits:
|
||||
reportError(NormalizationTooComplex{}, expr->location);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasLength(operandType, seen, &recursionCount))
|
||||
{
|
||||
if (isOptional(operandType))
|
||||
|
@ -659,12 +659,9 @@ TypeFunctionReductionResult<TypeId> lenTypeFunction(
|
||||
if (normTy->shouldSuppressErrors())
|
||||
return {ctx->builtins->numberType, false, {}, {}};
|
||||
|
||||
// if we have an uninhabited type (like `never`), we can never observe that the operator didn't work.
|
||||
if (inhabited == NormalizationResult::False)
|
||||
return {ctx->builtins->neverType, false, {}, {}};
|
||||
|
||||
// # always returns a number, even if its operand is never.
|
||||
// if we're checking the length of a string, that works!
|
||||
if (normTy->isSubtypeOfString())
|
||||
if (inhabited == NormalizationResult::False || normTy->isSubtypeOfString())
|
||||
return {ctx->builtins->numberType, false, {}, {}};
|
||||
|
||||
// we use the normalized operand here in case there was an intersection or union.
|
||||
@ -1576,42 +1573,53 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||
NotNull<TypeFunctionContext> ctx
|
||||
)
|
||||
{
|
||||
if (typeParams.size() != 2 || !packParams.empty())
|
||||
if (typeParams.size() < 2 || !packParams.empty())
|
||||
{
|
||||
ctx->ice->ice("refine type function: encountered a type function instance without the required argument structure");
|
||||
LUAU_ASSERT(false);
|
||||
}
|
||||
|
||||
TypeId targetTy = follow(typeParams.at(0));
|
||||
TypeId discriminantTy = follow(typeParams.at(1));
|
||||
std::vector<TypeId> discriminantTypes;
|
||||
for (size_t i = 1; i < typeParams.size(); i++)
|
||||
discriminantTypes.push_back(follow(typeParams.at(i)));
|
||||
|
||||
// check to see if both operand types are resolved enough, and wait to reduce if not
|
||||
if (isPending(targetTy, ctx->solver))
|
||||
return {std::nullopt, false, {targetTy}, {}};
|
||||
else if (isPending(discriminantTy, ctx->solver))
|
||||
return {std::nullopt, false, {discriminantTy}, {}};
|
||||
|
||||
// if either type is free but has only one remaining reference, we can generalize it to its upper bound here.
|
||||
else
|
||||
{
|
||||
for (auto t : discriminantTypes)
|
||||
{
|
||||
if (isPending(t, ctx->solver))
|
||||
return {std::nullopt, false, {t}, {}};
|
||||
}
|
||||
}
|
||||
// Refine a target type and a discriminant one at a time.
|
||||
// Returns result : TypeId, toBlockOn : vector<TypeId>
|
||||
auto stepRefine = [&ctx](TypeId target, TypeId discriminant) -> std::pair<TypeId, std::vector<TypeId>>
|
||||
{
|
||||
std::vector<TypeId> toBlock;
|
||||
if (ctx->solver)
|
||||
{
|
||||
std::optional<TypeId> targetMaybeGeneralized = ctx->solver->generalizeFreeType(ctx->scope, targetTy);
|
||||
std::optional<TypeId> discriminantMaybeGeneralized = ctx->solver->generalizeFreeType(ctx->scope, discriminantTy);
|
||||
std::optional<TypeId> targetMaybeGeneralized = ctx->solver->generalizeFreeType(ctx->scope, target);
|
||||
std::optional<TypeId> discriminantMaybeGeneralized = ctx->solver->generalizeFreeType(ctx->scope, discriminant);
|
||||
|
||||
if (!targetMaybeGeneralized)
|
||||
return {std::nullopt, false, {targetTy}, {}};
|
||||
return std::pair<TypeId, std::vector<TypeId>>{nullptr, {target}};
|
||||
else if (!discriminantMaybeGeneralized)
|
||||
return {std::nullopt, false, {discriminantTy}, {}};
|
||||
return std::pair<TypeId, std::vector<TypeId>>{nullptr, {discriminant}};
|
||||
|
||||
targetTy = *targetMaybeGeneralized;
|
||||
discriminantTy = *discriminantMaybeGeneralized;
|
||||
target = *targetMaybeGeneralized;
|
||||
discriminant = *discriminantMaybeGeneralized;
|
||||
}
|
||||
|
||||
// we need a more complex check for blocking on the discriminant in particular
|
||||
FindRefinementBlockers frb;
|
||||
frb.traverse(discriminantTy);
|
||||
frb.traverse(discriminant);
|
||||
|
||||
if (!frb.found.empty())
|
||||
return {std::nullopt, false, {frb.found.begin(), frb.found.end()}, {}};
|
||||
return {nullptr, {frb.found.begin(), frb.found.end()}};
|
||||
|
||||
/* HACK: Refinements sometimes produce a type T & ~any under the assumption
|
||||
* that ~any is the same as any. This is so so weird, but refinements needs
|
||||
@ -1624,38 +1632,57 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
|
||||
*
|
||||
* We need to treat T & ~any as T in this case.
|
||||
*/
|
||||
|
||||
if (auto nt = get<NegationType>(discriminantTy))
|
||||
if (auto nt = get<NegationType>(discriminant))
|
||||
if (get<AnyType>(follow(nt->ty)))
|
||||
return {targetTy, false, {}, {}};
|
||||
return {target, {}};
|
||||
|
||||
// If the target type is a table, then simplification already implements the logic to deal with refinements properly since the
|
||||
// type of the discriminant is guaranteed to only ever be an (arbitrarily-nested) table of a single property type.
|
||||
if (get<TableType>(targetTy))
|
||||
if (get<TableType>(target))
|
||||
{
|
||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, targetTy, discriminantTy);
|
||||
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
|
||||
if (!result.blockedTypes.empty())
|
||||
return {std::nullopt, false, {result.blockedTypes.begin(), result.blockedTypes.end()}, {}};
|
||||
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
||||
|
||||
return {result.result, false, {}, {}};
|
||||
return {result.result, {}};
|
||||
}
|
||||
|
||||
// In the general case, we'll still use normalization though.
|
||||
TypeId intersection = ctx->arena->addType(IntersectionType{{targetTy, discriminantTy}});
|
||||
TypeId intersection = ctx->arena->addType(IntersectionType{{target, discriminant}});
|
||||
std::shared_ptr<const NormalizedType> normIntersection = ctx->normalizer->normalize(intersection);
|
||||
std::shared_ptr<const NormalizedType> normType = ctx->normalizer->normalize(targetTy);
|
||||
std::shared_ptr<const NormalizedType> normType = ctx->normalizer->normalize(target);
|
||||
|
||||
// if the intersection failed to normalize, we can't reduce, but know nothing about inhabitance.
|
||||
if (!normIntersection || !normType)
|
||||
return {std::nullopt, false, {}, {}};
|
||||
return {nullptr, {}};
|
||||
|
||||
TypeId resultTy = ctx->normalizer->typeFromNormal(*normIntersection);
|
||||
|
||||
// include the error type if the target type is error-suppressing and the intersection we computed is not
|
||||
if (normType->shouldSuppressErrors() && !normIntersection->shouldSuppressErrors())
|
||||
resultTy = ctx->arena->addType(UnionType{{resultTy, ctx->builtins->errorType}});
|
||||
|
||||
return {resultTy, false, {}, {}};
|
||||
return {resultTy, {}};
|
||||
};
|
||||
|
||||
// refine target with each discriminant type in sequence (reverse of insertion order)
|
||||
// If we cannot proceed, block. If all discriminant types refine successfully, return
|
||||
// the result
|
||||
TypeId target = targetTy;
|
||||
while (!discriminantTypes.empty())
|
||||
{
|
||||
TypeId discriminant = discriminantTypes.back();
|
||||
auto [refined, blocked] = stepRefine(target, discriminant);
|
||||
|
||||
if (blocked.empty() && refined == nullptr)
|
||||
return {std::nullopt, false, {}, {}};
|
||||
|
||||
if (!blocked.empty())
|
||||
return {std::nullopt, false, blocked, {}};
|
||||
|
||||
target = refined;
|
||||
discriminantTypes.pop_back();
|
||||
}
|
||||
return {target, false, {}, {}};
|
||||
}
|
||||
|
||||
TypeFunctionReductionResult<TypeId> singletonTypeFunction(
|
||||
|
@ -34,8 +34,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRemoveBadRelationalOperatorWarning, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOkWithIteratingOverTableProperties, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReusableSubstitutions, false)
|
||||
LUAU_FASTFLAG(LuauDeclarationExtraPropData)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
@ -1756,8 +1754,6 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass&
|
||||
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
|
||||
ftv->hasSelf = true;
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
{
|
||||
FunctionDefinition defn;
|
||||
|
||||
defn.definitionModuleName = currentModule->name;
|
||||
@ -1768,16 +1764,12 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass&
|
||||
ftv->definition = defn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (assignTo.count(propName) == 0)
|
||||
{
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
assignTo[propName] = {propTy, /*deprecated*/ false, /*deprecatedSuggestion*/ "", prop.location};
|
||||
else
|
||||
assignTo[propName] = {propTy};
|
||||
}
|
||||
else if (FFlag::LuauDeclarationExtraPropData)
|
||||
else
|
||||
{
|
||||
Luau::Property& prop = assignTo[propName];
|
||||
TypeId currentTy = prop.type();
|
||||
@ -1805,31 +1797,6 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass&
|
||||
reportError(declaredClass.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeId currentTy = assignTo[propName].type();
|
||||
|
||||
// We special-case this logic to keep the intersection flat; otherwise we
|
||||
// would create a ton of nested intersection types.
|
||||
if (const IntersectionType* itv = get<IntersectionType>(currentTy))
|
||||
{
|
||||
std::vector<TypeId> options = itv->parts;
|
||||
options.push_back(propTy);
|
||||
TypeId newItv = addType(IntersectionType{std::move(options)});
|
||||
|
||||
assignTo[propName] = {newItv};
|
||||
}
|
||||
else if (get<FunctionType>(currentTy))
|
||||
{
|
||||
TypeId intersection = addType(IntersectionType{{currentTy, propTy}});
|
||||
|
||||
assignTo[propName] = {intersection};
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError(declaredClass.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ControlFlow::None;
|
||||
@ -1870,13 +1837,10 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFuncti
|
||||
|
||||
FunctionDefinition defn;
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
{
|
||||
defn.definitionModuleName = currentModule->name;
|
||||
defn.definitionLocation = global.location;
|
||||
defn.varargLocation = global.vararg ? std::make_optional(global.varargLocation) : std::nullopt;
|
||||
defn.originalNameLocation = global.nameLocation;
|
||||
}
|
||||
|
||||
TypeId fnType = addType(FunctionType{funScope->level, std::move(genericTys), std::move(genericTps), argPack, retPack, defn});
|
||||
FunctionType* ftv = getMutable<FunctionType>(fnType);
|
||||
@ -4991,24 +4955,12 @@ TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location locat
|
||||
|
||||
std::optional<TypeId> instantiated;
|
||||
|
||||
if (FFlag::LuauReusableSubstitutions)
|
||||
{
|
||||
reusableInstantiation.resetState(log, ¤tModule->internalTypes, builtinTypes, scope->level, /*scope*/ nullptr);
|
||||
|
||||
if (instantiationChildLimit)
|
||||
reusableInstantiation.childLimit = *instantiationChildLimit;
|
||||
|
||||
instantiated = reusableInstantiation.substitute(ty);
|
||||
}
|
||||
else
|
||||
{
|
||||
Instantiation instantiation{log, ¤tModule->internalTypes, builtinTypes, scope->level, /*scope*/ nullptr};
|
||||
|
||||
if (instantiationChildLimit)
|
||||
instantiation.childLimit = *instantiationChildLimit;
|
||||
|
||||
instantiated = instantiation.substitute(ty);
|
||||
}
|
||||
|
||||
if (instantiated.has_value())
|
||||
return *instantiated;
|
||||
|
@ -19,7 +19,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNativeAttribute, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDeclarationExtraPropData, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions, false)
|
||||
|
||||
namespace Luau
|
||||
@ -938,16 +937,10 @@ AstStat* Parser::parseTypeFunction(const Location& start)
|
||||
|
||||
AstDeclaredClassProp Parser::parseDeclaredClassMethod()
|
||||
{
|
||||
Location start;
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
start = lexer.current().location;
|
||||
Location start = lexer.current().location;
|
||||
|
||||
nextLexeme();
|
||||
|
||||
if (!FFlag::LuauDeclarationExtraPropData)
|
||||
start = lexer.current().location;
|
||||
|
||||
Name fnName = parseName("function name");
|
||||
|
||||
// TODO: generic method declarations CLI-39909
|
||||
@ -972,7 +965,7 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod()
|
||||
expectMatchAndConsume(')', matchParen);
|
||||
|
||||
AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||
Location end = FFlag::LuauDeclarationExtraPropData ? lexer.previousLocation() : lexer.current().location;
|
||||
Location end = lexer.previousLocation();
|
||||
|
||||
TempVector<AstType*> vars(scratchType);
|
||||
TempVector<std::optional<AstArgumentName>> varNames(scratchOptArgName);
|
||||
@ -980,10 +973,7 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod()
|
||||
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
||||
{
|
||||
return AstDeclaredClassProp{
|
||||
fnName.name,
|
||||
FFlag::LuauDeclarationExtraPropData ? fnName.location : Location{},
|
||||
reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"),
|
||||
true
|
||||
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
|
||||
};
|
||||
}
|
||||
|
||||
@ -1005,13 +995,7 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod()
|
||||
Location(start, end), generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes
|
||||
);
|
||||
|
||||
return AstDeclaredClassProp{
|
||||
fnName.name,
|
||||
FFlag::LuauDeclarationExtraPropData ? fnName.location : Location{},
|
||||
fnType,
|
||||
true,
|
||||
FFlag::LuauDeclarationExtraPropData ? Location(start, end) : Location{}
|
||||
};
|
||||
return AstDeclaredClassProp{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||
}
|
||||
|
||||
AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*>& attributes)
|
||||
@ -1067,7 +1051,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||
if (vararg && !varargAnnotation)
|
||||
return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated");
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
return allocator.alloc<AstStatDeclareFunction>(
|
||||
Location(start, end),
|
||||
attributes,
|
||||
@ -1081,20 +1064,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||
varargLocation,
|
||||
retTypes
|
||||
);
|
||||
else
|
||||
return allocator.alloc<AstStatDeclareFunction>(
|
||||
Location(start, end),
|
||||
attributes,
|
||||
globalName.name,
|
||||
Location{},
|
||||
generics,
|
||||
genericPacks,
|
||||
AstTypeList{copy(vars), varargAnnotation},
|
||||
copy(varNames),
|
||||
false,
|
||||
Location{},
|
||||
retTypes
|
||||
);
|
||||
}
|
||||
else if (AstName(lexer.current().name) == "class")
|
||||
{
|
||||
@ -1124,8 +1093,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||
const Lexeme begin = lexer.current();
|
||||
nextLexeme(); // [
|
||||
|
||||
if (FFlag::LuauDeclarationExtraPropData)
|
||||
{
|
||||
const Location nameBegin = lexer.current().location;
|
||||
std::optional<AstArray<char>> chars = parseCharArray();
|
||||
|
||||
@ -1139,26 +1106,13 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||
bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size);
|
||||
|
||||
if (chars && !containsNull)
|
||||
{
|
||||
props.push_back(AstDeclaredClassProp{
|
||||
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
|
||||
});
|
||||
else
|
||||
report(begin.location, "String literal contains malformed escape sequence or \\0");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::optional<AstArray<char>> chars = parseCharArray();
|
||||
|
||||
expectMatchAndConsume(']', begin);
|
||||
expectAndConsume(':', "property type annotation");
|
||||
AstType* type = parseType();
|
||||
|
||||
// since AstName contains a char*, it can't contain null
|
||||
bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size);
|
||||
|
||||
if (chars && !containsNull)
|
||||
props.push_back(AstDeclaredClassProp{AstName(chars->data), Location{}, type, false});
|
||||
else
|
||||
report(begin.location, "String literal contains malformed escape sequence or \\0");
|
||||
}
|
||||
}
|
||||
@ -1178,7 +1132,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||
indexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt);
|
||||
}
|
||||
}
|
||||
else if (FFlag::LuauDeclarationExtraPropData)
|
||||
else
|
||||
{
|
||||
Location propStart = lexer.current().location;
|
||||
Name propName = parseName("property name");
|
||||
@ -1187,13 +1141,6 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||
props.push_back(AstDeclaredClassProp{propName.name, propName.location, propType, false, Location(propStart, lexer.previousLocation())}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Name propName = parseName("property name");
|
||||
expectAndConsume(':', "property type annotation");
|
||||
AstType* propType = parseType();
|
||||
props.push_back(AstDeclaredClassProp{propName.name, Location{}, propType, false});
|
||||
}
|
||||
}
|
||||
|
||||
Location classEnd = lexer.current().location;
|
||||
@ -1206,9 +1153,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
||||
expectAndConsume(':', "global variable declaration");
|
||||
|
||||
AstType* type = parseType(/* in declaration context */ true);
|
||||
return allocator.alloc<AstStatDeclareGlobal>(
|
||||
Location(start, type->location), globalName->name, FFlag::LuauDeclarationExtraPropData ? globalName->location : Location{}, type
|
||||
);
|
||||
return allocator.alloc<AstStatDeclareGlobal>(Location(start, type->location), globalName->name, globalName->location, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorResumeCleanupArgs, false)
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Error-recovery functions
|
||||
@ -426,9 +428,13 @@ static void resume_handle(lua_State* L, void* ud)
|
||||
resume_continue(L);
|
||||
}
|
||||
|
||||
static int resume_error(lua_State* L, const char* msg)
|
||||
static int resume_error(lua_State* L, const char* msg, int narg)
|
||||
{
|
||||
if (FFlag::LuauErrorResumeCleanupArgs)
|
||||
L->top -= narg;
|
||||
else
|
||||
L->top = L->ci->base;
|
||||
|
||||
setsvalue(L, L->top, luaS_new(L, msg));
|
||||
incr_top(L);
|
||||
return LUA_ERRRUN;
|
||||
@ -455,11 +461,11 @@ int lua_resume(lua_State* L, lua_State* from, int nargs)
|
||||
{
|
||||
int status;
|
||||
if (L->status != LUA_YIELD && L->status != LUA_BREAK && (L->status != 0 || L->ci != L->base_ci))
|
||||
return resume_error(L, "cannot resume non-suspended coroutine");
|
||||
return resume_error(L, "cannot resume non-suspended coroutine", nargs);
|
||||
|
||||
L->nCcalls = from ? from->nCcalls : 0;
|
||||
if (L->nCcalls >= LUAI_MAXCCALLS)
|
||||
return resume_error(L, "C stack overflow");
|
||||
return resume_error(L, "C stack overflow", nargs);
|
||||
|
||||
L->baseCcalls = ++L->nCcalls;
|
||||
L->isactive = true;
|
||||
@ -484,11 +490,11 @@ int lua_resumeerror(lua_State* L, lua_State* from)
|
||||
{
|
||||
int status;
|
||||
if (L->status != LUA_YIELD && L->status != LUA_BREAK && (L->status != 0 || L->ci != L->base_ci))
|
||||
return resume_error(L, "cannot resume non-suspended coroutine");
|
||||
return resume_error(L, "cannot resume non-suspended coroutine", 1);
|
||||
|
||||
L->nCcalls = from ? from->nCcalls : 0;
|
||||
if (L->nCcalls >= LUAI_MAXCCALLS)
|
||||
return resume_error(L, "C stack overflow");
|
||||
return resume_error(L, "C stack overflow", 1);
|
||||
|
||||
L->baseCcalls = ++L->nCcalls;
|
||||
L->isactive = true;
|
||||
|
@ -9,8 +9,6 @@
|
||||
#include <math.h>
|
||||
#include <ostream>
|
||||
|
||||
LUAU_FASTFLAG(LuauDeclarationExtraPropData)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
struct JsonEncoderFixture
|
||||
@ -417,8 +415,6 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatTypeAlias")
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction")
|
||||
{
|
||||
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
|
||||
|
||||
AstStat* statement = expectParseStatement("declare function foo(x: number): string");
|
||||
|
||||
std::string_view expected =
|
||||
@ -429,8 +425,6 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction")
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction2")
|
||||
{
|
||||
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
|
||||
|
||||
AstStat* statement = expectParseStatement("declare function foo(x: number, ...: string): string");
|
||||
|
||||
std::string_view expected =
|
||||
@ -441,8 +435,6 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction2")
|
||||
|
||||
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass")
|
||||
{
|
||||
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
|
||||
|
||||
AstStatBlock* root = expectParse(R"(
|
||||
declare class Foo
|
||||
prop: number
|
||||
|
@ -171,6 +171,8 @@ TEST_SUITE_BEGIN("AstQuery");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "last_argument_function_call_type")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
check(R"(
|
||||
local function foo() return 2 end
|
||||
local function bar(a: number) return -a end
|
||||
|
@ -1658,6 +1658,10 @@ table.create(42, {} :: {})
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperationsIndexer")
|
||||
{
|
||||
// CLI-116824 Linter incorrectly issues false positive when taking the length of a unannotated string function argument
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
LintResult result = lint(R"(
|
||||
local t1 = {} -- ok: empty
|
||||
local t2 = {1, 2} -- ok: array
|
||||
|
@ -440,11 +440,11 @@ struct NormalizeFixture : Fixture
|
||||
registerHiddenTypes(&frontend);
|
||||
}
|
||||
|
||||
std::shared_ptr<const NormalizedType> toNormalizedType(const std::string& annotation)
|
||||
std::shared_ptr<const NormalizedType> toNormalizedType(const std::string& annotation, int expectedErrors = 0)
|
||||
{
|
||||
normalizer.clearCaches();
|
||||
CheckResult result = check("type _Res = " + annotation);
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
LUAU_REQUIRE_ERROR_COUNT(expectedErrors, result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
@ -662,7 +662,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "negated_function_is_anything_except_a_funct
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "specific_functions_cannot_be_negated")
|
||||
{
|
||||
CHECK(nullptr == toNormalizedType("Not<(boolean) -> boolean>"));
|
||||
CHECK(nullptr == toNormalizedType("Not<(boolean) -> boolean>", FFlag::DebugLuauDeferredConstraintResolution ? 1 : 0));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "trivial_intersection_inhabited")
|
||||
@ -875,7 +875,7 @@ TEST_CASE_FIXTURE(NormalizeFixture, "top_table_type")
|
||||
|
||||
TEST_CASE_FIXTURE(NormalizeFixture, "negations_of_tables")
|
||||
{
|
||||
CHECK(nullptr == toNormalizedType("Not<{}>"));
|
||||
CHECK(nullptr == toNormalizedType("Not<{}>", FFlag::DebugLuauDeferredConstraintResolution ? 1 : 0));
|
||||
CHECK("(boolean | buffer | class | function | number | string | thread)?" == toString(normal("Not<tbl>")));
|
||||
CHECK("table" == toString(normal("Not<Not<tbl>>")));
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ LUAU_FASTINT(LuauTypeLengthLimit);
|
||||
LUAU_FASTINT(LuauParseErrorLimit);
|
||||
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
||||
LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr);
|
||||
LUAU_FASTFLAG(LuauDeclarationExtraPropData);
|
||||
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions);
|
||||
|
||||
namespace
|
||||
@ -1918,8 +1917,6 @@ function func():end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_declarations")
|
||||
{
|
||||
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
|
||||
|
||||
AstStatBlock* stat = parseEx(R"(
|
||||
declare foo: number
|
||||
declare function bar(x: number): string
|
||||
@ -1957,8 +1954,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_declarations")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_class_declarations")
|
||||
{
|
||||
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
|
||||
|
||||
AstStatBlock* stat = parseEx(R"(
|
||||
declare class Foo
|
||||
prop: number
|
||||
|
@ -104,6 +104,9 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "mismatched_generic_type_param")
|
||||
{
|
||||
// We erroneously report an extra error in this case when the new solver is enabled.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T<A> = (A...) -> ()
|
||||
)");
|
||||
@ -240,6 +243,9 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases")
|
||||
{
|
||||
// CLI-116108
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type T<a> = { f: a, g: U<a> }
|
||||
@ -411,6 +417,8 @@ TEST_CASE_FIXTURE(Fixture, "corecursive_function_types")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_param_remap")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
const std::string code = R"(
|
||||
-- An example of a forwarded use of a type that has different type arguments than parameters
|
||||
type A<T,U> = {t:T, u:U, next:A<U,T>?}
|
||||
@ -535,6 +543,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_import_mutation")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_local_mutation")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Cool = { a: number, b: string }
|
||||
local c: Cool = { a = 1, b = "s" }
|
||||
@ -554,6 +564,8 @@ type NotCool<x> = Cool
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_local_rename")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Cool = { a: number, b: string }
|
||||
type NotCool = Cool
|
||||
@ -648,9 +660,17 @@ type X<T, U> = Import.X<U, T>
|
||||
ty2 = lookupType("X");
|
||||
REQUIRE(ty2);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK(toString(*ty1, {true}) == "t1 where t1 = { C: t1?, a: T, b: U }");
|
||||
CHECK(toString(*ty2, {true}) == "t1 where t1 = { C: t1?, a: U, b: T }");
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(toString(*ty1, {true}), "t1 where t1 = {| C: t1?, a: T, b: U |}");
|
||||
CHECK_EQ(toString(*ty2, {true}), "{| C: t1, a: U, b: T |} where t1 = {| C: t1, a: U, b: T |}?");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "module_export_free_type_leak")
|
||||
{
|
||||
@ -691,6 +711,9 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_ok")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1")
|
||||
{
|
||||
// CLI-116108
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
-- OK because forwarded types are used with their parameters.
|
||||
type Tree<T> = { data: T, children: Forest<T> }
|
||||
@ -702,6 +725,9 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_1")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_restriction_not_ok_2")
|
||||
{
|
||||
// CLI-116108
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
-- Not OK because forwarded types are used with different types than their parameters.
|
||||
type Forest<T> = {Tree<{T}>}
|
||||
@ -723,6 +749,9 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_ok")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_types_swapsies_not_ok")
|
||||
{
|
||||
// CLI-116108
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Tree1<T,U> = { data: T, children: {Tree2<U,T>} }
|
||||
type Tree2<T,U> = { data: U, children: {Tree1<T,U>} }
|
||||
@ -845,6 +874,9 @@ TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_ok")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok")
|
||||
{
|
||||
// CLI-116108
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
-- this would be an infinite type if we allowed it
|
||||
type Tree<T> = { data: T, children: {Tree<{T}>} }
|
||||
@ -855,6 +887,9 @@ TEST_CASE_FIXTURE(Fixture, "recursive_types_restriction_not_ok")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "report_shadowed_aliases")
|
||||
{
|
||||
// CLI-116110
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
// We allow a previous type alias to depend on a future type alias. That exact feature enables a confusing example, like the following snippet,
|
||||
// which has the type alias FakeString point to the type alias `string` that which points to `number`.
|
||||
CheckResult result = check(R"(
|
||||
@ -936,6 +971,9 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_locations")
|
||||
*/
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "dont_lose_track_of_PendingExpansionTypes_after_substitution")
|
||||
{
|
||||
// CLI-114134 - We need egraphs to properly simplify these types.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
fileResolver.source["game/ReactCurrentDispatcher"] = R"(
|
||||
export type BasicStateAction<S> = ((S) -> S) | S
|
||||
export type Dispatch<A> = (A) -> ()
|
||||
|
@ -132,6 +132,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_predicate")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_bad_predicate")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local t = {'one', 'two', 'three'}
|
||||
@ -510,6 +512,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "buffer_is_a_type")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "coroutine_resume_anything_goes")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function nifty(x, y)
|
||||
print(x, y)
|
||||
@ -547,6 +551,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "coroutine_wrap_anything_goes")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "setmetatable_should_not_mutate_persisted_types")
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local string = string
|
||||
|
||||
@ -591,6 +598,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_arg_count_mismatch")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_correctly_ordered_types")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
string.format("%s", 123)
|
||||
@ -694,10 +705,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_select_should_not_crash")
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
// Counterintuitively, the parametr l0 is unconstrained and therefore it is valid to pass nil.
|
||||
// Counterintuitively, the parameter l0 is unconstrained and therefore it is valid to pass nil.
|
||||
// The new solver therefore considers that parameter to be optional.
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK("Argument count mismatch. Function expects 1 argument, but none are specified" == toString(result.errors[0]));
|
||||
CHECK("Argument count mismatch. Function expects at least 1 argument, but none are specified" == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -709,6 +720,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bad_select_should_not_crash")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "select_way_out_of_range")
|
||||
{
|
||||
// CLI-115720
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
select(5432598430953240958)
|
||||
)");
|
||||
@ -720,6 +735,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_way_out_of_range")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "select_slightly_out_of_range")
|
||||
{
|
||||
// CLI-115720
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
select(3, "a", 1)
|
||||
)");
|
||||
@ -750,6 +769,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail_and_string_head")
|
||||
{
|
||||
// CLI-115720
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
local function f(...)
|
||||
@ -769,6 +792,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_with_variadic_typepack_tail_and_strin
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "string_format_as_method")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check("local _ = ('%s'):format(5)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
@ -792,6 +819,10 @@ TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "string_format_use_correct_argument2")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local _ = ("%s %d").format("%d %s", "A type error", 2)
|
||||
)");
|
||||
@ -850,6 +881,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "debug_info_is_crazy")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "aliased_string_format")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local fmt = string.format
|
||||
local s = fmt("%d", "oops")
|
||||
@ -909,6 +944,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "select_on_variadic")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_report_all_type_errors_at_correct_positions")
|
||||
{
|
||||
// CLI-115690
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
("%s%d%s"):format(1, "hello", true)
|
||||
string.format("%s%d%s", 1, "hello", true)
|
||||
@ -1039,6 +1078,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_removes_falsy_types3")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "assert_removes_falsy_types_even_from_type_pack_tail_but_only_for_the_first_type")
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(...: number?)
|
||||
return assert(...)
|
||||
@ -1051,6 +1093,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_removes_falsy_types_even_from_type_pa
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "assert_returns_false_and_string_iff_it_knows_the_first_argument_cannot_be_truthy")
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
// CLI-114134 - egraph simplification
|
||||
return;
|
||||
}
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: nil)
|
||||
return assert(x, "hmm")
|
||||
@ -1080,6 +1128,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("Key 'b' not found in table '{ a: number }'" == toString(result.errors[0]));
|
||||
else
|
||||
CHECK_EQ("Key 'b' not found in table '{| a: number |}'", toString(result.errors[0]));
|
||||
CHECK(Location({13, 18}, {13, 23}) == result.errors[0].location);
|
||||
|
||||
@ -1095,6 +1146,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_is_generic")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "set_metatable_needs_arguments")
|
||||
{
|
||||
// In the new solver, nil can certainly be used where a generic is required, so all generic parameters are optional.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local a = {b=setmetatable}
|
||||
a.b()
|
||||
|
@ -128,6 +128,8 @@ TEST_CASE_FIXTURE(ClassFixture, "we_can_infer_that_a_parameter_must_be_a_particu
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function makeClone(o)
|
||||
return BaseClass.Clone(o)
|
||||
@ -152,6 +154,34 @@ TEST_CASE_FIXTURE(ClassFixture, "we_can_report_when_someone_is_trying_to_use_a_t
|
||||
CHECK_EQ("BaseClass", toString(tm->wantedType));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class_using_new_solver")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function makeClone(o)
|
||||
return BaseClass.Clone(o)
|
||||
end
|
||||
|
||||
type Oopsies = { read BaseMethod: (Oopsies, number) -> ()}
|
||||
|
||||
local oopsies: Oopsies = {
|
||||
BaseMethod = function (self: Oopsies, i: number)
|
||||
print('gadzooks!')
|
||||
end
|
||||
}
|
||||
|
||||
makeClone(oopsies)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
TypeMismatch* tm = get<TypeMismatch>(result.errors.at(0));
|
||||
REQUIRE(tm != nullptr);
|
||||
|
||||
CHECK_EQ("Oopsies", toString(tm->givenType));
|
||||
CHECK_EQ("BaseClass", toString(tm->wantedType));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "assign_to_prop_of_class")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
@ -204,6 +234,9 @@ TEST_CASE_FIXTURE(ClassFixture, "can_assign_to_prop_of_base_class_using_string")
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "cannot_unify_class_instance_with_primitive")
|
||||
{
|
||||
// This is allowed in the new solver
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local v = Vector2.New(0, 5)
|
||||
v = 444
|
||||
@ -364,10 +397,18 @@ TEST_CASE_FIXTURE(ClassFixture, "table_class_unification_reports_sane_errors_for
|
||||
foo(a)
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK("Type 'Vector2' could not be converted into '{ Y: number, w: number, x: number }'" == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
REQUIRE_EQ("Key 'w' not found in class 'Vector2'", toString(result.errors.at(0)));
|
||||
REQUIRE_EQ("Key 'x' not found in class 'Vector2'. Did you mean 'X'?", toString(result.errors[1]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "class_unification_type_mismatch_is_correct_order")
|
||||
{
|
||||
@ -412,15 +453,27 @@ b(a)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK("Type 'number' could not be converted into 'string'" == toString(result.errors.at(0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'Vector2' could not be converted into '{- X: number, Y: string -}'
|
||||
caused by:
|
||||
Property 'Y' is not compatible.
|
||||
Type 'number' could not be converted into 'string')";
|
||||
|
||||
CHECK_EQ(expected, toString(result.errors.at(0)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "class_type_mismatch_with_name_conflict")
|
||||
{
|
||||
// CLI-116433
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local i = ChildClass.New()
|
||||
type ChildClass = { x: number }
|
||||
@ -611,7 +664,11 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes")
|
||||
local y = x[true]
|
||||
)");
|
||||
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK(
|
||||
"Type 'boolean' could not be converted into 'number | string'" == toString(result.errors.at(0))
|
||||
);
|
||||
else
|
||||
CHECK_EQ(
|
||||
toString(result.errors.at(0)), "Type 'boolean' could not be converted into 'number | string'; none of the union options are compatible"
|
||||
);
|
||||
@ -622,6 +679,11 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes")
|
||||
x[true] = 42
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK(
|
||||
"Type 'boolean' could not be converted into 'number | string'" == toString(result.errors.at(0))
|
||||
);
|
||||
else
|
||||
CHECK_EQ(
|
||||
toString(result.errors.at(0)), "Type 'boolean' could not be converted into 'number | string'; none of the union options are compatible"
|
||||
);
|
||||
@ -633,6 +695,12 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes")
|
||||
local x : IndexableClass
|
||||
x.key = "string value"
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
// Disabled for now. CLI-115686
|
||||
}
|
||||
else
|
||||
CHECK_EQ(toString(result.errors.at(0)), "Type 'string' could not be converted into 'number'");
|
||||
}
|
||||
{
|
||||
@ -682,7 +750,7 @@ TEST_CASE_FIXTURE(ClassFixture, "indexable_classes")
|
||||
local y = x["key"]
|
||||
)");
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ(toString(result.errors.at(0)), "Key 'key' not found in class 'IndexableNumericKeyClass'");
|
||||
CHECK(toString(result.errors.at(0)) == "Key 'key' not found in class 'IndexableNumericKeyClass'");
|
||||
else
|
||||
CHECK_EQ(toString(result.errors.at(0)), "Type 'string' could not be converted into 'number'");
|
||||
}
|
||||
|
@ -7,8 +7,6 @@
|
||||
|
||||
#include "doctest.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauDeclarationExtraPropData)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
TEST_SUITE_BEGIN("DefinitionTests");
|
||||
@ -346,8 +344,6 @@ TEST_CASE_FIXTURE(Fixture, "definitions_documentation_symbols")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_referenced_types")
|
||||
{
|
||||
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
|
||||
|
||||
loadDefinition(R"(
|
||||
declare class MyClass
|
||||
function myMethod(self)
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "ClassFixture.h"
|
||||
#include "Fixture.h"
|
||||
|
||||
#include "ScopedFlags.h"
|
||||
#include "doctest.h"
|
||||
|
||||
using namespace Luau;
|
||||
@ -255,7 +256,7 @@ TEST_CASE_FIXTURE(Fixture, "list_only_alternative_overloads_that_match_argument_
|
||||
REQUIRE(ei);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("Available overloads: (number) -> number; and (number) -> string" == ei->message);
|
||||
CHECK("Available overloads: (number) -> number; (number) -> string; and (number, number) -> number" == ei->message);
|
||||
else
|
||||
CHECK_EQ("Other overloads are also not viable: (number) -> string", ei->message);
|
||||
}
|
||||
@ -310,6 +311,9 @@ TEST_CASE_FIXTURE(Fixture, "infer_return_type_from_selected_overload")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_many_arguments")
|
||||
{
|
||||
// This is not part of the new non-strict specification currently.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
|
||||
@ -475,6 +479,20 @@ TEST_CASE_FIXTURE(Fixture, "another_higher_order_function")
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "another_other_higher_order_function")
|
||||
{
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local function f(d)
|
||||
d:foo()
|
||||
d:foo()
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local d
|
||||
@ -484,6 +502,7 @@ TEST_CASE_FIXTURE(Fixture, "another_other_higher_order_function")
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "local_function")
|
||||
{
|
||||
@ -585,6 +604,9 @@ TEST_CASE_FIXTURE(Fixture, "duplicate_functions_allowed_in_nonstrict")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "duplicate_functions_with_different_signatures_not_allowed_in_nonstrict")
|
||||
{
|
||||
// This is not part of the spec for the new non-strict mode currently.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
function foo(): number
|
||||
@ -612,7 +634,7 @@ TEST_CASE_FIXTURE(Fixture, "complicated_return_types_require_an_explicit_annotat
|
||||
local i = 0
|
||||
function most_of_the_natural_numbers(): number?
|
||||
if i < 10 then
|
||||
i = i + 1
|
||||
i += 1
|
||||
return i
|
||||
else
|
||||
return nil
|
||||
@ -737,7 +759,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "higher_order_function_4")
|
||||
end
|
||||
end
|
||||
|
||||
function mergesort(arr, comp)
|
||||
function mergesort<T>(arr: {T}, comp: (T, T) -> boolean)
|
||||
local work = {}
|
||||
for i = 1, #arr do
|
||||
work[i] = arr[i]
|
||||
@ -756,6 +778,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "higher_order_function_4")
|
||||
end
|
||||
)");
|
||||
|
||||
// This function currently has a bug in the new solver reporting `{T} | {T}` is not a table.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
else
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
/*
|
||||
@ -861,6 +887,9 @@ TEST_CASE_FIXTURE(Fixture, "another_indirect_function_case_where_it_is_ok_to_pro
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "report_exiting_without_return_nonstrict")
|
||||
{
|
||||
// new non-strict mode spec does not include this error yet.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
|
||||
@ -985,11 +1014,17 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_lea
|
||||
opts.exhaustive = true;
|
||||
opts.maxTableLength = 0;
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ("{string}", toString(requireType("tab"), opts));
|
||||
else
|
||||
CHECK_EQ("{any}", toString(requireType("tab"), opts));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_many_return_values")
|
||||
{
|
||||
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
||||
@ -1011,6 +1046,9 @@ TEST_CASE_FIXTURE(Fixture, "too_many_return_values")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_many_return_values_in_parentheses")
|
||||
{
|
||||
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
||||
@ -1032,6 +1070,9 @@ TEST_CASE_FIXTURE(Fixture, "too_many_return_values_in_parentheses")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_many_return_values_no_function")
|
||||
{
|
||||
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
||||
@ -1072,6 +1113,17 @@ TEST_CASE_FIXTURE(Fixture, "function_does_not_return_enough_values")
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
auto tpm = get<TypePackMismatch>(result.errors[0]);
|
||||
REQUIRE(tpm);
|
||||
CHECK("number, string" == toString(tpm->wantedTp));
|
||||
CHECK("number" == toString(tpm->givenTp));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CountMismatch* acm = get<CountMismatch>(result.errors[0]);
|
||||
@ -1080,6 +1132,7 @@ TEST_CASE_FIXTURE(Fixture, "function_does_not_return_enough_values")
|
||||
CHECK_EQ(acm->expected, 2);
|
||||
CHECK_EQ(acm->actual, 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_cast_error_uses_correct_language")
|
||||
{
|
||||
@ -1100,12 +1153,18 @@ TEST_CASE_FIXTURE(Fixture, "function_cast_error_uses_correct_language")
|
||||
REQUIRE(tm1);
|
||||
|
||||
CHECK_EQ("(string) -> number", toString(tm1->wantedType));
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ("(unknown, unknown) -> number", toString(tm1->givenType));
|
||||
else
|
||||
CHECK_EQ("(string, *error-type*) -> number", toString(tm1->givenType));
|
||||
|
||||
auto tm2 = get<TypeMismatch>(result.errors[1]);
|
||||
REQUIRE(tm2);
|
||||
|
||||
CHECK_EQ("(number, number) -> (number, number)", toString(tm2->wantedType));
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ("(unknown, unknown) -> number", toString(tm1->givenType));
|
||||
else
|
||||
CHECK_EQ("(string, *error-type*) -> number", toString(tm2->givenType));
|
||||
}
|
||||
|
||||
@ -1123,6 +1182,9 @@ TEST_CASE_FIXTURE(Fixture, "no_lossy_function_type")
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
TypeId type = requireTypeAtPosition(Position(6, 14));
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ("(unknown, number, number) -> number", toString(type));
|
||||
else
|
||||
CHECK_EQ("(tbl, number, number) -> number", toString(type));
|
||||
auto ftv = get<FunctionType>(follow(type));
|
||||
REQUIRE(ftv);
|
||||
@ -1166,6 +1228,10 @@ TEST_CASE_FIXTURE(Fixture, "return_type_by_overload")
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireType("x")));
|
||||
// the new solver does not currently "favor" arity-matching overloads when the call itself is ill-typed.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ("string", toString(requireType("y")));
|
||||
else
|
||||
CHECK_EQ("number", toString(requireType("y")));
|
||||
// Should this be string|number?
|
||||
CHECK_EQ("string", toString(requireType("z")));
|
||||
@ -1173,6 +1239,9 @@ TEST_CASE_FIXTURE(Fixture, "return_type_by_overload")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "infer_anonymous_function_arguments")
|
||||
{
|
||||
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
// Simple direct arg to arg propagation
|
||||
CheckResult result = check(R"(
|
||||
type Table = { x: number, y: number }
|
||||
@ -1289,6 +1358,9 @@ f(function(x) return x * 2 end)
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "infer_generic_function_function_argument")
|
||||
{
|
||||
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function sum<a>(x: a, y: a, f: (a, a) -> a) return f(x, y) end
|
||||
return sum(2, 3, function(a, b) return a + b end)
|
||||
@ -1317,6 +1389,9 @@ local r = foldl(a, {s=0,c=0}, function(a, b) return {s = a.s + b, c = a.c + 1} e
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_function_function_argument_overloaded")
|
||||
{
|
||||
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function g1<T>(a: T, f: (T) -> T) return f(a) end
|
||||
local function g2<T>(a: T, b: T, f: (T, T) -> T) return f(a, b) end
|
||||
@ -1381,6 +1456,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "variadic_any_is_compatible_with_a_generic_Ty
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_anonymous_function_arguments_outside_call")
|
||||
{
|
||||
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Table = { x: number, y: number }
|
||||
local f: (Table) -> number = function(t) return t.x + t.y end
|
||||
@ -1412,11 +1490,18 @@ local function i(): ...{string|number}
|
||||
end
|
||||
)");
|
||||
|
||||
// `h` regresses in the new solver, the return type is not being pushed into the body.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
else
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_arg_count")
|
||||
{
|
||||
// FIXME: CLI-116111 test disabled until type path stringification is improved
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = (number, number) -> string
|
||||
type B = (number) -> string
|
||||
@ -1437,6 +1522,9 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_arg")
|
||||
{
|
||||
// FIXME: CLI-116111 test disabled until type path stringification is improved
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = (number, number) -> string
|
||||
type B = (number, string) -> string
|
||||
@ -1458,6 +1546,9 @@ Type 'string' could not be converted into 'number')";
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret_count")
|
||||
{
|
||||
// FIXME: CLI-116111 test disabled until type path stringification is improved
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = (number, number) -> (number)
|
||||
type B = (number, number) -> (number, boolean)
|
||||
@ -1478,6 +1569,9 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret")
|
||||
{
|
||||
// FIXME: CLI-116111 test disabled until type path stringification is improved
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = (number, number) -> string
|
||||
type B = (number, number) -> number
|
||||
@ -1499,6 +1593,9 @@ Type 'string' could not be converted into 'number')";
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_function_mismatch_ret_mult")
|
||||
{
|
||||
// FIXME: CLI-116111 test disabled until type path stringification is improved
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type A = (number, number) -> (number, string)
|
||||
type B = (number, number) -> (number, boolean)
|
||||
@ -1578,10 +1675,19 @@ t.f = function(x)
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)");
|
||||
CHECK_EQ(toString(result.errors[1]), R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)");
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'string' could not be converted into 'number')");
|
||||
CHECK_EQ(toString(result.errors[1]), R"(Type 'string' could not be converted into 'number')");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_the_right_time2")
|
||||
{
|
||||
@ -1611,6 +1717,9 @@ TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_th
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "inferred_higher_order_functions_are_quantified_at_the_right_time3")
|
||||
{
|
||||
// This test regresses in the new solver, but is sort of nonsensical insofar as `foo` is known to be `nil`, so it's "right" to not be able to call it.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local foo
|
||||
|
||||
@ -1637,6 +1746,13 @@ t.f = function(x)
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)");
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type
|
||||
'(string) -> string'
|
||||
@ -1653,6 +1769,7 @@ caused by:
|
||||
Type 'number' could not be converted into 'string')");
|
||||
CHECK_EQ(toString(result.errors[1]), R"(Type 'string' could not be converted into 'number')");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments")
|
||||
{
|
||||
@ -1666,6 +1783,9 @@ TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_statement_sealed_table_assignment_through_indexer")
|
||||
{
|
||||
// FIXME: CLI-116122 bug where `t:b` does not check against the type from the indexer annotation on `t`.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {[string]: () -> number} = {}
|
||||
|
||||
@ -1708,6 +1828,9 @@ TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic_generic")
|
||||
{
|
||||
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function test(a: number, b: string, ...)
|
||||
return 1
|
||||
@ -1733,6 +1856,9 @@ wrapper(test)
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "too_few_arguments_variadic_generic2")
|
||||
{
|
||||
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function test(a: number, b: string, ...)
|
||||
return 1
|
||||
@ -1807,7 +1933,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_
|
||||
|
||||
CHECK_EQ("<a>(a) -> a", toString(requireType("f")));
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ("<a>({+ p: {+ q: a +} +}) -> a & ~false", toString(requireType("g")));
|
||||
CHECK_EQ("({ read p: { read q: unknown } }) -> ~(false?)?", toString(requireType("g")));
|
||||
else
|
||||
CHECK_EQ("({+ p: {+ q: nil +} +}) -> nil", toString(requireType("g")));
|
||||
}
|
||||
@ -1854,6 +1980,21 @@ u.b().foo()
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(9, result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
// These improvements to the error messages are currently regressed in the new type solver.
|
||||
CHECK_EQ(toString(result.errors[0]), "Argument count mismatch. Function expects 1 argument, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[1]), "Argument count mismatch. Function expects 1 to 2 arguments, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[2]), "Argument count mismatch. Function expects 1 to 3 arguments, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[3]), "Argument count mismatch. Function expects 2 to 4 arguments, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[4]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[5]), "Argument count mismatch. Function expects 2 to 3 arguments, but only 1 is specified");
|
||||
CHECK_EQ(toString(result.errors[6]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[7]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[8]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(toString(result.errors[0]), "Argument count mismatch. Function 'foo1' expects 1 argument, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[1]), "Argument count mismatch. Function 'foo2' expects 1 to 2 arguments, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[2]), "Argument count mismatch. Function 'foo3' expects 1 to 3 arguments, but none are specified");
|
||||
@ -1864,10 +2005,14 @@ u.b().foo()
|
||||
CHECK_EQ(toString(result.errors[7]), "Argument count mismatch. Function 'u.a.foo' expects at least 1 argument, but none are specified");
|
||||
CHECK_EQ(toString(result.errors[8]), "Argument count mismatch. Function expects at least 1 argument, but none are specified");
|
||||
}
|
||||
}
|
||||
|
||||
// This might be surprising, but since 'any' became optional, unannotated functions in non-strict 'expect' 0 arguments
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "improved_function_arg_mismatch_error_nonstrict")
|
||||
{
|
||||
// This behavior is not part of the current specification of the new type solver.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
local function foo(a, b) end
|
||||
@ -1880,6 +2025,9 @@ foo(string.find("hello", "e"))
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "luau_subtyping_is_np_hard")
|
||||
{
|
||||
// The case that _should_ succeed here (`z = x`) does not currently in the new solver.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
|
||||
@ -1954,7 +2102,16 @@ TEST_CASE_FIXTURE(Fixture, "concrete_functions_are_not_supertypes_of_function")
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
||||
CHECK(6 == result.errors[0].location.begin.line);
|
||||
auto tm1 = get<TypeMismatch>(result.errors[0]);
|
||||
REQUIRE(tm1);
|
||||
CHECK("() -> ()" == toString(tm1->wantedType));
|
||||
CHECK("function" == toString(tm1->givenType));
|
||||
|
||||
CHECK(7 == result.errors[1].location.begin.line);
|
||||
auto tm2 = get<TypeMismatch>(result.errors[1]);
|
||||
REQUIRE(tm2);
|
||||
CHECK("<T>(T) -> T" == toString(tm2->wantedType));
|
||||
CHECK("function" == toString(tm2->givenType));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "other_things_are_not_related_to_function")
|
||||
@ -1991,9 +2148,8 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "dont_assert_when_the_tarjan_limit_is_exceeded_during_generalization")
|
||||
{
|
||||
ScopedFastInt sfi{FInt::LuauTarjanChildLimit, 2};
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true};
|
||||
ScopedFastInt sfi{FInt::LuauTarjanChildLimit, 1};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(t)
|
||||
@ -2003,8 +2159,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_assert_when_the_tarjan_limit_is_exceede
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_MESSAGE(get<CodeTooComplex>(result.errors[0]), "Expected CodeTooComplex but got: " << toString(result.errors[0]));
|
||||
CHECK(Location({1, 17}, {1, 18}) == result.errors[0].location);
|
||||
CHECK_MESSAGE(get<UnificationTooComplex>(result.errors[0]), "Expected UnificationTooComplex but got: " << toString(result.errors[0]));
|
||||
}
|
||||
|
||||
/* We had a bug under DCR where instantiated type packs had a nullptr scope.
|
||||
@ -2014,7 +2169,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_assert_when_the_tarjan_limit_is_exceede
|
||||
TEST_CASE_FIXTURE(Fixture, "instantiated_type_packs_must_have_a_non_null_scope")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
function pcall<A..., R...>(...: A...): R...
|
||||
function pcall<A..., R...>(...: (A...) -> R...): (boolean, R...)
|
||||
return nil :: any
|
||||
end
|
||||
|
||||
type Dispatch<A> = (A) -> ()
|
||||
@ -2068,9 +2224,14 @@ TEST_CASE_FIXTURE(Fixture, "function_exprs_are_generalized_at_signature_scope_no
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK(toString(requireType("foo")) == "((unknown) -> nil)?");
|
||||
else
|
||||
{
|
||||
// note that b is not in the generic list; it is free, the unconstrained type of `bar`.
|
||||
CHECK(toString(requireType("foo")) == "<a>(a) -> b");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible")
|
||||
{
|
||||
@ -2082,6 +2243,17 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "param_1_and_2_both_takes_the_same_generic_bu
|
||||
local ret: number = foo(vec2, { x = 5 })
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
auto tm = get<TypeMismatch>(result.errors[0]);
|
||||
REQUIRE(tm);
|
||||
CHECK("number" == toString(tm->wantedType));
|
||||
CHECK("{ x: number }" == toString(tm->givenType));
|
||||
}
|
||||
else
|
||||
{
|
||||
// In the old solver, this produces a very strange result:
|
||||
//
|
||||
// Here, we instantiate `<a>(x: a, y: a?) -> a` with a fresh type `'a` for `a`.
|
||||
@ -2123,6 +2295,7 @@ Table type '{ x: number }' not compatible with type 'vec2' because the former is
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
CHECK_EQ("Type 'vec2' could not be converted into 'number'", toString(result.errors[1]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible_2")
|
||||
{
|
||||
@ -2134,11 +2307,23 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "param_1_and_2_both_takes_the_same_generic_bu
|
||||
local z: boolean = f(5, "five")
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
auto tm = get<TypeMismatch>(result.errors[0]);
|
||||
REQUIRE(tm);
|
||||
CHECK("boolean" == toString(tm->wantedType));
|
||||
CHECK("number | string" == toString(tm->givenType));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
||||
CHECK_EQ(toString(result.errors[0]), "Type 'string' could not be converted into 'number'");
|
||||
CHECK_EQ(toString(result.errors[1]), "Type 'number' could not be converted into 'boolean'");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "attempt_to_call_an_intersection_of_tables")
|
||||
{
|
||||
@ -2183,10 +2368,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "attempt_to_call_an_intersection_of_tables_wi
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_packs_are_not_variadic")
|
||||
{
|
||||
// This test is blocking CI until subtyping is complete.
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -2201,7 +2382,9 @@ TEST_CASE_FIXTURE(Fixture, "generic_packs_are_not_variadic")
|
||||
apply(add, 5)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
// FIXME: this errored at some point, but doesn't anymore.
|
||||
// the desired behavior here is erroring.
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str")
|
||||
@ -2316,14 +2499,11 @@ end
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
CHECK(
|
||||
toString(result.errors[0]) ==
|
||||
"Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"
|
||||
);
|
||||
LUAU_REQUIRE_ERROR_COUNT(5, result);
|
||||
CHECK(get<ConstraintSolvingIncompleteError>(result.errors[0]));
|
||||
CHECK(
|
||||
toString(result.errors[1]) ==
|
||||
"Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"
|
||||
"Type pack '*blocked-tp-1*' could not be converted into 'boolean'; type *blocked-tp-1*.tail() (*blocked-tp-1*) is not a subtype of boolean (boolean)"
|
||||
);
|
||||
CHECK(
|
||||
toString(result.errors[2]) ==
|
||||
@ -2333,6 +2513,10 @@ end
|
||||
toString(result.errors[3]) ==
|
||||
"Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"
|
||||
);
|
||||
CHECK(
|
||||
toString(result.errors[4]) ==
|
||||
"Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2421,11 +2605,9 @@ end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
auto err = get<ExplicitFunctionAnnotationRecommended>(result.errors.back());
|
||||
LUAU_ASSERT(err);
|
||||
CHECK("unknown" == toString(err->recommendedReturn));
|
||||
REQUIRE(err->recommendedArgs.size() == 1);
|
||||
CHECK("a" == toString(err->recommendedArgs[0].second));
|
||||
auto err = get<NotATable>(result.errors.back());
|
||||
REQUIRE(err);
|
||||
CHECK("a" == toString(err->ty));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "local_function_fwd_decl_doesnt_crash")
|
||||
@ -2463,12 +2645,27 @@ TEST_CASE_FIXTURE(Fixture, "bidirectional_checking_of_callback_property")
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
auto tm = get<TypeMismatch>(result.errors[0]);
|
||||
REQUIRE(tm);
|
||||
|
||||
CHECK("((Point) -> ())?" == toString(tm->wantedType));
|
||||
CHECK("({ read z: number }) -> ()" == toString(tm->givenType));
|
||||
|
||||
Location location = result.errors[0].location;
|
||||
CHECK(location.begin.line == 6);
|
||||
CHECK(location.end.line == 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_MESSAGE(get<UnknownProperty>(result.errors[0]), "Expected UnknownProperty but got " << result.errors[0]);
|
||||
|
||||
Location location = result.errors[0].location;
|
||||
CHECK(location.begin.line == 7);
|
||||
CHECK(location.end.line == 7);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ClassFixture, "bidirectional_inference_of_class_methods")
|
||||
{
|
||||
@ -2698,7 +2895,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
|
||||
auto tm2 = get<TypePackMismatch>(result.errors[1]);
|
||||
REQUIRE(tm2);
|
||||
CHECK(toString(tm2->wantedTp) == "string");
|
||||
CHECK(toString(tm2->givenTp) == "~(false?)");
|
||||
CHECK(toString(tm2->givenTp) == "buffer | class | function | number | string | table | thread | true");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "Fixture.h"
|
||||
|
||||
#include "ScopedFlags.h"
|
||||
#include "doctest.h"
|
||||
|
||||
using namespace Luau;
|
||||
@ -331,6 +332,9 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
|
||||
{
|
||||
ScopedFastFlag dcr{
|
||||
FFlag::DebugLuauDeferredConstraintResolution, false
|
||||
}; // CLI-116476 Subtyping between type alias and an equivalent but not named type isn't working.
|
||||
CheckResult result = check(R"(
|
||||
type X = { x: (number) -> number }
|
||||
type Y = { y: (string) -> string }
|
||||
@ -368,6 +372,7 @@ caused by:
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "table_write_sealed_indirect")
|
||||
{
|
||||
ScopedFastFlag dcr{FFlag::DebugLuauDeferredConstraintResolution, false}; // CLI-
|
||||
// After normalization, previous 'table_intersection_write_sealed_indirect' is identical to this one
|
||||
CheckResult result = check(R"(
|
||||
type XY = { x: (number) -> number, y: (string) -> string }
|
||||
@ -576,6 +581,9 @@ could not be converted into
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union_saturate_overloaded_functions")
|
||||
{
|
||||
ScopedFastFlag dcr{
|
||||
FFlag::DebugLuauDeferredConstraintResolution, false
|
||||
}; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
|
||||
CheckResult result = check(R"(
|
||||
function f(x: ((number) -> number) & ((string) -> string))
|
||||
local y : ((number | string) -> (number | string)) = x -- OK
|
||||
@ -754,6 +762,9 @@ could not be converted into
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_result")
|
||||
{
|
||||
ScopedFastFlag dcr{
|
||||
FFlag::DebugLuauDeferredConstraintResolution, false
|
||||
}; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
|
||||
CheckResult result = check(R"(
|
||||
function f<a...,b...>()
|
||||
function g(x : ((number) -> number) & ((nil) -> unknown))
|
||||
@ -773,6 +784,9 @@ could not be converted into
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_arguments")
|
||||
{
|
||||
ScopedFastFlag dcr{
|
||||
FFlag::DebugLuauDeferredConstraintResolution, false
|
||||
}; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
|
||||
CheckResult result = check(R"(
|
||||
function f<a...,b...>()
|
||||
function g(x : ((number) -> number?) & ((unknown) -> string?))
|
||||
@ -801,6 +815,29 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_result")
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((nil) -> never) & ((number) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> number'; type ((nil) -> never) & ((number) -> number)[0].arguments()[0] (number) is not a supertype of (number?) -> number.arguments()[0][1] (nil)
|
||||
type ((nil) -> never) & ((number) -> number)[1].arguments()[0] (nil) is not a supertype of (number?) -> number.arguments()[0][0] (number))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((nil) -> never) & ((number) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> never'; type ((nil) -> never) & ((number) -> number)[0].arguments()[0] (number) is not a supertype of (number?) -> never.arguments()[0][1] (nil)
|
||||
type ((nil) -> never) & ((number) -> number)[0].returns()[0] (number) is not a subtype of (number?) -> never.returns()[0] (never)
|
||||
type ((nil) -> never) & ((number) -> number)[1].arguments()[0] (nil) is not a supertype of (number?) -> never.arguments()[0][0] (number))",
|
||||
toString(result.errors[1])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((nil) -> never) & ((number) -> number)'
|
||||
@ -808,6 +845,7 @@ could not be converted into
|
||||
'(number?) -> never'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_arguments")
|
||||
{
|
||||
@ -830,6 +868,9 @@ could not be converted into
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_overlapping_results_and_variadics")
|
||||
{
|
||||
ScopedFastFlag dcr{
|
||||
FFlag::DebugLuauDeferredConstraintResolution, false
|
||||
}; // CLI-116474 Semantic subtyping of assignments needs to decide how to interpret intersections of functions
|
||||
CheckResult result = check(R"(
|
||||
function f(x : ((string?) -> (string | number)) & ((number?) -> ...number))
|
||||
local y : ((nil) -> (number, number?)) = x -- OK
|
||||
@ -856,12 +897,19 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_1")
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
"Type '(() -> (a...)) & (() -> (b...))' could not be converted into '() -> ()'; none of the intersection parts are compatible"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_2")
|
||||
{
|
||||
@ -874,12 +922,19 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_2")
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
"Type '((a...) -> ()) & ((b...) -> ())' could not be converted into '() -> ()'; none of the intersection parts are compatible"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_3")
|
||||
{
|
||||
@ -892,6 +947,12 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_3")
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'(() -> (a...)) & (() -> (number?, a...))'
|
||||
@ -899,6 +960,7 @@ could not be converted into
|
||||
'() -> number'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_4")
|
||||
{
|
||||
@ -912,11 +974,27 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_4")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((a...) -> ()) & ((number, a...) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> ()'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
'((a...) -> ()) & ((number, a...) -> number)'; at [0].returns(), is not a subtype of number
|
||||
type ((a...) -> ()) & ((number, a...) -> number)[1].arguments().tail() (a...) is not a supertype of ((a...) -> ()) & ((number, a...) -> number)[0].arguments().tail() (a...))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((a...) -> ()) & ((number, a...) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> ()'; none of the intersection parts are compatible)",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "intersect_metatables")
|
||||
|
@ -32,6 +32,13 @@ TEST_CASE_FIXTURE(Fixture, "for_loop")
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
// Luau cannot see that the loop must always run at least once, so we
|
||||
// think that q could be nil.
|
||||
CHECK("number?" == toString(requireType("q")));
|
||||
}
|
||||
else
|
||||
CHECK_EQ(*builtinTypes->numberType, *requireType("q"));
|
||||
}
|
||||
|
||||
@ -107,9 +114,18 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "iteration_regression_issue_69967_alt")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
// It's possible for the loop body to execute 0 times.
|
||||
CHECK("number?" == toString(requireType("x")));
|
||||
CHECK("string?" == toString(requireType("y")));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ("number", toString(requireType("x")));
|
||||
CHECK_EQ("string", toString(requireType("y")));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop")
|
||||
{
|
||||
@ -124,12 +140,23 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop")
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK("number?" == toString(requireType("n")));
|
||||
CHECK("string?" == toString(requireType("s")));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(*builtinTypes->numberType, *requireType("n"));
|
||||
CHECK_EQ(*builtinTypes->stringType, *requireType("s"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next")
|
||||
{
|
||||
// CLI-116494 The generics K and V are leaking out of the next() function somehow.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local n
|
||||
local s
|
||||
@ -240,11 +267,17 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error")
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
||||
TypeId p = requireType("p");
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_EQ("*error-type*?", toString(p));
|
||||
else
|
||||
CHECK_EQ("*error-type*", toString(p));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function")
|
||||
{
|
||||
// We report a spuriouus duplicate error here.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local bad_iter = 5
|
||||
|
||||
@ -259,6 +292,9 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_factory_not_returning_the_right_amount_of_values")
|
||||
{
|
||||
// Spurious duplicate errors
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function hasDivisors(value: number, table)
|
||||
return false
|
||||
@ -308,6 +344,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_factory_not_returning_t
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_error_on_iterator_requiring_args_but_none_given")
|
||||
{
|
||||
// CLI-116496
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function prime_iter(state, index)
|
||||
return 1
|
||||
@ -380,6 +419,9 @@ TEST_CASE_FIXTURE(Fixture, "while_loop")
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("number?" == toString(requireType("i")));
|
||||
else
|
||||
CHECK_EQ(*builtinTypes->numberType, *requireType("i"));
|
||||
}
|
||||
|
||||
@ -394,6 +436,9 @@ TEST_CASE_FIXTURE(Fixture, "repeat_loop")
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("string?" == toString(requireType("i")));
|
||||
else
|
||||
CHECK_EQ(*builtinTypes->stringType, *requireType("i"));
|
||||
}
|
||||
|
||||
@ -490,6 +535,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "properly_infer_iteratee_is_a_free_table")
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
// In the new solver, we infer iter: unknown and so we warn on use of its properties.
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK(get<UnknownProperty>(result.errors[0]));
|
||||
CHECK(Location{{2, 12}, {2, 18}} == result.errors[0].location);
|
||||
}
|
||||
else
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
@ -532,6 +585,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "ipairs_produces_integral_indices")
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("number?" == toString(requireType("key")));
|
||||
else
|
||||
REQUIRE_EQ("number", toString(requireType("key")));
|
||||
}
|
||||
|
||||
@ -639,6 +695,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "unreachable_code_after_infinite_loop")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "loop_typecheck_crash_on_empty_optional")
|
||||
{
|
||||
// CLI-116498 Sometimes you can iterate over tables with no indexers.
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
ScopedFastFlag sff{FFlag::LuauOkWithIteratingOverTableProperties, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -704,6 +764,9 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_basic")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "loop_iter_trailing_nil")
|
||||
{
|
||||
// CLI-116498 Sometimes you can iterate over tables with no indexers.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {string} = {}
|
||||
local extra
|
||||
@ -718,7 +781,11 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_trailing_nil")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer_strict")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauOkWithIteratingOverTableProperties, true};
|
||||
// CLI-116498 Sometimes you can iterate over tables with no indexers.
|
||||
ScopedFastFlag sff[] = {
|
||||
{FFlag::DebugLuauDeferredConstraintResolution, false},
|
||||
{FFlag::LuauOkWithIteratingOverTableProperties, true}
|
||||
};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t = {}
|
||||
@ -742,7 +809,8 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer_nonstrict")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_nil")
|
||||
{
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
// CLI-116499 Free types persisting until typechecking time.
|
||||
if (1 || !FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -757,7 +825,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_nil")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_not_enough_returns")
|
||||
{
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
// CLI-116500
|
||||
if (1 || !FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -778,7 +847,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_not_enough_returns")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_ok")
|
||||
{
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
// CLI-116500
|
||||
if (1 || !FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -794,7 +864,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_ok")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_metamethod_ok_with_inference")
|
||||
{
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
// CLI-116500
|
||||
if (1 || !FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -844,6 +915,10 @@ TEST_CASE_FIXTURE(Fixture, "for_loop_lower_bound_is_string_3")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "cli_68448_iterators_need_not_accept_nil")
|
||||
{
|
||||
// CLI-116500
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function makeEnum(members)
|
||||
local enum = {}
|
||||
@ -975,6 +1050,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_fragmented_keys")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_xpath_candidates")
|
||||
{
|
||||
// CLI-116500
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Instance = {}
|
||||
local function findCandidates(instances: { Instance }, path: { string })
|
||||
@ -1004,11 +1083,18 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_on_never_gives_never")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("never?" == toString(requireType("ans"))); // CLI-114134 egraph simplification. Should just be nil.
|
||||
else
|
||||
CHECK(toString(requireType("ans")) == "never");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "iterate_over_properties")
|
||||
{
|
||||
// CLI-116498 - Sometimes you can iterate over tables with no indexer.
|
||||
ScopedFastFlag sff0{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
ScopedFastFlag sff{FFlag::LuauOkWithIteratingOverTableProperties, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
|
@ -56,12 +56,24 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "require")
|
||||
return {hooty=hooty}
|
||||
)";
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
fileResolver.source["game/B"] = R"(
|
||||
local Hooty = require(game.A)
|
||||
|
||||
local h = 4
|
||||
local i = Hooty.hooty(h)
|
||||
)";
|
||||
}
|
||||
else
|
||||
{
|
||||
fileResolver.source["game/B"] = R"(
|
||||
local Hooty = require(game.A)
|
||||
|
||||
local h -- free!
|
||||
local i = Hooty.hooty(h)
|
||||
)";
|
||||
}
|
||||
|
||||
CheckResult aResult = frontend.check("game/A");
|
||||
dumpErrors(aResult);
|
||||
|
@ -18,6 +18,9 @@ TEST_SUITE_BEGIN("TypeInferOOP");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon")
|
||||
{
|
||||
// CLI-116571 method calls are missing arity checking?
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local someTable = {}
|
||||
|
||||
@ -33,6 +36,9 @@ TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_not_defi
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2")
|
||||
{
|
||||
// CLI-116571 method calls are missing arity checking?
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local someTable = {}
|
||||
|
||||
@ -138,6 +144,9 @@ TEST_CASE_FIXTURE(Fixture, "inferring_hundreds_of_self_calls_should_not_suffocat
|
||||
)");
|
||||
|
||||
ModulePtr module = getMainModule();
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK_GE(80, module->internalTypes.types.size());
|
||||
else
|
||||
CHECK_GE(50, module->internalTypes.types.size());
|
||||
}
|
||||
|
||||
@ -410,10 +419,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cycle_between_object_constructor_and_alias")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "promise_type_error_too_complex" * doctest::timeout(2))
|
||||
{
|
||||
// TODO: LTI changes to function call resolution have rendered this test impossibly slow
|
||||
// shared self should fix it, but there may be other mitigations possible as well
|
||||
REQUIRE(!FFlag::DebugLuauDeferredConstraintResolution);
|
||||
|
||||
frontend.options.retainFullTypeGraphs = false;
|
||||
|
||||
// Used `luau-reduce` tool to extract a minimal reproduction.
|
||||
|
@ -189,10 +189,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_overloaded_multiply_that_is_an_int
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_EQ("Vec3", toString(requireType("a")));
|
||||
CHECK_EQ("Vec3", toString(requireType("b")));
|
||||
CHECK_EQ("Vec3", toString(requireType("c")));
|
||||
CHECK_EQ("Vec3", toString(requireType("d")));
|
||||
CHECK("Vec3" == toString(requireType("a")));
|
||||
CHECK("Vec3" == toString(requireType("b")));
|
||||
CHECK("Vec3" == toString(requireType("c")));
|
||||
CHECK("Vec3" == toString(requireType("d")));
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("mul<Vec3, string>" == toString(requireType("e")));
|
||||
else
|
||||
CHECK_EQ("Vec3", toString(requireType("e")));
|
||||
}
|
||||
|
||||
@ -223,10 +227,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_overloaded_multiply_that_is_an_int
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_EQ("Vec3", toString(requireType("a")));
|
||||
CHECK_EQ("Vec3", toString(requireType("b")));
|
||||
CHECK_EQ("Vec3", toString(requireType("c")));
|
||||
CHECK_EQ("Vec3", toString(requireType("d")));
|
||||
CHECK("Vec3" == toString(requireType("a")));
|
||||
CHECK("Vec3" == toString(requireType("b")));
|
||||
CHECK("Vec3" == toString(requireType("c")));
|
||||
CHECK("Vec3" == toString(requireType("d")));
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("mul<string, Vec3>" == toString(requireType("e")));
|
||||
else
|
||||
CHECK_EQ("Vec3", toString(requireType("e")));
|
||||
}
|
||||
|
||||
@ -477,7 +485,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "compound_assign_result_must_be_compatible_wi
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK(result.errors[0] == TypeError{Location{{13, 8}, {13, 14}}, TypeMismatch{requireType("x"), builtinTypes->numberType}});
|
||||
|
||||
CHECK(Location{{13, 8}, {13, 14}} == result.errors[0].location);
|
||||
|
||||
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
|
||||
REQUIRE(tm);
|
||||
CHECK("x" == toString(tm->wantedType));
|
||||
CHECK("number" == toString(tm->givenType));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "compound_assign_mismatch_metatable")
|
||||
@ -593,6 +607,17 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error")
|
||||
local a = -foo
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
||||
CHECK(get<UninhabitedTypeFunction>(result.errors[0]));
|
||||
|
||||
// This second error is spurious. We should not be reporting it.
|
||||
CHECK(get<TypeMismatch>(result.errors[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_EQ("string", toString(requireType("a")));
|
||||
@ -601,9 +626,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error")
|
||||
REQUIRE_EQ(*tm->wantedType, *builtinTypes->booleanType);
|
||||
// given type is the typeof(foo) which is complex to compare against
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_len_error")
|
||||
{
|
||||
// CLI-116463
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local mt = {}
|
||||
@ -673,8 +702,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "disallow_string_and_types_without_metatables
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK(get<UninhabitedTypeFunction>(result.errors[0]));
|
||||
CHECK(Location{{2, 18}, {2, 30}} == result.errors[0].location);
|
||||
CHECK(get<UninhabitedTypeFunction>(result.errors[1]));
|
||||
CHECK(Location{{8, 18}, {8, 25}} == result.errors[1].location);
|
||||
CHECK(get<UninhabitedTypeFunction>(result.errors[2]));
|
||||
CHECK(Location{{24, 18}, {24, 27}} == result.errors[2].location);
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
|
||||
REQUIRE(tm);
|
||||
REQUIRE_MESSAGE(tm, "Expected a TypeMismatch but got " << result.errors[0]);
|
||||
CHECK_EQ(*tm->wantedType, *builtinTypes->numberType);
|
||||
CHECK_EQ(*tm->givenType, *builtinTypes->stringType);
|
||||
|
||||
@ -690,6 +730,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "disallow_string_and_types_without_metatables
|
||||
CHECK_EQ(*tm2->wantedType, *builtinTypes->numberType);
|
||||
CHECK_EQ(*tm2->givenType, *requireType("foo"));
|
||||
}
|
||||
}
|
||||
|
||||
// CLI-29033
|
||||
TEST_CASE_FIXTURE(Fixture, "unknown_type_in_comparison")
|
||||
@ -712,9 +753,17 @@ TEST_CASE_FIXTURE(Fixture, "concat_op_on_free_lhs_and_string_rhs")
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK("<a>(a) -> concat<a, string>" == toString(requireType("f")));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
REQUIRE(get<CannotInferBinaryOperation>(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "concat_op_on_string_lhs_and_free_rhs")
|
||||
{
|
||||
@ -726,6 +775,9 @@ TEST_CASE_FIXTURE(Fixture, "concat_op_on_string_lhs_and_free_rhs")
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("<a>(a) -> concat<string, a>" == toString(requireType("f")));
|
||||
else
|
||||
CHECK_EQ("(string) -> string", toString(requireType("f")));
|
||||
}
|
||||
|
||||
@ -746,11 +798,10 @@ TEST_CASE_FIXTURE(Fixture, "strict_binary_op_where_lhs_unknown")
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(ops.size(), result);
|
||||
CHECK_EQ(
|
||||
"Type function instance Add<a, b> depends on generic function parameters but does not appear in the function signature; this "
|
||||
"construct cannot be type-checked at this time",
|
||||
"Operator '+' could not be applied to operands of types unknown and unknown; there is no corresponding overload for __add",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
CHECK_EQ("Unknown type used in - operation; consider adding a type annotation to 'a'", toString(result.errors[1]));
|
||||
CHECK_EQ("Operator '-' could not be applied to operands of types unknown and unknown; there is no corresponding overload for __sub", toString(result.errors[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -833,6 +884,9 @@ TEST_CASE_FIXTURE(Fixture, "error_on_invalid_operand_types_to_relational_operato
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "cli_38355_recursive_union")
|
||||
{
|
||||
// There's an extra spurious warning here when the new solver is enabled.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local _
|
||||
@ -846,6 +900,8 @@ TEST_CASE_FIXTURE(Fixture, "cli_38355_recursive_union")
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "UnknownGlobalCompoundAssign")
|
||||
{
|
||||
// In non-strict mode, global definition is still allowed
|
||||
{
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
@ -856,6 +912,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "UnknownGlobalCompoundAssign")
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), "Unknown global 'a'");
|
||||
}
|
||||
}
|
||||
|
||||
// In strict mode we no longer generate two errors from lhs
|
||||
{
|
||||
@ -870,6 +927,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "UnknownGlobalCompoundAssign")
|
||||
}
|
||||
|
||||
// In non-strict mode, compound assignment is not a definition, it's a modification
|
||||
{
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
@ -881,6 +940,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "UnknownGlobalCompoundAssign")
|
||||
CHECK_EQ(toString(result.errors[0]), "Unknown global 'a'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "strip_nil_from_lhs_or_operator")
|
||||
{
|
||||
@ -1233,7 +1293,8 @@ TEST_CASE_FIXTURE(Fixture, "unrelated_primitives_cannot_be_compared")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "mm_comparisons_must_return_a_boolean")
|
||||
{
|
||||
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||
// CLI-115687
|
||||
if (1 || !FFlag::DebugLuauDeferredConstraintResolution)
|
||||
return;
|
||||
|
||||
CheckResult result = check(R"(
|
||||
@ -1286,7 +1347,7 @@ local w = c and 1
|
||||
CHECK("boolean | number" == toString(requireType("z"))); // 'false' widened to boolean
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("((false?) & unknown) | number" == toString(requireType("w")));
|
||||
CHECK("number?" == toString(requireType("w")));
|
||||
else
|
||||
CHECK("(boolean | number)?" == toString(requireType("w")));
|
||||
}
|
||||
@ -1364,6 +1425,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_is_array_simplified")
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_is_array")
|
||||
{
|
||||
// CLI-116480 Subtyping bug: table should probably be a subtype of {[unknown]: unknown}
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
return function(value: any): boolean
|
||||
@ -1536,4 +1600,17 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_infinite_expansion_of_free_type" * doctes
|
||||
// just type-checking this code is enough
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "compound_operator_on_upvalue")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local byteCursor: number = 0
|
||||
|
||||
local function advance(bytes: number)
|
||||
byteCursor += bytes
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -73,7 +73,7 @@ TEST_CASE_FIXTURE(Fixture, "string_function_indirect")
|
||||
CHECK_EQ(*requireType("p"), *builtinTypes->stringType);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "CheckMethodsOfNumber")
|
||||
TEST_CASE_FIXTURE(Fixture, "check_methods_of_number")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
local x: number = 9999
|
||||
@ -83,9 +83,18 @@ end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
CHECK("Expected type table, got 'number' instead" == toString(result.errors[0]));
|
||||
CHECK("Type 'number' could not be converted into 'string'" == toString(result.errors[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(toString(result.errors[0]), "Cannot add method to non-table type 'number'");
|
||||
CHECK_EQ(toString(result.errors[1]), "Type 'number' could not be converted into 'string'");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("singleton_types")
|
||||
{
|
||||
|
@ -2252,4 +2252,67 @@ function(obj)
|
||||
end
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "more_complex_long_disjunction_of_refinements_shouldnt_trip_ice")
|
||||
{
|
||||
CHECK_NOTHROW(check(R"(
|
||||
script:connect(function(obj)
|
||||
if script.Parent.SeatNumber.Value == "1D" or
|
||||
script.Parent.SeatNumber.Value == "2D" or
|
||||
script.Parent.SeatNumber.Value == "3D" or
|
||||
script.Parent.SeatNumber.Value == "4D" or
|
||||
script.Parent.SeatNumber.Value == "5D" or
|
||||
script.Parent.SeatNumber.Value == "6D" or
|
||||
script.Parent.SeatNumber.Value == "7D" or
|
||||
script.Parent.SeatNumber.Value == "8D" or
|
||||
script.Parent.SeatNumber.Value == "9D" or
|
||||
script.Parent.SeatNumber.Value == "10D" or
|
||||
script.Parent.SeatNumber.Value == "11D" or
|
||||
script.Parent.SeatNumber.Value == "12D" or
|
||||
script.Parent.SeatNumber.Value == "13D" or
|
||||
script.Parent.SeatNumber.Value == "14D" or
|
||||
script.Parent.SeatNumber.Value == "15D" or
|
||||
script.Parent.SeatNumber.Value == "16D" or
|
||||
script.Parent.SeatNumber.Value == "1C" or
|
||||
script.Parent.SeatNumber.Value == "2C" or
|
||||
script.Parent.SeatNumber.Value == "3C" or
|
||||
script.Parent.SeatNumber.Value == "4C" or
|
||||
script.Parent.SeatNumber.Value == "5C" or
|
||||
script.Parent.SeatNumber.Value == "6C" or
|
||||
script.Parent.SeatNumber.Value == "7C" or
|
||||
script.Parent.SeatNumber.Value == "8C" or
|
||||
script.Parent.SeatNumber.Value == "9C" or
|
||||
script.Parent.SeatNumber.Value == "10C" or
|
||||
script.Parent.SeatNumber.Value == "11C" or
|
||||
script.Parent.SeatNumber.Value == "12C" or
|
||||
script.Parent.SeatNumber.Value == "13C" or
|
||||
script.Parent.SeatNumber.Value == "14C" or
|
||||
script.Parent.SeatNumber.Value == "15C" or
|
||||
script.Parent.SeatNumber.Value == "16C" then
|
||||
end)
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "refinements_should_avoid_building_up_big_intersect_families")
|
||||
{
|
||||
CHECK_NOTHROW(check(R"(
|
||||
script:connect(function(obj)
|
||||
if script.Parent.SeatNumber.Value == "1D" or script.Parent.SeatNumber.Value == "2D" or script.Parent.SeatNumber.Value == "3D" or script.Parent.SeatNumber.Value == "4D" or script.Parent.SeatNumber.Value == "5D" or script.Parent.SeatNumber.Value == "6D" or script.Parent.SeatNumber.Value == "7D" or script.Parent.SeatNumber.Value == "8D" or script.Parent.SeatNumber.Value == "9D" or script.Parent.SeatNumber.Value == "10D" or script.Parent.SeatNumber.Value == "11D" or script.Parent.SeatNumber.Value == "12D" or script.Parent.SeatNumber.Value == "13D" or script.Parent.SeatNumber.Value == "14D" or script.Parent.SeatNumber.Value == "15D" or script.Parent.SeatNumber.Value == "16D" or script.Parent.SeatNumber.Value == "1C" or script.Parent.SeatNumber.Value == "2C" or script.Parent.SeatNumber.Value == "3C" or script.Parent.SeatNumber.Value == "4C" or script.Parent.SeatNumber.Value == "5C" or script.Parent.SeatNumber.Value == "6C" or script.Parent.SeatNumber.Value == "7C" or script.Parent.SeatNumber.Value == "8C" or script.Parent.SeatNumber.Value == "9C" or script.Parent.SeatNumber.Value == "10C" or script.Parent.SeatNumber.Value == "11C" or script.Parent.SeatNumber.Value == "12C" or script.Parent.SeatNumber.Value == "13C" or script.Parent.SeatNumber.Value == "14C" or script.Parent.SeatNumber.Value == "15C" or script.Parent.SeatNumber.Value == "16C" then
|
||||
if p.Name == script.Parent.Parent.Parent.Parent.Parent.Parent.MainParts.CD.SurfaceGui[script.Parent.SeatNumber.Value].Player.Value or script.Parent.Parent.Parent.Parent.Parent.Parent.MainParts.CD.SurfaceGui[script.Parent.SeatNumber.Value].Player.Value == "" then
|
||||
else
|
||||
if script.Parent:FindFirstChild("SeatWeld") then
|
||||
end
|
||||
end
|
||||
else
|
||||
if p.Name == script.Parent.Parent.Parent.Parent.Parent.Parent.MainParts.AB.SurfaceGui[script.Parent.SeatNumber.Value].Player.Value or script.Parent.Parent.Parent.Parent.Parent.Parent.MainParts.AB.SurfaceGui[script.Parent.SeatNumber.Value].Player.Value == "" then
|
||||
print("Allowed")
|
||||
else
|
||||
if script.Parent:FindFirstChild("SeatWeld") then
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
@ -153,6 +153,8 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons_mismatch")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(g: ((true, string) -> ()) & ((false, number) -> ()))
|
||||
g(true, 37)
|
||||
@ -192,6 +194,10 @@ TEST_CASE_FIXTURE(Fixture, "enums_using_singletons_mismatch")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("Type '\"bang\"' could not be converted into '\"bar\" | \"baz\" | \"foo\"'" == toString(result.errors[0]));
|
||||
else
|
||||
CHECK_EQ(
|
||||
"Type '\"bang\"' could not be converted into '\"bar\" | \"baz\" | \"foo\"'; none of the union options are compatible",
|
||||
toString(result.errors[0])
|
||||
@ -337,6 +343,14 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK(
|
||||
"Type\n"
|
||||
" '{ [\"\\n\"]: number }'\n"
|
||||
"could not be converted into\n"
|
||||
" '{ [\"<>\"]: number }'" == toString(result.errors[0])
|
||||
);
|
||||
else
|
||||
CHECK_EQ(
|
||||
R"(Table type '{ ["\n"]: number }' not compatible with type '{| ["<>"]: number |}' because the former is missing field '<>')",
|
||||
toString(result.errors[0])
|
||||
@ -354,12 +368,17 @@ local a: Animal = { tag = 'cat', cafood = 'something' }
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("Type '{ cafood: string, tag: \"cat\" }' could not be converted into 'Cat | Dog'" == toString(result.errors[0]));
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'a' could not be converted into 'Cat | Dog'
|
||||
caused by:
|
||||
None of the union options are compatible. For example:
|
||||
Table type 'a' not compatible with type 'Cat' because the former is missing field 'catfood')";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_tagged_union_mismatch_bool")
|
||||
{
|
||||
@ -372,12 +391,17 @@ local a: Result = { success = false, result = 'something' }
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("Type '{ result: string, success: boolean }' could not be converted into 'Bad | Good'" == toString(result.errors[0]));
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'a' could not be converted into 'Bad | Good'
|
||||
caused by:
|
||||
None of the union options are compatible. For example:
|
||||
Table type 'a' not compatible with type 'Bad' because the former is missing field 'error')";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parametric_tagged_union_alias")
|
||||
{
|
||||
@ -418,6 +442,8 @@ local a: Animal = if true then { tag = 'cat', catfood = 'something' } else { tag
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "widen_the_supertype_if_it_is_free_and_subtype_has_singleton")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function foo(f, x)
|
||||
if x == "hi" then
|
||||
@ -427,7 +453,7 @@ TEST_CASE_FIXTURE(Fixture, "widen_the_supertype_if_it_is_free_and_subtype_has_si
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
LUAU_CHECK_NO_ERRORS(result);
|
||||
|
||||
CHECK_EQ(R"("hi")", toString(requireTypeAtPosition({3, 18})));
|
||||
// should be <a...>((string) -> a..., string) -> () but needs lower bounds calculation
|
||||
@ -436,6 +462,8 @@ TEST_CASE_FIXTURE(Fixture, "widen_the_supertype_if_it_is_free_and_subtype_has_si
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "return_type_of_f_is_not_widened")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function foo(f, x): "hello"? -- anyone there?
|
||||
return if x == "hi"
|
||||
|
@ -224,6 +224,8 @@ TEST_CASE_FIXTURE(Fixture, "statements_are_topologically_sorted")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "unify_nearly_identical_recursive_types")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local o
|
||||
o:method()
|
||||
@ -263,6 +265,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "weird_case")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_ice_when_failing_the_occurs_check")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local s
|
||||
@ -380,6 +384,8 @@ TEST_CASE_FIXTURE(Fixture, "exponential_blowup_from_copying_types")
|
||||
// checker. We also want it to somewhat match up with production values, so we push up the parser recursion limit a little bit instead.
|
||||
TEST_CASE_FIXTURE(Fixture, "check_type_infer_recursion_count")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
#if defined(LUAU_ENABLE_ASAN)
|
||||
int limit = 250;
|
||||
#elif defined(_DEBUG) || defined(_NOOPT)
|
||||
@ -435,6 +441,9 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "globals")
|
||||
{
|
||||
// The new solver does not permit assignments to globals like this.
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
foo = true
|
||||
@ -447,6 +456,8 @@ TEST_CASE_FIXTURE(Fixture, "globals")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "globals2")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
foo = function() return 1 end
|
||||
@ -495,6 +506,8 @@ TEST_CASE_FIXTURE(Fixture, "correctly_scope_locals_do")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "checking_should_not_ice")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CHECK_NOTHROW(check(R"(
|
||||
--!nonstrict
|
||||
f,g = ...
|
||||
@ -587,6 +600,8 @@ TEST_CASE_FIXTURE(Fixture, "tc_after_error_recovery_no_assert")
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "tc_after_error_recovery_no_replacement_name_in_error")
|
||||
{
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local t = { x = 10, y = 20 }
|
||||
@ -607,6 +622,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tc_after_error_recovery_no_replacement_name_
|
||||
}
|
||||
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
function string.() end
|
||||
@ -680,6 +697,8 @@ TEST_CASE_FIXTURE(Fixture, "cli_39932_use_unifier_in_ensure_methods")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstStatError")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
foo
|
||||
)");
|
||||
@ -689,6 +708,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstStatError")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "dont_report_type_errors_within_an_AstExprError")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local a = foo:
|
||||
)");
|
||||
@ -738,6 +759,9 @@ TEST_CASE_FIXTURE(Fixture, "no_stack_overflow_from_isoptional")
|
||||
std::optional<TypeId> t0 = lookupType("t0");
|
||||
REQUIRE(t0);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("any" == toString(*t0));
|
||||
else
|
||||
CHECK_EQ("*error-type*", toString(*t0));
|
||||
|
||||
auto it = std::find_if(
|
||||
@ -1075,6 +1099,8 @@ end
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
--!nolint
|
||||
@ -1150,6 +1176,10 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_no_ice")
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("Type contains a self-recursive construct that cannot be resolved" == toString(result.errors[0]));
|
||||
else
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
||||
}
|
||||
|
||||
@ -1168,6 +1198,10 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer")
|
||||
REQUIRE_MESSAGE(!result.errors.empty(), getErrors(result));
|
||||
|
||||
CHECK(1 == result.errors.size());
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK(Location{{3, 22}, {3, 42}} == result.errors[0].location);
|
||||
else
|
||||
CHECK(Location{{3, 12}, {3, 46}} == result.errors[0].location);
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
||||
}
|
||||
@ -1187,6 +1221,9 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_cache_limit_normalizer")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "follow_on_new_types_in_substitution")
|
||||
{
|
||||
// CLI-114134
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local obj = {}
|
||||
|
||||
@ -1355,6 +1392,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_function_that_invokes_itself_with_
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("(unknown) -> ()" == toString(requireType("readValue")));
|
||||
else
|
||||
CHECK("<a>(a) -> ()" == toString(requireType("readValue")));
|
||||
}
|
||||
|
||||
@ -1368,6 +1408,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "recursive_function_that_invokes_itself_with_
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
CHECK("(unknown) -> ()" == toString(requireType("readValue")));
|
||||
else
|
||||
CHECK("(number) -> ()" == toString(requireType("readValue")));
|
||||
}
|
||||
|
||||
@ -1520,6 +1563,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "lti_must_record_contributing_locations")
|
||||
*/
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "be_sure_to_use_active_txnlog_when_evaluating_a_variadic_overload")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function concat<T>(target: {T}, ...: {T} | T): {T}
|
||||
return (nil :: any) :: {T}
|
||||
|
@ -154,6 +154,8 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_intersection_sub_anything")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_never")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(arg : { prop : string & number }) : never
|
||||
return arg
|
||||
@ -164,6 +166,8 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_never")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_anything")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(arg : { prop : string & number }) : boolean
|
||||
return arg
|
||||
@ -174,6 +178,8 @@ TEST_CASE_FIXTURE(Fixture, "uninhabited_table_sub_anything")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "members_of_failed_typepack_unification_are_unified_with_errorType")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(arg: number) end
|
||||
local a
|
||||
@ -189,6 +195,8 @@ TEST_CASE_FIXTURE(Fixture, "members_of_failed_typepack_unification_are_unified_w
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "result_of_failed_typepack_unification_is_constrained")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(arg: number) return arg end
|
||||
local a
|
||||
|
@ -787,6 +787,8 @@ local d: Y<number, string, ...boolean, ...() -> ()>
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Y<T = T> = { a: T }
|
||||
local a: Y = { a = 2 }
|
||||
@ -794,39 +796,58 @@ local a: Y = { a = 2 }
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), "Unknown type 'T'");
|
||||
}
|
||||
|
||||
result = check(R"(
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors2")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
type Y<T... = T...> = { a: (T...) -> () }
|
||||
local a: Y<>
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), "Unknown type 'T'");
|
||||
}
|
||||
|
||||
result = check(R"(
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors3")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Y<T = string, U... = ...string> = { a: (T) -> U... }
|
||||
local a: Y<...number>
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), "Generic type 'Y<T, U...>' expects at least 1 type argument, but none are specified");
|
||||
}
|
||||
|
||||
result = check(R"(
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors4")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Packed<T> = (T) -> T
|
||||
local a: Packed
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(toString(result.errors[0]), "Type parameter list is required");
|
||||
}
|
||||
|
||||
result = check(R"(
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors5")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
type Y<T, U = T, V> = { a: T }
|
||||
local a: Y<number>
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
}
|
||||
|
||||
result = check(R"(
|
||||
TEST_CASE_FIXTURE(Fixture, "type_alias_default_type_errors6")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
type Y<T..., U... = T..., V...> = { a: T }
|
||||
local a: Y<...number>
|
||||
)");
|
||||
@ -929,6 +950,19 @@ a = b
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type\n"
|
||||
" '() -> (number, ...boolean)'\n"
|
||||
"could not be converted into\n"
|
||||
" '() -> (number, ...string)'; at returns().tail().variadic(), boolean is not a subtype of string";
|
||||
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'() -> (number, ...boolean)'
|
||||
could not be converted into
|
||||
@ -937,6 +971,7 @@ caused by:
|
||||
Type 'boolean' could not be converted into 'string')";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: File a Jira about this
|
||||
/*
|
||||
@ -1030,6 +1065,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "detect_cyclic_typepacks2")
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function foo(...: string): number
|
||||
return 1
|
||||
|
@ -121,11 +121,11 @@ TEST_CASE_FIXTURE(Fixture, "type_packs_containing_never_is_itself_uninhabitable"
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ("Function only returns 2 values, but 3 are required here", toString(result.errors[0]));
|
||||
CHECK("Function only returns 2 values, but 3 are required here" == toString(result.errors[0]));
|
||||
|
||||
CHECK_EQ("string", toString(requireType("x")));
|
||||
CHECK_EQ("never", toString(requireType("y")));
|
||||
CHECK_EQ("*error-type*", toString(requireType("z")));
|
||||
CHECK("string" == toString(requireType("x")));
|
||||
CHECK("never" == toString(requireType("y")));
|
||||
CHECK("nil" == toString(requireType("z")));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -335,9 +335,21 @@ TEST_CASE_FIXTURE(Fixture, "math_operators_and_never")
|
||||
end
|
||||
)");
|
||||
|
||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK(get<ExplicitFunctionAnnotationRecommended>(result.errors[0]));
|
||||
|
||||
// CLI-114134 Egraph-based simplification.
|
||||
// CLI-116549 x ~= nil : false when x : nil
|
||||
CHECK("<a>(nil, a) -> and<boolean, mul<nil & ~nil, a>>" == toString(requireType("mul")));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("<a>(nil, a) -> boolean", toString(requireType("mul")));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "compare_never")
|
||||
{
|
||||
|
@ -1,26 +1,9 @@
|
||||
AstQuery.last_argument_function_call_type
|
||||
AutocompleteTest.anonymous_autofilled_generic_on_argument_type_pack_vararg
|
||||
AutocompleteTest.anonymous_autofilled_generic_type_pack_vararg
|
||||
AutocompleteTest.autocomplete_string_singletons
|
||||
AutocompleteTest.string_singleton_as_table_key
|
||||
AutocompleteTest.suggest_table_keys
|
||||
AutocompleteTest.type_correct_suggestion_for_overloads
|
||||
AutocompleteTest.type_correct_suggestion_in_table
|
||||
BuiltinTests.aliased_string_format
|
||||
BuiltinTests.assert_removes_falsy_types_even_from_type_pack_tail_but_only_for_the_first_type
|
||||
BuiltinTests.assert_returns_false_and_string_iff_it_knows_the_first_argument_cannot_be_truthy
|
||||
BuiltinTests.coroutine_resume_anything_goes
|
||||
BuiltinTests.select_slightly_out_of_range
|
||||
BuiltinTests.select_way_out_of_range
|
||||
BuiltinTests.select_with_variadic_typepack_tail_and_string_head
|
||||
BuiltinTests.set_metatable_needs_arguments
|
||||
BuiltinTests.setmetatable_should_not_mutate_persisted_types
|
||||
BuiltinTests.sort_with_bad_predicate
|
||||
BuiltinTests.string_format_as_method
|
||||
BuiltinTests.string_format_correctly_ordered_types
|
||||
BuiltinTests.string_format_report_all_type_errors_at_correct_positions
|
||||
BuiltinTests.string_format_use_correct_argument2
|
||||
BuiltinTests.table_freeze_is_generic
|
||||
GenericsTests.do_not_always_instantiate_generic_intersection_types
|
||||
GenericsTests.error_detailed_function_mismatch_generic_pack
|
||||
GenericsTests.error_detailed_function_mismatch_generic_types
|
||||
@ -47,174 +30,16 @@ IntersectionTypes.overloaded_functions_mentioning_generic_packs
|
||||
IntersectionTypes.overloaded_functions_mentioning_generics
|
||||
IntersectionTypes.overloaded_functions_returning_intersections
|
||||
IntersectionTypes.overloadeded_functions_with_never_arguments
|
||||
IntersectionTypes.overloadeded_functions_with_never_result
|
||||
IntersectionTypes.overloadeded_functions_with_overlapping_results_and_variadics
|
||||
IntersectionTypes.overloadeded_functions_with_unknown_arguments
|
||||
IntersectionTypes.overloadeded_functions_with_unknown_result
|
||||
IntersectionTypes.overloadeded_functions_with_weird_typepacks_1
|
||||
IntersectionTypes.overloadeded_functions_with_weird_typepacks_2
|
||||
IntersectionTypes.overloadeded_functions_with_weird_typepacks_3
|
||||
IntersectionTypes.overloadeded_functions_with_weird_typepacks_4
|
||||
IntersectionTypes.table_write_sealed_indirect
|
||||
IntersectionTypes.union_saturate_overloaded_functions
|
||||
Linter.TableOperationsIndexer
|
||||
ModuleTests.clone_self_property
|
||||
Negations.cofinite_strings_can_be_compared_for_equality
|
||||
Normalize.higher_order_function_with_annotation
|
||||
Normalize.negations_of_tables
|
||||
Normalize.specific_functions_cannot_be_negated
|
||||
RefinementTest.refine_a_param_that_got_resolved_during_constraint_solving_stage
|
||||
RefinementTest.x_as_any_if_x_is_instance_elseif_x_is_table
|
||||
RefinementTest.x_is_not_instance_or_else_not_part
|
||||
TryUnifyTests.members_of_failed_typepack_unification_are_unified_with_errorType
|
||||
TryUnifyTests.result_of_failed_typepack_unification_is_constrained
|
||||
TryUnifyTests.uninhabited_table_sub_anything
|
||||
TryUnifyTests.uninhabited_table_sub_never
|
||||
TypeAliases.dont_lose_track_of_PendingExpansionTypes_after_substitution
|
||||
TypeAliases.generic_param_remap
|
||||
TypeAliases.mismatched_generic_type_param
|
||||
TypeAliases.mutually_recursive_generic_aliases
|
||||
TypeAliases.mutually_recursive_types_restriction_not_ok_1
|
||||
TypeAliases.mutually_recursive_types_restriction_not_ok_2
|
||||
TypeAliases.mutually_recursive_types_swapsies_not_ok
|
||||
TypeAliases.recursive_types_restriction_not_ok
|
||||
TypeAliases.report_shadowed_aliases
|
||||
TypeAliases.type_alias_local_mutation
|
||||
TypeAliases.type_alias_local_rename
|
||||
TypeAliases.type_alias_of_an_imported_recursive_generic_type
|
||||
TypeInfer.be_sure_to_use_active_txnlog_when_evaluating_a_variadic_overload
|
||||
TypeInfer.check_type_infer_recursion_count
|
||||
TypeInfer.checking_should_not_ice
|
||||
TypeInfer.cli_50041_committing_txnlog_in_apollo_client_error
|
||||
TypeInfer.dont_ice_when_failing_the_occurs_check
|
||||
TypeInfer.dont_report_type_errors_within_an_AstExprError
|
||||
TypeInfer.dont_report_type_errors_within_an_AstStatError
|
||||
TypeInfer.follow_on_new_types_in_substitution
|
||||
TypeInfer.globals
|
||||
TypeInfer.globals2
|
||||
TypeInfer.infer_through_group_expr
|
||||
TypeInfer.no_stack_overflow_from_isoptional
|
||||
TypeInfer.recursive_function_that_invokes_itself_with_a_refinement_of_its_parameter
|
||||
TypeInfer.recursive_function_that_invokes_itself_with_a_refinement_of_its_parameter_2
|
||||
TypeInfer.tc_after_error_recovery_no_replacement_name_in_error
|
||||
TypeInfer.type_infer_recursion_limit_no_ice
|
||||
TypeInfer.type_infer_recursion_limit_normalizer
|
||||
TypeInfer.unify_nearly_identical_recursive_types
|
||||
TypeInferClasses.cannot_unify_class_instance_with_primitive
|
||||
TypeInferClasses.class_type_mismatch_with_name_conflict
|
||||
TypeInferClasses.detailed_class_unification_error
|
||||
TypeInferClasses.indexable_classes
|
||||
TypeInferClasses.table_class_unification_reports_sane_errors_for_missing_properties
|
||||
TypeInferClasses.we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class
|
||||
TypeInferFunctions.another_other_higher_order_function
|
||||
TypeInferFunctions.bidirectional_checking_of_callback_property
|
||||
TypeInferFunctions.calling_function_with_anytypepack_doesnt_leak_free_types
|
||||
TypeInferFunctions.complicated_return_types_require_an_explicit_annotation
|
||||
TypeInferFunctions.concrete_functions_are_not_supertypes_of_function
|
||||
TypeInferFunctions.dont_assert_when_the_tarjan_limit_is_exceeded_during_generalization
|
||||
TypeInferFunctions.dont_give_other_overloads_message_if_only_one_argument_matching_overload_exists
|
||||
TypeInferFunctions.dont_infer_parameter_types_for_functions_from_their_call_site
|
||||
TypeInferFunctions.duplicate_functions_with_different_signatures_not_allowed_in_nonstrict
|
||||
TypeInferFunctions.error_detailed_function_mismatch_arg
|
||||
TypeInferFunctions.error_detailed_function_mismatch_arg_count
|
||||
TypeInferFunctions.error_detailed_function_mismatch_ret
|
||||
TypeInferFunctions.error_detailed_function_mismatch_ret_count
|
||||
TypeInferFunctions.error_detailed_function_mismatch_ret_mult
|
||||
TypeInferFunctions.function_cast_error_uses_correct_language
|
||||
TypeInferFunctions.function_decl_non_self_sealed_overwrite_2
|
||||
TypeInferFunctions.function_decl_non_self_unsealed_overwrite
|
||||
TypeInferFunctions.function_does_not_return_enough_values
|
||||
TypeInferFunctions.function_exprs_are_generalized_at_signature_scope_not_enclosing
|
||||
TypeInferFunctions.function_is_supertype_of_concrete_functions
|
||||
TypeInferFunctions.function_statement_sealed_table_assignment_through_indexer
|
||||
TypeInferFunctions.generic_packs_are_not_variadic
|
||||
TypeInferFunctions.higher_order_function_4
|
||||
TypeInferFunctions.improved_function_arg_mismatch_error_nonstrict
|
||||
TypeInferFunctions.improved_function_arg_mismatch_errors
|
||||
TypeInferFunctions.infer_anonymous_function_arguments
|
||||
TypeInferFunctions.infer_anonymous_function_arguments_outside_call
|
||||
TypeInferFunctions.infer_generic_function_function_argument
|
||||
TypeInferFunctions.infer_generic_function_function_argument_overloaded
|
||||
TypeInferFunctions.infer_return_value_type
|
||||
TypeInferFunctions.inferred_higher_order_functions_are_quantified_at_the_right_time3
|
||||
TypeInferFunctions.instantiated_type_packs_must_have_a_non_null_scope
|
||||
TypeInferFunctions.list_only_alternative_overloads_that_match_argument_count
|
||||
TypeInferFunctions.luau_subtyping_is_np_hard
|
||||
TypeInferFunctions.no_lossy_function_type
|
||||
TypeInferFunctions.occurs_check_failure_in_function_return_type
|
||||
TypeInferFunctions.other_things_are_not_related_to_function
|
||||
TypeInferFunctions.param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible
|
||||
TypeInferFunctions.param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible_2
|
||||
TypeInferFunctions.report_exiting_without_return_nonstrict
|
||||
TypeInferFunctions.return_type_by_overload
|
||||
TypeInferFunctions.simple_unannotated_mutual_recursion
|
||||
TypeInferFunctions.too_few_arguments_variadic
|
||||
TypeInferFunctions.too_few_arguments_variadic_generic
|
||||
TypeInferFunctions.too_few_arguments_variadic_generic2
|
||||
TypeInferFunctions.too_many_arguments
|
||||
TypeInferFunctions.too_many_return_values_in_parentheses
|
||||
TypeInferFunctions.too_many_return_values_no_function
|
||||
TypeInferFunctions.unifier_should_not_bind_free_types
|
||||
TypeInferLoops.cli_68448_iterators_need_not_accept_nil
|
||||
TypeInferLoops.dcr_iteration_on_never_gives_never
|
||||
TypeInferLoops.dcr_xpath_candidates
|
||||
TypeInferLoops.for_in_loop
|
||||
TypeInferLoops.for_in_loop_error_on_factory_not_returning_the_right_amount_of_values
|
||||
TypeInferLoops.for_in_loop_error_on_iterator_requiring_args_but_none_given
|
||||
TypeInferLoops.for_in_loop_on_error
|
||||
TypeInferLoops.for_in_loop_on_non_function
|
||||
TypeInferLoops.for_in_loop_with_next
|
||||
TypeInferLoops.for_loop
|
||||
TypeInferLoops.ipairs_produces_integral_indices
|
||||
TypeInferLoops.iterate_over_properties
|
||||
TypeInferLoops.iteration_regression_issue_69967_alt
|
||||
TypeInferLoops.loop_iter_metamethod_nil
|
||||
TypeInferLoops.loop_iter_metamethod_not_enough_returns
|
||||
TypeInferLoops.loop_iter_metamethod_ok
|
||||
TypeInferLoops.loop_iter_metamethod_ok_with_inference
|
||||
TypeInferLoops.loop_iter_no_indexer_strict
|
||||
TypeInferLoops.loop_iter_trailing_nil
|
||||
TypeInferLoops.loop_typecheck_crash_on_empty_optional
|
||||
TypeInferLoops.properly_infer_iteratee_is_a_free_table
|
||||
TypeInferLoops.repeat_loop
|
||||
TypeInferLoops.while_loop
|
||||
TypeInferModules.require
|
||||
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2
|
||||
TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon
|
||||
TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory
|
||||
TypeInferOOP.promise_type_error_too_complex
|
||||
TypeInferOperators.cli_38355_recursive_union
|
||||
TypeInferOperators.compound_assign_result_must_be_compatible_with_var
|
||||
TypeInferOperators.concat_op_on_free_lhs_and_string_rhs
|
||||
TypeInferOperators.concat_op_on_string_lhs_and_free_rhs
|
||||
TypeInferOperators.disallow_string_and_types_without_metatables_from_arithmetic_binary_ops
|
||||
TypeInferOperators.luau_polyfill_is_array
|
||||
TypeInferOperators.mm_comparisons_must_return_a_boolean
|
||||
TypeInferOperators.reworked_and
|
||||
TypeInferOperators.strict_binary_op_where_lhs_unknown
|
||||
TypeInferOperators.typecheck_overloaded_multiply_that_is_an_intersection
|
||||
TypeInferOperators.typecheck_overloaded_multiply_that_is_an_intersection_on_rhs
|
||||
TypeInferOperators.typecheck_unary_len_error
|
||||
TypeInferOperators.typecheck_unary_minus_error
|
||||
TypeInferOperators.UnknownGlobalCompoundAssign
|
||||
TypeInferPrimitives.CheckMethodsOfNumber
|
||||
TypeInferUnknownNever.assign_to_local_which_is_never
|
||||
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_never
|
||||
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_sorta_never
|
||||
TypeInferUnknownNever.length_of_never
|
||||
TypeInferUnknownNever.math_operators_and_never
|
||||
TypeInferUnknownNever.type_packs_containing_never_is_itself_uninhabitable
|
||||
TypePackTests.fuzz_typepack_iter_follow_2
|
||||
TypePackTests.pack_tail_unification_check
|
||||
TypePackTests.type_alias_backwards_compatible
|
||||
TypePackTests.type_alias_default_type_errors
|
||||
TypePackTests.unify_variadic_tails_in_arguments
|
||||
TypeSingletons.enums_using_singletons_mismatch
|
||||
TypeSingletons.error_detailed_tagged_union_mismatch_bool
|
||||
TypeSingletons.error_detailed_tagged_union_mismatch_string
|
||||
TypeSingletons.overloaded_function_call_with_singletons_mismatch
|
||||
TypeSingletons.return_type_of_f_is_not_widened
|
||||
TypeSingletons.table_properties_type_error_escapes
|
||||
TypeSingletons.widen_the_supertype_if_it_is_free_and_subtype_has_singleton
|
||||
TypeStatesTest.typestates_preserve_error_suppression_properties
|
||||
VisitType.throw_when_limit_is_exceeded
|
||||
|
Loading…
Reference in New Issue
Block a user