mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Sync to upstream/release/612 (#1162)
New solver * Fix bugs where bidirectional type inference would fail to take effect at the proper stage. * Improve inference of mutually recursive functions * Fix crashes --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
67ce75e870
commit
d6c2472f0c
@ -102,6 +102,7 @@ struct FunctionCheckConstraint
|
|||||||
TypePackId argsPack;
|
TypePackId argsPack;
|
||||||
|
|
||||||
class AstExprCall* callSite = nullptr;
|
class AstExprCall* callSite = nullptr;
|
||||||
|
NotNull<DenseHashMap<const AstExpr*, TypeId>> astExpectedTypes;
|
||||||
};
|
};
|
||||||
|
|
||||||
// prim FreeType ExpectedType PrimitiveType
|
// prim FreeType ExpectedType PrimitiveType
|
||||||
|
@ -138,6 +138,53 @@ void forEachConstraint(const Checkpoint& start, const Checkpoint& end, const Con
|
|||||||
f(cg->constraints[i]);
|
f(cg->constraints[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct HasFreeType : TypeOnceVisitor
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
HasFreeType()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty) override
|
||||||
|
{
|
||||||
|
if (result || ty->persistent)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypePackId tp) override
|
||||||
|
{
|
||||||
|
if (result)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const ClassType&) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const FreeType&) override
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypePackId ty, const FreeTypePack&) override
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool hasFreeType(TypeId ty)
|
||||||
|
{
|
||||||
|
HasFreeType hft{};
|
||||||
|
hft.traverse(ty);
|
||||||
|
return hft.result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ConstraintGenerator::ConstraintGenerator(ModulePtr module, NotNull<Normalizer> normalizer, NotNull<ModuleResolver> moduleResolver,
|
ConstraintGenerator::ConstraintGenerator(ModulePtr module, NotNull<Normalizer> normalizer, NotNull<ModuleResolver> moduleResolver,
|
||||||
@ -229,6 +276,8 @@ std::optional<TypeId> ConstraintGenerator::lookup(const ScopePtr& scope, DefId d
|
|||||||
{
|
{
|
||||||
if (auto found = scope->lookup(def))
|
if (auto found = scope->lookup(def))
|
||||||
return *found;
|
return *found;
|
||||||
|
else if (phi->operands.size() == 1)
|
||||||
|
return lookup(scope, phi->operands[0], prototype);
|
||||||
else if (!prototype)
|
else if (!prototype)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
@ -837,6 +886,10 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
|
|||||||
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
||||||
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->func->location};
|
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->func->location};
|
||||||
|
|
||||||
|
bool sigFullyDefined = !hasFreeType(sig.signature);
|
||||||
|
if (sigFullyDefined)
|
||||||
|
asMutable(functionType)->ty.emplace<BoundType>(sig.signature);
|
||||||
|
|
||||||
DefId def = dfg->getDef(function->name);
|
DefId def = dfg->getDef(function->name);
|
||||||
scope->lvalueTypes[def] = functionType;
|
scope->lvalueTypes[def] = functionType;
|
||||||
scope->rvalueRefinements[def] = functionType;
|
scope->rvalueRefinements[def] = functionType;
|
||||||
@ -847,25 +900,32 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
|
|||||||
checkFunctionBody(sig.bodyScope, function->func);
|
checkFunctionBody(sig.bodyScope, function->func);
|
||||||
Checkpoint end = checkpoint(this);
|
Checkpoint end = checkpoint(this);
|
||||||
|
|
||||||
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
|
if (!sigFullyDefined)
|
||||||
std::unique_ptr<Constraint> c =
|
{
|
||||||
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
|
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
|
||||||
|
std::unique_ptr<Constraint> c =
|
||||||
|
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
|
||||||
|
|
||||||
Constraint* previous = nullptr;
|
Constraint* previous = nullptr;
|
||||||
forEachConstraint(start, end, this, [&c, &previous](const ConstraintPtr& constraint) {
|
forEachConstraint(start, end, this,
|
||||||
c->dependencies.push_back(NotNull{constraint.get()});
|
[&c, &previous](const ConstraintPtr& constraint)
|
||||||
|
{
|
||||||
|
c->dependencies.push_back(NotNull{constraint.get()});
|
||||||
|
|
||||||
if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
|
if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
|
||||||
{
|
{
|
||||||
if (previous)
|
if (previous)
|
||||||
constraint->dependencies.push_back(NotNull{previous});
|
constraint->dependencies.push_back(NotNull{previous});
|
||||||
|
|
||||||
previous = constraint.get();
|
previous = constraint.get();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addConstraint(scope, std::move(c));
|
addConstraint(scope, std::move(c));
|
||||||
module->astTypes[function->func] = functionType;
|
module->astTypes[function->func] = functionType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
module->astTypes[function->func] = sig.signature;
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
@ -879,12 +939,19 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
|||||||
|
|
||||||
Checkpoint start = checkpoint(this);
|
Checkpoint start = checkpoint(this);
|
||||||
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
||||||
|
bool sigFullyDefined = !hasFreeType(sig.signature);
|
||||||
|
|
||||||
|
if (sigFullyDefined)
|
||||||
|
asMutable(generalizedType)->ty.emplace<BoundType>(sig.signature);
|
||||||
|
|
||||||
DenseHashSet<Constraint*> excludeList{nullptr};
|
DenseHashSet<Constraint*> excludeList{nullptr};
|
||||||
|
|
||||||
DefId def = dfg->getDef(function->name);
|
DefId def = dfg->getDef(function->name);
|
||||||
std::optional<TypeId> existingFunctionTy = lookup(scope, def);
|
std::optional<TypeId> existingFunctionTy = lookup(scope, def);
|
||||||
|
|
||||||
|
if (sigFullyDefined && existingFunctionTy && get<BlockedType>(*existingFunctionTy))
|
||||||
|
asMutable(*existingFunctionTy)->ty.emplace<BoundType>(sig.signature);
|
||||||
|
|
||||||
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
||||||
{
|
{
|
||||||
if (existingFunctionTy)
|
if (existingFunctionTy)
|
||||||
@ -906,7 +973,8 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
|||||||
if (!existingFunctionTy)
|
if (!existingFunctionTy)
|
||||||
ice->ice("prepopulateGlobalScope did not populate a global name", globalName->location);
|
ice->ice("prepopulateGlobalScope did not populate a global name", globalName->location);
|
||||||
|
|
||||||
generalizedType = *existingFunctionTy;
|
if (!sigFullyDefined)
|
||||||
|
generalizedType = *existingFunctionTy;
|
||||||
|
|
||||||
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
|
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
|
||||||
sig.bodyScope->lvalueTypes[def] = sig.signature;
|
sig.bodyScope->lvalueTypes[def] = sig.signature;
|
||||||
@ -943,25 +1011,30 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
|||||||
checkFunctionBody(sig.bodyScope, function->func);
|
checkFunctionBody(sig.bodyScope, function->func);
|
||||||
Checkpoint end = checkpoint(this);
|
Checkpoint end = checkpoint(this);
|
||||||
|
|
||||||
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
|
if (!sigFullyDefined)
|
||||||
std::unique_ptr<Constraint> c =
|
{
|
||||||
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{generalizedType, sig.signature});
|
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
|
||||||
|
std::unique_ptr<Constraint> c =
|
||||||
|
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{generalizedType, sig.signature});
|
||||||
|
|
||||||
Constraint* previous = nullptr;
|
Constraint* previous = nullptr;
|
||||||
forEachConstraint(start, end, this, [&c, &excludeList, &previous](const ConstraintPtr& constraint) {
|
forEachConstraint(start, end, this,
|
||||||
if (!excludeList.contains(constraint.get()))
|
[&c, &excludeList, &previous](const ConstraintPtr& constraint)
|
||||||
c->dependencies.push_back(NotNull{constraint.get()});
|
{
|
||||||
|
if (!excludeList.contains(constraint.get()))
|
||||||
|
c->dependencies.push_back(NotNull{constraint.get()});
|
||||||
|
|
||||||
if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
|
if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
|
||||||
{
|
{
|
||||||
if (previous)
|
if (previous)
|
||||||
constraint->dependencies.push_back(NotNull{previous});
|
constraint->dependencies.push_back(NotNull{previous});
|
||||||
|
|
||||||
previous = constraint.get();
|
previous = constraint.get();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addConstraint(scope, std::move(c));
|
addConstraint(scope, std::move(c));
|
||||||
|
}
|
||||||
|
|
||||||
return ControlFlow::None;
|
return ControlFlow::None;
|
||||||
}
|
}
|
||||||
@ -1626,24 +1699,6 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
|||||||
TypePackId argPack = addTypePack(std::move(args), argTail);
|
TypePackId argPack = addTypePack(std::move(args), argTail);
|
||||||
FunctionType ftv(TypeLevel{}, scope.get(), argPack, rets, std::nullopt, call->self);
|
FunctionType ftv(TypeLevel{}, scope.get(), argPack, rets, std::nullopt, call->self);
|
||||||
|
|
||||||
NotNull<Constraint> fcc = addConstraint(scope, call->func->location,
|
|
||||||
FunctionCallConstraint{
|
|
||||||
fnType,
|
|
||||||
argPack,
|
|
||||||
rets,
|
|
||||||
call,
|
|
||||||
std::move(discriminantTypes),
|
|
||||||
&module->astOverloadResolvedTypes,
|
|
||||||
});
|
|
||||||
|
|
||||||
NotNull<Constraint> foo = addConstraint(scope, call->func->location,
|
|
||||||
FunctionCheckConstraint{
|
|
||||||
fnType,
|
|
||||||
argPack,
|
|
||||||
call
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To make bidirectional type checking work, we need to solve these constraints in a particular order:
|
* To make bidirectional type checking work, we need to solve these constraints in a particular order:
|
||||||
*
|
*
|
||||||
@ -1653,14 +1708,35 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
|||||||
* 4. Solve the call
|
* 4. Solve the call
|
||||||
*/
|
*/
|
||||||
|
|
||||||
forEachConstraint(funcBeginCheckpoint, funcEndCheckpoint, this, [foo](const ConstraintPtr& constraint) {
|
NotNull<Constraint> checkConstraint = addConstraint(scope, call->func->location,
|
||||||
foo->dependencies.emplace_back(constraint.get());
|
FunctionCheckConstraint{
|
||||||
|
fnType,
|
||||||
|
argPack,
|
||||||
|
call,
|
||||||
|
NotNull{&module->astExpectedTypes}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
forEachConstraint(funcBeginCheckpoint, funcEndCheckpoint, this, [checkConstraint](const ConstraintPtr& constraint) {
|
||||||
|
checkConstraint->dependencies.emplace_back(constraint.get());
|
||||||
});
|
});
|
||||||
|
|
||||||
forEachConstraint(argBeginCheckpoint, argEndCheckpoint, this, [foo, fcc](const ConstraintPtr& constraint) {
|
NotNull<Constraint> callConstraint = addConstraint(scope, call->func->location,
|
||||||
constraint->dependencies.emplace_back(foo);
|
FunctionCallConstraint{
|
||||||
|
fnType,
|
||||||
|
argPack,
|
||||||
|
rets,
|
||||||
|
call,
|
||||||
|
std::move(discriminantTypes),
|
||||||
|
&module->astOverloadResolvedTypes,
|
||||||
|
});
|
||||||
|
|
||||||
fcc->dependencies.emplace_back(constraint.get());
|
callConstraint->dependencies.push_back(checkConstraint);
|
||||||
|
|
||||||
|
forEachConstraint(argBeginCheckpoint, argEndCheckpoint, this, [checkConstraint, callConstraint](const ConstraintPtr& constraint) {
|
||||||
|
constraint->dependencies.emplace_back(checkConstraint);
|
||||||
|
|
||||||
|
callConstraint->dependencies.emplace_back(constraint.get());
|
||||||
});
|
});
|
||||||
|
|
||||||
return InferencePack{rets, {refinementArena.variadic(returnRefinements)}};
|
return InferencePack{rets, {refinementArena.variadic(returnRefinements)}};
|
||||||
@ -1884,7 +1960,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
|
|||||||
checkFunctionBody(sig.bodyScope, func);
|
checkFunctionBody(sig.bodyScope, func);
|
||||||
Checkpoint endCheckpoint = checkpoint(this);
|
Checkpoint endCheckpoint = checkpoint(this);
|
||||||
|
|
||||||
if (generalize)
|
if (generalize && hasFreeType(sig.signature))
|
||||||
{
|
{
|
||||||
TypeId generalizedTy = arena->addType(BlockedType{});
|
TypeId generalizedTy = arena->addType(BlockedType{});
|
||||||
NotNull<Constraint> gc = addConstraint(sig.signatureScope, func->location, GeneralizationConstraint{generalizedTy, sig.signature});
|
NotNull<Constraint> gc = addConstraint(sig.signatureScope, func->location, GeneralizationConstraint{generalizedTy, sig.signature});
|
||||||
|
@ -1139,7 +1139,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
|||||||
return block(fn, constraint);
|
return block(fn, constraint);
|
||||||
|
|
||||||
if (isBlocked(argsPack))
|
if (isBlocked(argsPack))
|
||||||
return block(argsPack, constraint);
|
return true;
|
||||||
|
|
||||||
// We know the type of the function and the arguments it expects to receive.
|
// We know the type of the function and the arguments it expects to receive.
|
||||||
// We also know the TypeIds of the actual arguments that will be passed.
|
// We also know the TypeIds of the actual arguments that will be passed.
|
||||||
@ -1152,38 +1152,42 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
|||||||
// types.
|
// types.
|
||||||
|
|
||||||
// FIXME: Bidirectional type checking of overloaded functions is not yet supported.
|
// FIXME: Bidirectional type checking of overloaded functions is not yet supported.
|
||||||
if (auto ftv = get<FunctionType>(fn))
|
const FunctionType* ftv = get<FunctionType>(fn);
|
||||||
|
if (!ftv)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const std::vector<TypeId> expectedArgs = flatten(ftv->argTypes).first;
|
||||||
|
const std::vector<TypeId> argPackHead = flatten(argsPack).first;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < c.callSite->args.size && i < expectedArgs.size() && i < argPackHead.size(); ++i)
|
||||||
{
|
{
|
||||||
const std::vector<TypeId> expectedArgs = flatten(ftv->argTypes).first;
|
const TypeId expectedArgTy = follow(expectedArgs[i]);
|
||||||
const std::vector<TypeId> argPackHead = flatten(argsPack).first;
|
const TypeId actualArgTy = follow(argPackHead[i]);
|
||||||
|
const AstExpr* expr = c.callSite->args.data[i];
|
||||||
|
|
||||||
for (size_t i = 0; i < c.callSite->args.size && i < expectedArgs.size() && i < argPackHead.size(); ++i)
|
(*c.astExpectedTypes)[expr] = expectedArgTy;
|
||||||
|
|
||||||
|
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
|
||||||
|
const FunctionType* lambdaTy = get<FunctionType>(actualArgTy);
|
||||||
|
const AstExprFunction* lambdaExpr = expr->as<AstExprFunction>();
|
||||||
|
|
||||||
|
if (expectedLambdaTy && lambdaTy && lambdaExpr)
|
||||||
{
|
{
|
||||||
const TypeId expectedArgTy = follow(expectedArgs[i]);
|
const std::vector<TypeId> expectedLambdaArgTys = flatten(expectedLambdaTy->argTypes).first;
|
||||||
const TypeId actualArgTy = follow(argPackHead[i]);
|
const std::vector<TypeId> lambdaArgTys = flatten(lambdaTy->argTypes).first;
|
||||||
|
|
||||||
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
|
for (size_t j = 0; j < expectedLambdaArgTys.size() && j < lambdaArgTys.size() && j < lambdaExpr->args.size; ++j)
|
||||||
const FunctionType* lambdaTy = get<FunctionType>(actualArgTy);
|
|
||||||
const AstExprFunction* lambdaExpr = c.callSite->args.data[i]->as<AstExprFunction>();
|
|
||||||
|
|
||||||
if (expectedLambdaTy && lambdaTy && lambdaExpr)
|
|
||||||
{
|
{
|
||||||
const std::vector<TypeId> expectedLambdaArgTys = flatten(expectedLambdaTy->argTypes).first;
|
if (!lambdaExpr->args.data[j]->annotation && get<FreeType>(follow(lambdaArgTys[j])))
|
||||||
const std::vector<TypeId> lambdaArgTys = flatten(lambdaTy->argTypes).first;
|
|
||||||
|
|
||||||
for (size_t j = 0; j < expectedLambdaArgTys.size() && j < lambdaArgTys.size() && j < lambdaExpr->args.size; ++j)
|
|
||||||
{
|
{
|
||||||
if (!lambdaExpr->args.data[j]->annotation && get<FreeType>(follow(lambdaArgTys[j])))
|
asMutable(lambdaArgTys[j])->ty.emplace<BoundType>(expectedLambdaArgTys[j]);
|
||||||
{
|
|
||||||
asMutable(lambdaArgTys[j])->ty.emplace<BoundType>(expectedLambdaArgTys[j]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else if (expr->is<AstExprConstantBool>() || expr->is<AstExprConstantString>() || expr->is<AstExprConstantNumber>() || expr->is<AstExprConstantNil>() || expr->is<AstExprTable>())
|
||||||
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
{
|
||||||
u2.unify(actualArgTy, expectedArgTy);
|
Unifier2 u2{arena, builtinTypes, constraint->scope, NotNull{&iceReporter}};
|
||||||
}
|
u2.unify(actualArgTy, expectedArgTy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,6 +593,8 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
|||||||
return SubtypingResult{false}.withSubComponent(TypePath::PackField::Tail);
|
return SubtypingResult{false}.withSubComponent(TypePath::PackField::Tail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (get<ErrorTypePack>(*subTail))
|
||||||
|
return SubtypingResult{true}.withSubComponent(TypePath::PackField::Tail);
|
||||||
else
|
else
|
||||||
unexpected(*subTail);
|
unexpected(*subTail);
|
||||||
}
|
}
|
||||||
@ -643,6 +645,8 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
|
|||||||
return SubtypingResult{false}.withSuperComponent(TypePath::PackField::Tail);
|
return SubtypingResult{false}.withSuperComponent(TypePath::PackField::Tail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (get<ErrorTypePack>(*superTail))
|
||||||
|
return SubtypingResult{true}.withSuperComponent(TypePath::PackField::Tail);
|
||||||
else
|
else
|
||||||
unexpected(*superTail);
|
unexpected(*superTail);
|
||||||
}
|
}
|
||||||
|
@ -495,11 +495,12 @@ struct TypeChecker2
|
|||||||
return checkForFamilyInhabitance(follow(*ty), annotation->location);
|
return checkForFamilyInhabitance(follow(*ty), annotation->location);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId lookupPackAnnotation(AstTypePack* annotation)
|
std::optional<TypePackId> lookupPackAnnotation(AstTypePack* annotation)
|
||||||
{
|
{
|
||||||
TypePackId* tp = module->astResolvedTypePacks.find(annotation);
|
TypePackId* tp = module->astResolvedTypePacks.find(annotation);
|
||||||
LUAU_ASSERT(tp);
|
if (tp != nullptr)
|
||||||
return follow(*tp);
|
return {follow(*tp)};
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId lookupExpectedType(AstExpr* expr)
|
TypeId lookupExpectedType(AstExpr* expr)
|
||||||
@ -2187,9 +2188,11 @@ struct TypeChecker2
|
|||||||
}
|
}
|
||||||
else if (p.typePack)
|
else if (p.typePack)
|
||||||
{
|
{
|
||||||
TypePackId tp = lookupPackAnnotation(p.typePack);
|
std::optional<TypePackId> tp = lookupPackAnnotation(p.typePack);
|
||||||
|
if (!tp.has_value())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (typesProvided < typesRequired && size(tp) == 1 && finite(tp) && first(tp))
|
if (typesProvided < typesRequired && size(*tp) == 1 && finite(*tp) && first(*tp))
|
||||||
{
|
{
|
||||||
typesProvided += 1;
|
typesProvided += 1;
|
||||||
}
|
}
|
||||||
@ -2607,7 +2610,12 @@ struct TypeChecker2
|
|||||||
// shape. We instead want to report the unknown property error of
|
// shape. We instead want to report the unknown property error of
|
||||||
// the `else` branch.
|
// the `else` branch.
|
||||||
else if (context == ValueContext::LValue && !get<ClassType>(tableTy))
|
else if (context == ValueContext::LValue && !get<ClassType>(tableTy))
|
||||||
reportError(CannotExtendTable{tableTy, CannotExtendTable::Property, prop}, location);
|
{
|
||||||
|
if (get<PrimitiveType>(tableTy) || get<FunctionType>(tableTy))
|
||||||
|
reportError(NotATable{tableTy}, location);
|
||||||
|
else
|
||||||
|
reportError(CannotExtendTable{tableTy, CannotExtendTable::Property, prop}, location);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
reportError(UnknownProperty{tableTy, prop}, location);
|
reportError(UnknownProperty{tableTy, prop}, location);
|
||||||
}
|
}
|
||||||
|
@ -92,10 +92,11 @@ private:
|
|||||||
size_t tail_size = queue_size - head_size; // how many elements are in the tail portion (i.e. any portion that wrapped to the front)
|
size_t tail_size = queue_size - head_size; // how many elements are in the tail portion (i.e. any portion that wrapped to the front)
|
||||||
|
|
||||||
// move the head into the new buffer
|
// move the head into the new buffer
|
||||||
std::uninitialized_move(buffer + head, buffer + head + head_size, new_buffer);
|
if (head_size != 0)
|
||||||
|
std::uninitialized_move(buffer + head, buffer + head + head_size, new_buffer);
|
||||||
|
|
||||||
// move the tail into the new buffer immediately after
|
// move the tail into the new buffer immediately after
|
||||||
if (head_size < queue_size)
|
if (tail_size != 0)
|
||||||
std::uninitialized_move(buffer, buffer + tail_size, new_buffer + head_size);
|
std::uninitialized_move(buffer, buffer + tail_size, new_buffer + head_size);
|
||||||
|
|
||||||
// destroy the old elements
|
// destroy the old elements
|
||||||
@ -128,8 +129,16 @@ public:
|
|||||||
, head(other.head)
|
, head(other.head)
|
||||||
, queue_size(other.queue_size)
|
, queue_size(other.queue_size)
|
||||||
{
|
{
|
||||||
// copy the contents of the other buffer to this one
|
// copy the initialized contents of the other buffer to this one
|
||||||
std::uninitialized_copy(other.buffer, other.buffer + other.buffer_capacity, buffer);
|
size_t head_size = std::min(other.queue_size,
|
||||||
|
other.buffer_capacity - other.head); // how many elements are in the head portion (i.e. from the head to the end of the buffer)
|
||||||
|
size_t tail_size = other.queue_size - head_size; // how many elements are in the tail portion (i.e. any portion that wrapped to the front)
|
||||||
|
|
||||||
|
if (head_size != 0)
|
||||||
|
std::uninitialized_copy(other.buffer + other.head, other.buffer + other.head + head_size, buffer + head);
|
||||||
|
|
||||||
|
if (tail_size != 0)
|
||||||
|
std::uninitialized_copy(other.buffer, other.buffer + tail_size, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
VecDeque(const VecDeque& other, const Allocator& alloc)
|
VecDeque(const VecDeque& other, const Allocator& alloc)
|
||||||
@ -139,8 +148,16 @@ public:
|
|||||||
, head(other.head)
|
, head(other.head)
|
||||||
, queue_size(other.queue_size)
|
, queue_size(other.queue_size)
|
||||||
{
|
{
|
||||||
// copy the contents of the other buffer to this one
|
// copy the initialized contents of the other buffer to this one
|
||||||
std::uninitialized_copy(other.buffer, other.buffer + other.buffer_capacity, buffer);
|
size_t head_size = std::min(other.queue_size,
|
||||||
|
other.buffer_capacity - other.head); // how many elements are in the head portion (i.e. from the head to the end of the buffer)
|
||||||
|
size_t tail_size = other.queue_size - head_size; // how many elements are in the tail portion (i.e. any portion that wrapped to the front)
|
||||||
|
|
||||||
|
if (head_size != 0)
|
||||||
|
std::uninitialized_copy(other.buffer + other.head, other.buffer + other.head + head_size, buffer + head);
|
||||||
|
|
||||||
|
if (tail_size != 0)
|
||||||
|
std::uninitialized_copy(other.buffer, other.buffer + tail_size, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
VecDeque(VecDeque&& other) noexcept
|
VecDeque(VecDeque&& other) noexcept
|
||||||
@ -195,14 +212,19 @@ public:
|
|||||||
buffer_capacity = other.buffer_capacity;
|
buffer_capacity = other.buffer_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t head_size = other.capacity() - other.head; // how many elements are in the head portion (i.e. from the head to the end of the buffer)
|
size_t head_size = std::min(other.queue_size,
|
||||||
size_t tail_size = other.size() - head_size; // how many elements are in the tail portion (i.e. any portion that wrapped to the front)
|
other.buffer_capacity - other.head); // how many elements are in the head portion (i.e. from the head to the end of the buffer)
|
||||||
|
size_t tail_size = other.queue_size - head_size; // how many elements are in the tail portion (i.e. any portion that wrapped to the front)
|
||||||
|
|
||||||
// copy the contents of the other buffer's head into place
|
// Assignment doesn't try to match the capacity of 'other' and thus makes the buffer contiguous
|
||||||
std::uninitialized_copy(other.buffer + other.head, other.buffer + head + head_size, buffer);
|
head = 0;
|
||||||
|
queue_size = other.queue_size;
|
||||||
|
|
||||||
// copy the contents of the other buffer's tail into place immediately after
|
if (head_size != 0)
|
||||||
std::uninitialized_copy(other.buffer, other.buffer + tail_size, buffer + head_size);
|
std::uninitialized_copy(other.buffer + other.head, other.buffer + other.head + head_size, buffer);
|
||||||
|
|
||||||
|
if (tail_size != 0)
|
||||||
|
std::uninitialized_copy(other.buffer, other.buffer + tail_size, buffer + head_size);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -325,17 +347,16 @@ public:
|
|||||||
T* new_buffer = this->allocate(new_capacity);
|
T* new_buffer = this->allocate(new_capacity);
|
||||||
|
|
||||||
// move the head into the new buffer
|
// move the head into the new buffer
|
||||||
std::uninitialized_move(buffer + head, buffer + head + head_size, new_buffer);
|
if (head_size != 0)
|
||||||
|
std::uninitialized_move(buffer + head, buffer + head + head_size, new_buffer);
|
||||||
// move the tail into the new buffer immediately after, if we have one
|
|
||||||
if (head_size < queue_size)
|
|
||||||
std::uninitialized_move(buffer, buffer + tail_size, new_buffer + head_size);
|
|
||||||
|
|
||||||
// move the tail into the new buffer immediately after
|
// move the tail into the new buffer immediately after
|
||||||
std::uninitialized_move(buffer, buffer + tail_size, new_buffer + head_size);
|
if (tail_size != 0)
|
||||||
|
std::uninitialized_move(buffer, buffer + tail_size, new_buffer + head_size);
|
||||||
|
|
||||||
// destroy all the existing elements before freeing the old buffer
|
// destroy all the existing elements before freeing the old buffer
|
||||||
destroyElements();
|
destroyElements();
|
||||||
|
|
||||||
// deallocate the old buffer
|
// deallocate the old buffer
|
||||||
this->deallocate(buffer, old_capacity);
|
this->deallocate(buffer, old_capacity);
|
||||||
|
|
||||||
@ -350,7 +371,8 @@ public:
|
|||||||
return buffer_capacity;
|
return buffer_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void shrink_to_fit() {
|
void shrink_to_fit()
|
||||||
|
{
|
||||||
size_t old_capacity = capacity();
|
size_t old_capacity = capacity();
|
||||||
size_t new_capacity = queue_size;
|
size_t new_capacity = queue_size;
|
||||||
|
|
||||||
@ -365,10 +387,11 @@ public:
|
|||||||
T* new_buffer = this->allocate(new_capacity);
|
T* new_buffer = this->allocate(new_capacity);
|
||||||
|
|
||||||
// move the head into the new buffer
|
// move the head into the new buffer
|
||||||
std::uninitialized_move(buffer + head, buffer + head + head_size, new_buffer);
|
if (head_size != 0)
|
||||||
|
std::uninitialized_move(buffer + head, buffer + head + head_size, new_buffer);
|
||||||
|
|
||||||
// move the tail into the new buffer immediately after, if we have one
|
// move the tail into the new buffer immediately after, if we have one
|
||||||
if (head_size < queue_size)
|
if (tail_size != 0)
|
||||||
std::uninitialized_move(buffer, buffer + tail_size, new_buffer + head_size);
|
std::uninitialized_move(buffer, buffer + tail_size, new_buffer + head_size);
|
||||||
|
|
||||||
// destroy all the existing elements before freeing the old buffer
|
// destroy all the existing elements before freeing the old buffer
|
||||||
|
@ -2707,16 +2707,26 @@ local t: Test = { @1 }
|
|||||||
CHECK(ac.entryMap.count("first"));
|
CHECK(ac.entryMap.count("first"));
|
||||||
CHECK(ac.entryMap.count("second"));
|
CHECK(ac.entryMap.count("second"));
|
||||||
CHECK_EQ(ac.context, AutocompleteContext::Property);
|
CHECK_EQ(ac.context, AutocompleteContext::Property);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "suggest_table_keys_no_initial_character_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauAutocompleteTableKeysNoInitialCharacter, true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
type Test = { first: number, second: number }
|
type Test = { first: number, second: number }
|
||||||
local t: Test = { first = 1, @1 }
|
local t: Test = { first = 1, @1 }
|
||||||
)");
|
)");
|
||||||
|
|
||||||
ac = autocomplete('1');
|
auto ac = autocomplete('1');
|
||||||
CHECK_EQ(ac.entryMap.count("first"), 0);
|
CHECK_EQ(ac.entryMap.count("first"), 0);
|
||||||
CHECK(ac.entryMap.count("second"));
|
CHECK(ac.entryMap.count("second"));
|
||||||
CHECK_EQ(ac.context, AutocompleteContext::Property);
|
CHECK_EQ(ac.context, AutocompleteContext::Property);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "suggest_table_keys_no_initial_character_3")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauAutocompleteTableKeysNoInitialCharacter, true};
|
||||||
|
|
||||||
check(R"(
|
check(R"(
|
||||||
type Properties = { TextScaled: boolean, Text: string }
|
type Properties = { TextScaled: boolean, Text: string }
|
||||||
@ -2725,7 +2735,8 @@ local function create(props: Properties) end
|
|||||||
create({ @1 })
|
create({ @1 })
|
||||||
)");
|
)");
|
||||||
|
|
||||||
ac = autocomplete('1');
|
auto ac = autocomplete('1');
|
||||||
|
CHECK(ac.entryMap.size() > 0);
|
||||||
CHECK(ac.entryMap.count("TextScaled"));
|
CHECK(ac.entryMap.count("TextScaled"));
|
||||||
CHECK(ac.entryMap.count("Text"));
|
CHECK(ac.entryMap.count("Text"));
|
||||||
CHECK_EQ(ac.context, AutocompleteContext::Property);
|
CHECK_EQ(ac.context, AutocompleteContext::Property);
|
||||||
|
@ -582,4 +582,28 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_rfc_example")
|
|||||||
CHECK_EQ("\"cactus\"", toString(tm->givenType));
|
CHECK_EQ("\"cactus\"", toString(tm->givenType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_oss_crash_gh1161")
|
||||||
|
{
|
||||||
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local EnumVariants = {
|
||||||
|
["a"] = 1, ["b"] = 2, ["c"] = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnumKey = keyof<typeof(EnumVariants)>
|
||||||
|
|
||||||
|
function fnA<T>(i: T): keyof<T> end
|
||||||
|
|
||||||
|
function fnB(i: EnumKey) end
|
||||||
|
|
||||||
|
local result = fnA(EnumVariants)
|
||||||
|
fnB(result)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK(get<FunctionExitsWithoutReturning>(result.errors[0]));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -1075,4 +1075,11 @@ TEST_CASE_FIXTURE(Fixture, "typeof_is_not_a_valid_alias_name")
|
|||||||
CHECK("Type aliases cannot be named typeof" == toString(result.errors[0]));
|
CHECK("Type aliases cannot be named typeof" == toString(result.errors[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "fuzzer_bug_doesnt_crash")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type t0 = (t0<t0...>)
|
||||||
|
)");
|
||||||
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
|
}
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -50,13 +50,30 @@ TEST_CASE_FIXTURE(Fixture, "tc_function")
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "check_function_bodies")
|
TEST_CASE_FIXTURE(Fixture, "check_function_bodies")
|
||||||
{
|
{
|
||||||
CheckResult result = check("function myFunction() local a = 0 a = true end");
|
CheckResult result = check(R"(
|
||||||
|
function myFunction(): number
|
||||||
|
local a = 0
|
||||||
|
a = true
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
CHECK_EQ(result.errors[0], (TypeError{Location{Position{0, 44}, Position{0, 48}}, TypeMismatch{
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
builtinTypes->numberType,
|
{
|
||||||
builtinTypes->booleanType,
|
const TypePackMismatch* tm = get<TypePackMismatch>(result.errors[0]);
|
||||||
}}));
|
REQUIRE(tm);
|
||||||
|
CHECK(toString(tm->wantedTp) == "number");
|
||||||
|
CHECK(toString(tm->givenTp) == "boolean");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ(result.errors[0], (TypeError{Location{Position{3, 16}, Position{3, 20}}, TypeMismatch{
|
||||||
|
builtinTypes->numberType,
|
||||||
|
builtinTypes->booleanType,
|
||||||
|
}}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "cannot_hoist_interior_defns_into_signature")
|
TEST_CASE_FIXTURE(Fixture, "cannot_hoist_interior_defns_into_signature")
|
||||||
@ -2227,4 +2244,69 @@ a = function(a, b) return a + b end
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "simple_unannotated_mutual_recursion")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
function even(n)
|
||||||
|
if n == 0 then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return odd(n - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function odd(n)
|
||||||
|
if n == 0 then
|
||||||
|
return false
|
||||||
|
elseif n == 1 then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return even(n - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
{
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
CHECK(toString(result.errors[0]) == "Type family instance sub<unknown, number> is uninhabited");
|
||||||
|
CHECK(toString(result.errors[1]) == "Type family instance sub<unknown, number> is uninhabited");
|
||||||
|
CHECK(toString(result.errors[2]) == "Type family instance sub<unknown, number> is uninhabited");
|
||||||
|
CHECK(toString(result.errors[3]) == "Type family instance sub<unknown, number> is uninhabited");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK(toString(result.errors[0]) == "Unknown type used in - operation; consider adding a type annotation to 'n'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "simple_lightly_annotated_mutual_recursion")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
function even(n: number)
|
||||||
|
if n == 0 then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return odd(n - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function odd(n: number)
|
||||||
|
if n == 0 then
|
||||||
|
return false
|
||||||
|
elseif n == 1 then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return even(n - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("(number) -> boolean", toString(requireType("even")));
|
||||||
|
CHECK_EQ("(number) -> boolean", toString(requireType("odd")));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -144,12 +144,12 @@ TEST_CASE_FIXTURE(Fixture, "check_recursive_generic_function")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions")
|
TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local id2
|
function id1<a>(x:a):a
|
||||||
local function id1<a>(x:a):a
|
|
||||||
local y: string = id2("hi")
|
local y: string = id2("hi")
|
||||||
local z: number = id2(37)
|
local z: number = id2(37)
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
|
|
||||||
function id2<a>(x:a):a
|
function id2<a>(x:a):a
|
||||||
local y: string = id1("hi")
|
local y: string = id1("hi")
|
||||||
local z: number = id1(37)
|
local z: number = id1(37)
|
||||||
@ -159,6 +159,68 @@ TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions")
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_unannotated")
|
||||||
|
{
|
||||||
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
function id1(x)
|
||||||
|
local y: string = id2("hi")
|
||||||
|
local z: number = id2(37)
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
|
||||||
|
function id2(x)
|
||||||
|
local y: string = id1("hi")
|
||||||
|
local z: number = id1(37)
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "check_mutual_generic_functions_errors")
|
||||||
|
{
|
||||||
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
function id1(x)
|
||||||
|
local y: string = id2(37) -- odd
|
||||||
|
local z: number = id2("hi") -- even
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
|
||||||
|
function id2(x)
|
||||||
|
local y: string = id1(37) -- odd
|
||||||
|
local z: number = id1("hi") -- even
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
|
||||||
|
// odd errors
|
||||||
|
for (int i = 0; i < 4; i += 2)
|
||||||
|
{
|
||||||
|
TypeMismatch* tm = get<TypeMismatch>(result.errors[i]);
|
||||||
|
REQUIRE(tm);
|
||||||
|
CHECK_EQ("string", toString(tm->wantedType));
|
||||||
|
CHECK_EQ("number", toString(tm->givenType));
|
||||||
|
}
|
||||||
|
|
||||||
|
// even errors
|
||||||
|
for (int i = 1; i < 4; i += 2)
|
||||||
|
{
|
||||||
|
TypeMismatch* tm = get<TypeMismatch>(result.errors[i]);
|
||||||
|
REQUIRE(tm);
|
||||||
|
CHECK_EQ("number", toString(tm->wantedType));
|
||||||
|
CHECK_EQ("string", toString(tm->givenType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
|
TEST_CASE_FIXTURE(Fixture, "generic_functions_in_types")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
@ -947,7 +1009,7 @@ TEST_CASE_FIXTURE(Fixture, "instantiate_cyclic_generic_function")
|
|||||||
|
|
||||||
TypeId arg = follow(*optionArg);
|
TypeId arg = follow(*optionArg);
|
||||||
const TableType* argTable = get<TableType>(arg);
|
const TableType* argTable = get<TableType>(arg);
|
||||||
REQUIRE(argTable != nullptr);
|
REQUIRE_MESSAGE(argTable != nullptr, "Expected table but got " << toString(arg));
|
||||||
|
|
||||||
std::optional<Property> methodProp = get(argTable->props, "method");
|
std::optional<Property> methodProp = get(argTable->props, "method");
|
||||||
REQUIRE(bool(methodProp));
|
REQUIRE(bool(methodProp));
|
||||||
|
@ -287,311 +287,485 @@ TEST_CASE("shrink_to_fit_works")
|
|||||||
CHECK_EQ(queue[j], j);
|
CHECK_EQ(queue[j], j);
|
||||||
}
|
}
|
||||||
|
|
||||||
// To avoid hitting SSO issues, we need sufficiently long strings.
|
const static std::string testStringSet[2][10] = {
|
||||||
// This list of test strings consists of quotes from Ursula K. Le Guin.
|
|
||||||
const static std::string testStrings[] = {
|
// To hit potential SSO issues showing memory management issues, we need small strings
|
||||||
"Love doesn't just sit there, like a stone, it has to be made, like bread; remade all the time, made new.",
|
{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"},
|
||||||
"People who deny the existence of dragons are often eaten by dragons. From within.",
|
|
||||||
"It is good to have an end to journey toward; but it is the journey that matters, in the end.",
|
// This list of non-SSO test strings consists of quotes from Ursula K. Le Guin.
|
||||||
"We're each of us alone, to be sure. What can you do but hold your hand out in the dark?",
|
{"Love doesn't just sit there, like a stone, it has to be made, like bread; remade all the time, made new.",
|
||||||
"When you light a candle, you also cast a shadow.",
|
"People who deny the existence of dragons are often eaten by dragons. From within.",
|
||||||
"You cannot buy the revolution. You cannot make the revolution. You can only be the revolution. It is in your spirit, or it is nowhere.",
|
"It is good to have an end to journey toward; but it is the journey that matters, in the end.",
|
||||||
"To learn which questions are unanswerable, and not to answer them: this skill is most needful in times of stress and darkness.",
|
"We're each of us alone, to be sure. What can you do but hold your hand out in the dark?", "When you light a candle, you also cast a shadow.",
|
||||||
"What sane person could live in this world and not be crazy?",
|
"You cannot buy the revolution. You cannot make the revolution. You can only be the revolution. It is in your spirit, or it is nowhere.",
|
||||||
"The only thing that makes life possible is permanent, intolerable uncertainty: not knowing what comes next.",
|
"To learn which questions are unanswerable, and not to answer them: this skill is most needful in times of stress and darkness.",
|
||||||
"My imagination makes me human and makes me a fool; it gives me all the world and exiles me from it."
|
"What sane person could live in this world and not be crazy?",
|
||||||
};
|
"The only thing that makes life possible is permanent, intolerable uncertainty: not knowing what comes next.",
|
||||||
|
"My imagination makes me human and makes me a fool; it gives me all the world and exiles me from it."}};
|
||||||
|
|
||||||
TEST_CASE("string_queue_test_no_initial_capacity")
|
TEST_CASE("string_queue_test_no_initial_capacity")
|
||||||
{
|
{
|
||||||
// initial capacity is not set, so this should grow to be 11
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue;
|
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
queue.push_back(testStrings[i]);
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
|
||||||
REQUIRE(queue.size() == 10);
|
|
||||||
|
|
||||||
CHECK_EQ(queue.capacity(), 11);
|
|
||||||
|
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
{
|
{
|
||||||
CHECK_EQ(queue.front(), testStrings[j]);
|
auto testStrings = testStringSet[stringSet];
|
||||||
CHECK_EQ(queue.back(), testStrings[9]);
|
|
||||||
|
// initial capacity is not set, so this should grow to be 11
|
||||||
|
Luau::VecDeque<std::string> queue;
|
||||||
|
|
||||||
|
REQUIRE(queue.empty());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
queue.push_back(testStrings[i]);
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
REQUIRE(!queue.empty());
|
||||||
queue.pop_front();
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue.capacity(), 11);
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
CHECK_EQ(queue.front(), testStrings[j]);
|
||||||
|
CHECK_EQ(queue.back(), testStrings[9]);
|
||||||
|
|
||||||
|
REQUIRE(!queue.empty());
|
||||||
|
queue.pop_front();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("string_queue_test")
|
TEST_CASE("string_queue_test")
|
||||||
{
|
{
|
||||||
// initial capacity set to 5 so that a grow is necessary
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue;
|
|
||||||
queue.reserve(5);
|
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
queue.push_back(testStrings[i]);
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
|
||||||
REQUIRE(queue.size() == 10);
|
|
||||||
|
|
||||||
CHECK_EQ(queue.capacity(), 13);
|
|
||||||
|
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
{
|
{
|
||||||
CHECK_EQ(queue.front(), testStrings[j]);
|
auto testStrings = testStringSet[stringSet];
|
||||||
CHECK_EQ(queue.back(), testStrings[9]);
|
|
||||||
|
// initial capacity set to 5 so that a grow is necessary
|
||||||
|
Luau::VecDeque<std::string> queue;
|
||||||
|
queue.reserve(5);
|
||||||
|
|
||||||
|
REQUIRE(queue.empty());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
queue.push_back(testStrings[i]);
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
REQUIRE(!queue.empty());
|
||||||
queue.pop_front();
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue.capacity(), 13);
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
CHECK_EQ(queue.front(), testStrings[j]);
|
||||||
|
CHECK_EQ(queue.back(), testStrings[9]);
|
||||||
|
|
||||||
|
REQUIRE(!queue.empty());
|
||||||
|
queue.pop_front();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("string_queue_test_initializer_list")
|
TEST_CASE("string_queue_test_initializer_list")
|
||||||
{
|
{
|
||||||
Luau::VecDeque<std::string> queue{
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
testStrings[0],
|
|
||||||
testStrings[1],
|
|
||||||
testStrings[2],
|
|
||||||
testStrings[3],
|
|
||||||
testStrings[4],
|
|
||||||
testStrings[5],
|
|
||||||
testStrings[6],
|
|
||||||
testStrings[7],
|
|
||||||
testStrings[8],
|
|
||||||
testStrings[9],
|
|
||||||
};
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
|
||||||
REQUIRE(queue.size() == 10);
|
|
||||||
|
|
||||||
CHECK_EQ(queue.capacity(), 10);
|
|
||||||
|
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
{
|
{
|
||||||
CHECK_EQ(queue.front(), testStrings[j]);
|
auto testStrings = testStringSet[stringSet];
|
||||||
CHECK_EQ(queue.back(), testStrings[9]);
|
|
||||||
|
Luau::VecDeque<std::string> queue{
|
||||||
|
testStrings[0],
|
||||||
|
testStrings[1],
|
||||||
|
testStrings[2],
|
||||||
|
testStrings[3],
|
||||||
|
testStrings[4],
|
||||||
|
testStrings[5],
|
||||||
|
testStrings[6],
|
||||||
|
testStrings[7],
|
||||||
|
testStrings[8],
|
||||||
|
testStrings[9],
|
||||||
|
};
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
REQUIRE(!queue.empty());
|
||||||
queue.pop_front();
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue.capacity(), 10);
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
CHECK_EQ(queue.front(), testStrings[j]);
|
||||||
|
CHECK_EQ(queue.back(), testStrings[9]);
|
||||||
|
|
||||||
|
REQUIRE(!queue.empty());
|
||||||
|
queue.pop_front();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("reverse_string_queue_test")
|
TEST_CASE("reverse_string_queue_test")
|
||||||
{
|
{
|
||||||
// initial capacity set to 5 so that a grow is necessary
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue;
|
|
||||||
queue.reserve(5);
|
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
queue.push_front(testStrings[i]);
|
|
||||||
// q: 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
|
||||||
REQUIRE(queue.size() == 10);
|
|
||||||
|
|
||||||
CHECK_EQ(queue.capacity(), 13);
|
|
||||||
|
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
{
|
{
|
||||||
CHECK_EQ(queue.front(), testStrings[9]);
|
auto testStrings = testStringSet[stringSet];
|
||||||
CHECK_EQ(queue.back(), testStrings[j]);
|
|
||||||
|
// initial capacity set to 5 so that a grow is necessary
|
||||||
|
Luau::VecDeque<std::string> queue;
|
||||||
|
queue.reserve(5);
|
||||||
|
|
||||||
|
REQUIRE(queue.empty());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
queue.push_front(testStrings[i]);
|
||||||
|
// q: 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
REQUIRE(!queue.empty());
|
||||||
queue.pop_back();
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue.capacity(), 13);
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
CHECK_EQ(queue.front(), testStrings[9]);
|
||||||
|
CHECK_EQ(queue.back(), testStrings[j]);
|
||||||
|
|
||||||
|
REQUIRE(!queue.empty());
|
||||||
|
queue.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("random_access_string_queue_test")
|
TEST_CASE("random_access_string_queue_test")
|
||||||
{
|
{
|
||||||
// initial capacity set to 5 so that a grow is necessary
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue;
|
|
||||||
queue.reserve(5);
|
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
queue.push_back(testStrings[i]);
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
|
||||||
REQUIRE(queue.size() == 10);
|
|
||||||
|
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
{
|
{
|
||||||
CHECK_EQ(queue.at(j), testStrings[j]);
|
auto testStrings = testStringSet[stringSet];
|
||||||
CHECK_EQ(queue[j], testStrings[j]);
|
|
||||||
|
// initial capacity set to 5 so that a grow is necessary
|
||||||
|
Luau::VecDeque<std::string> queue;
|
||||||
|
queue.reserve(5);
|
||||||
|
|
||||||
|
REQUIRE(queue.empty());
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
queue.push_back(testStrings[i]);
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
|
REQUIRE(!queue.empty());
|
||||||
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
CHECK_EQ(queue.at(j), testStrings[j]);
|
||||||
|
CHECK_EQ(queue[j], testStrings[j]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("clear_works_on_string_queue")
|
TEST_CASE("clear_works_on_string_queue")
|
||||||
{
|
{
|
||||||
// initial capacity set to 5 so that a grow is necessary
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue;
|
{
|
||||||
queue.reserve(5);
|
auto testStrings = testStringSet[stringSet];
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
// initial capacity set to 5 so that a grow is necessary
|
||||||
|
Luau::VecDeque<std::string> queue;
|
||||||
|
queue.reserve(5);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
REQUIRE(queue.empty());
|
||||||
queue.push_back(testStrings[i]);
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
for (int i = 0; i < 10; i++)
|
||||||
REQUIRE(queue.size() == 10);
|
queue.push_back(testStrings[i]);
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
for (int j = 0; j < 10; j++)
|
REQUIRE(!queue.empty());
|
||||||
CHECK_EQ(queue[j], testStrings[j]);
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
queue.clear();
|
for (int j = 0; j < 10; j++)
|
||||||
CHECK(queue.empty());
|
CHECK_EQ(queue[j], testStrings[j]);
|
||||||
CHECK(queue.size() == 0);
|
|
||||||
|
queue.clear();
|
||||||
|
CHECK(queue.empty());
|
||||||
|
CHECK(queue.size() == 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("pop_front_string_at_end")
|
TEST_CASE("pop_front_string_at_end")
|
||||||
{
|
{
|
||||||
// initial capacity set to 5 so that a grow is necessary
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue;
|
|
||||||
queue.reserve(5);
|
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
|
||||||
|
|
||||||
// setting up the internal buffer to be: 1234567890 by the end (i.e. 0 at the end of the buffer)
|
|
||||||
queue.push_front(testStrings[0]);
|
|
||||||
|
|
||||||
for (int i = 1; i < 10; i++)
|
|
||||||
queue.push_back(testStrings[i]);
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
|
||||||
REQUIRE(queue.size() == 10);
|
|
||||||
|
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
{
|
{
|
||||||
CHECK_EQ(queue.front(), testStrings[j]);
|
auto testStrings = testStringSet[stringSet];
|
||||||
CHECK_EQ(queue.back(), testStrings[9]);
|
|
||||||
|
// initial capacity set to 5 so that a grow is necessary
|
||||||
|
Luau::VecDeque<std::string> queue;
|
||||||
|
queue.reserve(5);
|
||||||
|
|
||||||
|
REQUIRE(queue.empty());
|
||||||
|
|
||||||
|
// setting up the internal buffer to be: 1234567890 by the end (i.e. 0 at the end of the buffer)
|
||||||
|
queue.push_front(testStrings[0]);
|
||||||
|
|
||||||
|
for (int i = 1; i < 10; i++)
|
||||||
|
queue.push_back(testStrings[i]);
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
REQUIRE(!queue.empty());
|
||||||
queue.pop_front();
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
CHECK_EQ(queue.front(), testStrings[j]);
|
||||||
|
CHECK_EQ(queue.back(), testStrings[9]);
|
||||||
|
|
||||||
|
REQUIRE(!queue.empty());
|
||||||
|
queue.pop_front();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("pop_back_string_at_front")
|
TEST_CASE("pop_back_string_at_front")
|
||||||
{
|
{
|
||||||
// initial capacity set to 5 so that a grow is necessary
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue;
|
|
||||||
queue.reserve(5);
|
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
|
||||||
|
|
||||||
// setting up the internal buffer to be: 9012345678 by the end (i.e. 9 at the front of the buffer)
|
|
||||||
queue.push_back(testStrings[0]);
|
|
||||||
|
|
||||||
for (int i = 1; i < 10; i++)
|
|
||||||
queue.push_front(testStrings[i]);
|
|
||||||
// q: 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
|
||||||
REQUIRE(queue.size() == 10);
|
|
||||||
|
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
{
|
{
|
||||||
CHECK_EQ(queue.front(), testStrings[9]);
|
auto testStrings = testStringSet[stringSet];
|
||||||
CHECK_EQ(queue.back(), testStrings[j]);
|
|
||||||
|
// initial capacity set to 5 so that a grow is necessary
|
||||||
|
Luau::VecDeque<std::string> queue;
|
||||||
|
queue.reserve(5);
|
||||||
|
|
||||||
|
REQUIRE(queue.empty());
|
||||||
|
|
||||||
|
// setting up the internal buffer to be: 9012345678 by the end (i.e. 9 at the front of the buffer)
|
||||||
|
queue.push_back(testStrings[0]);
|
||||||
|
|
||||||
|
for (int i = 1; i < 10; i++)
|
||||||
|
queue.push_front(testStrings[i]);
|
||||||
|
// q: 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
REQUIRE(!queue.empty());
|
||||||
queue.pop_back();
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
CHECK_EQ(queue.front(), testStrings[9]);
|
||||||
|
CHECK_EQ(queue.back(), testStrings[j]);
|
||||||
|
|
||||||
|
REQUIRE(!queue.empty());
|
||||||
|
queue.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("string_queue_is_contiguous")
|
TEST_CASE("string_queue_is_contiguous")
|
||||||
{
|
{
|
||||||
// initial capacity is not set, so this should grow to be 11
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue{};
|
{
|
||||||
|
auto testStrings = testStringSet[stringSet];
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
// initial capacity is not set, so this should grow to be 11
|
||||||
|
Luau::VecDeque<std::string> queue{};
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
REQUIRE(queue.empty());
|
||||||
queue.push_back(testStrings[i]);
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
for (int i = 0; i < 10; i++)
|
||||||
REQUIRE(queue.size() == 10);
|
queue.push_back(testStrings[i]);
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
CHECK_EQ(queue.capacity(), 11);
|
REQUIRE(!queue.empty());
|
||||||
CHECK(queue.is_contiguous());
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue.capacity(), 11);
|
||||||
|
CHECK(queue.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check copy construction
|
||||||
|
Luau::VecDeque<std::string> queue2 = queue;
|
||||||
|
|
||||||
|
REQUIRE(!queue2.empty());
|
||||||
|
REQUIRE(queue2.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue2.capacity(), 11);
|
||||||
|
CHECK(queue2.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue2[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check copy assignment
|
||||||
|
Luau::VecDeque<std::string> queue3;
|
||||||
|
queue3 = queue;
|
||||||
|
|
||||||
|
REQUIRE(!queue3.empty());
|
||||||
|
REQUIRE(queue3.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue3.capacity(), 11);
|
||||||
|
CHECK(queue3.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue3[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check move construction
|
||||||
|
Luau::VecDeque<std::string> queue4 = std::move(queue3);
|
||||||
|
|
||||||
|
REQUIRE(!queue4.empty());
|
||||||
|
REQUIRE(queue4.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue4.capacity(), 11);
|
||||||
|
CHECK(queue4.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue4[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check move assignment
|
||||||
|
Luau::VecDeque<std::string> queue5;
|
||||||
|
queue5 = std::move(queue2);
|
||||||
|
|
||||||
|
REQUIRE(!queue5.empty());
|
||||||
|
REQUIRE(queue5.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue5.capacity(), 11);
|
||||||
|
CHECK(queue5.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue5[j], testStrings[j]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("string_queue_is_not_contiguous")
|
TEST_CASE("string_queue_is_not_contiguous")
|
||||||
{
|
{
|
||||||
// initial capacity is not set, so this should grow to be 11
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue{};
|
{
|
||||||
|
auto testStrings = testStringSet[stringSet];
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
// initial capacity is not set, so this should grow to be 11
|
||||||
|
Luau::VecDeque<std::string> queue{};
|
||||||
|
|
||||||
for (int i = 5; i < 10; i++)
|
REQUIRE(queue.empty());
|
||||||
queue.push_back(testStrings[i]);
|
|
||||||
for (int i = 4; i >= 0; i--)
|
|
||||||
queue.push_front(testStrings[i]);
|
|
||||||
// buffer: 56789......01234
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
for (int i = 5; i < 10; i++)
|
||||||
REQUIRE(queue.size() == 10);
|
queue.push_back(testStrings[i]);
|
||||||
|
for (int i = 4; i >= 0; i--)
|
||||||
|
queue.push_front(testStrings[i]);
|
||||||
|
// buffer: 56789......01234
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
CHECK_EQ(queue.capacity(), 11);
|
REQUIRE(!queue.empty());
|
||||||
CHECK(!queue.is_contiguous());
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
// checking that it is indeed sequential integers from 0 to 9
|
CHECK_EQ(queue.capacity(), 11);
|
||||||
for (int j = 0; j < 10; j++)
|
CHECK(!queue.is_contiguous());
|
||||||
CHECK_EQ(queue[j], testStrings[j]);
|
|
||||||
|
// checking that it is indeed sequential integers from 0 to 9
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check copy construction
|
||||||
|
Luau::VecDeque<std::string> queue2 = queue;
|
||||||
|
|
||||||
|
REQUIRE(!queue2.empty());
|
||||||
|
REQUIRE(queue2.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue2.capacity(), 11);
|
||||||
|
CHECK(!queue2.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue2[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check copy assignment
|
||||||
|
Luau::VecDeque<std::string> queue3;
|
||||||
|
queue3 = queue;
|
||||||
|
|
||||||
|
REQUIRE(!queue3.empty());
|
||||||
|
REQUIRE(queue3.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue3.capacity(), 11);
|
||||||
|
CHECK(queue3.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue3[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check move construction
|
||||||
|
Luau::VecDeque<std::string> queue4 = std::move(queue);
|
||||||
|
|
||||||
|
REQUIRE(!queue4.empty());
|
||||||
|
REQUIRE(queue4.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue4.capacity(), 11);
|
||||||
|
CHECK(!queue4.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue4[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check move assignment
|
||||||
|
Luau::VecDeque<std::string> queue5;
|
||||||
|
queue5 = std::move(queue2);
|
||||||
|
|
||||||
|
REQUIRE(!queue5.empty());
|
||||||
|
REQUIRE(queue5.size() == 10);
|
||||||
|
|
||||||
|
CHECK_EQ(queue5.capacity(), 11);
|
||||||
|
CHECK(!queue5.is_contiguous());
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue5[j], testStrings[j]);
|
||||||
|
|
||||||
|
// Check that grow from discontiguous is handled well
|
||||||
|
queue4.push_back("zero");
|
||||||
|
queue4.push_back("?");
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue4[j], testStrings[j]);
|
||||||
|
CHECK_EQ(queue4[10], "zero");
|
||||||
|
CHECK_EQ(queue4[11], "?");
|
||||||
|
|
||||||
|
// Check that reserve from discontiguous is handled well
|
||||||
|
queue5.reserve(20);
|
||||||
|
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue5[j], testStrings[j]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("shrink_to_fit_works_with_strings")
|
TEST_CASE("shrink_to_fit_works_with_strings")
|
||||||
{
|
{
|
||||||
// initial capacity is not set, so this should grow to be 11
|
for (size_t stringSet = 0; stringSet < 2; stringSet++)
|
||||||
Luau::VecDeque<std::string> queue{};
|
{
|
||||||
|
auto testStrings = testStringSet[stringSet];
|
||||||
|
|
||||||
REQUIRE(queue.empty());
|
// initial capacity is not set, so this should grow to be 11
|
||||||
|
Luau::VecDeque<std::string> queue{};
|
||||||
|
|
||||||
for (int i = 5; i < 10; i++)
|
REQUIRE(queue.empty());
|
||||||
queue.push_back(testStrings[i]);
|
|
||||||
for (int i = 4; i >= 0; i--)
|
|
||||||
queue.push_front(testStrings[i]);
|
|
||||||
// buffer: 56789......01234
|
|
||||||
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
for (int i = 5; i < 10; i++)
|
||||||
REQUIRE(queue.size() == 10);
|
queue.push_back(testStrings[i]);
|
||||||
|
for (int i = 4; i >= 0; i--)
|
||||||
|
queue.push_front(testStrings[i]);
|
||||||
|
// buffer: 56789......01234
|
||||||
|
// q: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||||||
|
|
||||||
REQUIRE_EQ(queue.capacity(), 11);
|
REQUIRE(!queue.empty());
|
||||||
CHECK(!queue.is_contiguous());
|
REQUIRE(queue.size() == 10);
|
||||||
|
|
||||||
// checking that it is indeed sequential integers from 0 to 9
|
REQUIRE_EQ(queue.capacity(), 11);
|
||||||
for (int j = 0; j < 10; j++)
|
CHECK(!queue.is_contiguous());
|
||||||
CHECK_EQ(queue[j], testStrings[j]);
|
|
||||||
|
|
||||||
queue.shrink_to_fit();
|
// checking that it is indeed sequential integers from 0 to 9
|
||||||
// shrink to fit always makes a contiguous buffer
|
for (int j = 0; j < 10; j++)
|
||||||
CHECK(queue.is_contiguous());
|
CHECK_EQ(queue[j], testStrings[j]);
|
||||||
// the capacity should be exactly the size now
|
|
||||||
CHECK_EQ(queue.capacity(), queue.size());
|
|
||||||
|
|
||||||
REQUIRE(!queue.empty());
|
queue.shrink_to_fit();
|
||||||
|
// shrink to fit always makes a contiguous buffer
|
||||||
|
CHECK(queue.is_contiguous());
|
||||||
|
// the capacity should be exactly the size now
|
||||||
|
CHECK_EQ(queue.capacity(), queue.size());
|
||||||
|
|
||||||
// checking that it is still sequential integers from 0 to 9
|
REQUIRE(!queue.empty());
|
||||||
for (int j = 0; j < 10; j++)
|
|
||||||
CHECK_EQ(queue[j], testStrings[j]);
|
// checking that it is still sequential integers from 0 to 9
|
||||||
|
for (int j = 0; j < 10; j++)
|
||||||
|
CHECK_EQ(queue[j], testStrings[j]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestStruct
|
struct TestStruct
|
||||||
@ -610,6 +784,11 @@ TEST_CASE("push_front_elements_are_destroyed_correctly")
|
|||||||
queue.push_front(t);
|
queue.push_front(t);
|
||||||
REQUIRE(t.use_count() == 3); // Num of references to the TestStruct instance is now 3
|
REQUIRE(t.use_count() == 3); // Num of references to the TestStruct instance is now 3
|
||||||
// <-- call destructor here
|
// <-- call destructor here
|
||||||
|
|
||||||
|
// Extra check for correct copies
|
||||||
|
Luau::VecDeque<std::shared_ptr<TestStruct>> queue2 = queue;
|
||||||
|
Luau::VecDeque<std::shared_ptr<TestStruct>> queue3;
|
||||||
|
queue3 = queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point the destructor should be called and we should be back down to one instance of TestStruct
|
// At this point the destructor should be called and we should be back down to one instance of TestStruct
|
||||||
|
@ -2,24 +2,12 @@ AnnotationTests.typeof_expr
|
|||||||
AstQuery.last_argument_function_call_type
|
AstQuery.last_argument_function_call_type
|
||||||
AutocompleteTest.anonymous_autofilled_generic_on_argument_type_pack_vararg
|
AutocompleteTest.anonymous_autofilled_generic_on_argument_type_pack_vararg
|
||||||
AutocompleteTest.anonymous_autofilled_generic_type_pack_vararg
|
AutocompleteTest.anonymous_autofilled_generic_type_pack_vararg
|
||||||
AutocompleteTest.autocomplete_interpolated_string_as_singleton
|
|
||||||
AutocompleteTest.autocomplete_response_perf1
|
AutocompleteTest.autocomplete_response_perf1
|
||||||
AutocompleteTest.autocomplete_string_singleton_equality
|
AutocompleteTest.autocomplete_string_singleton_equality
|
||||||
AutocompleteTest.autocomplete_string_singleton_escape
|
|
||||||
AutocompleteTest.autocomplete_string_singletons
|
|
||||||
AutocompleteTest.do_wrong_compatible_nonself_calls
|
AutocompleteTest.do_wrong_compatible_nonself_calls
|
||||||
AutocompleteTest.suggest_external_module_type
|
|
||||||
AutocompleteTest.type_correct_expected_argument_type_pack_suggestion
|
|
||||||
AutocompleteTest.type_correct_expected_argument_type_suggestion
|
AutocompleteTest.type_correct_expected_argument_type_suggestion
|
||||||
AutocompleteTest.type_correct_expected_argument_type_suggestion_optional
|
|
||||||
AutocompleteTest.type_correct_expected_argument_type_suggestion_self
|
AutocompleteTest.type_correct_expected_argument_type_suggestion_self
|
||||||
AutocompleteTest.type_correct_expected_return_type_pack_suggestion
|
|
||||||
AutocompleteTest.type_correct_expected_return_type_suggestion
|
|
||||||
AutocompleteTest.type_correct_function_no_parenthesis
|
|
||||||
AutocompleteTest.type_correct_function_return_types
|
|
||||||
AutocompleteTest.type_correct_keywords
|
|
||||||
AutocompleteTest.type_correct_suggestion_for_overloads
|
AutocompleteTest.type_correct_suggestion_for_overloads
|
||||||
AutocompleteTest.type_correct_suggestion_in_argument
|
|
||||||
BuiltinTests.aliased_string_format
|
BuiltinTests.aliased_string_format
|
||||||
BuiltinTests.assert_removes_falsy_types
|
BuiltinTests.assert_removes_falsy_types
|
||||||
BuiltinTests.assert_removes_falsy_types_even_from_type_pack_tail_but_only_for_the_first_type
|
BuiltinTests.assert_removes_falsy_types_even_from_type_pack_tail_but_only_for_the_first_type
|
||||||
@ -110,6 +98,8 @@ GenericsTests.bound_tables_do_not_clone_original_fields
|
|||||||
GenericsTests.check_generic_function
|
GenericsTests.check_generic_function
|
||||||
GenericsTests.check_generic_local_function
|
GenericsTests.check_generic_local_function
|
||||||
GenericsTests.check_mutual_generic_functions
|
GenericsTests.check_mutual_generic_functions
|
||||||
|
GenericsTests.check_mutual_generic_functions_unannotated
|
||||||
|
GenericsTests.check_mutual_generic_functions_errors
|
||||||
GenericsTests.check_nested_generic_function
|
GenericsTests.check_nested_generic_function
|
||||||
GenericsTests.check_recursive_generic_function
|
GenericsTests.check_recursive_generic_function
|
||||||
GenericsTests.correctly_instantiate_polymorphic_member_functions
|
GenericsTests.correctly_instantiate_polymorphic_member_functions
|
||||||
@ -139,7 +129,6 @@ GenericsTests.infer_generic_local_function
|
|||||||
GenericsTests.infer_generic_property
|
GenericsTests.infer_generic_property
|
||||||
GenericsTests.infer_nested_generic_function
|
GenericsTests.infer_nested_generic_function
|
||||||
GenericsTests.inferred_local_vars_can_be_polytypes
|
GenericsTests.inferred_local_vars_can_be_polytypes
|
||||||
GenericsTests.instantiate_cyclic_generic_function
|
|
||||||
GenericsTests.instantiated_function_argument_names
|
GenericsTests.instantiated_function_argument_names
|
||||||
GenericsTests.local_vars_can_be_polytypes
|
GenericsTests.local_vars_can_be_polytypes
|
||||||
GenericsTests.no_stack_overflow_from_quantifying
|
GenericsTests.no_stack_overflow_from_quantifying
|
||||||
@ -347,7 +336,6 @@ TableTests.table_unifies_into_map
|
|||||||
TableTests.top_table_type
|
TableTests.top_table_type
|
||||||
TableTests.type_mismatch_on_massive_table_is_cut_short
|
TableTests.type_mismatch_on_massive_table_is_cut_short
|
||||||
TableTests.unification_of_unions_in_a_self_referential_type
|
TableTests.unification_of_unions_in_a_self_referential_type
|
||||||
TableTests.unifying_tables_shouldnt_uaf1
|
|
||||||
TableTests.used_colon_instead_of_dot
|
TableTests.used_colon_instead_of_dot
|
||||||
TableTests.used_dot_instead_of_colon
|
TableTests.used_dot_instead_of_colon
|
||||||
TableTests.when_augmenting_an_unsealed_table_with_an_indexer_apply_the_correct_scope_to_the_indexer_type
|
TableTests.when_augmenting_an_unsealed_table_with_an_indexer_apply_the_correct_scope_to_the_indexer_type
|
||||||
@ -411,7 +399,6 @@ TypeInfer.no_stack_overflow_from_isoptional
|
|||||||
TypeInfer.promote_tail_type_packs
|
TypeInfer.promote_tail_type_packs
|
||||||
TypeInfer.recursive_function_that_invokes_itself_with_a_refinement_of_its_parameter
|
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.recursive_function_that_invokes_itself_with_a_refinement_of_its_parameter_2
|
||||||
TypeInfer.statements_are_topologically_sorted
|
|
||||||
TypeInfer.stringify_nested_unions_with_optionals
|
TypeInfer.stringify_nested_unions_with_optionals
|
||||||
TypeInfer.tc_after_error_recovery_no_replacement_name_in_error
|
TypeInfer.tc_after_error_recovery_no_replacement_name_in_error
|
||||||
TypeInfer.type_infer_recursion_limit_no_ice
|
TypeInfer.type_infer_recursion_limit_no_ice
|
||||||
@ -445,7 +432,6 @@ TypeInferClasses.unions_of_intersections_of_classes
|
|||||||
TypeInferClasses.we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class
|
TypeInferClasses.we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class
|
||||||
TypeInferFunctions.another_other_higher_order_function
|
TypeInferFunctions.another_other_higher_order_function
|
||||||
TypeInferFunctions.calling_function_with_anytypepack_doesnt_leak_free_types
|
TypeInferFunctions.calling_function_with_anytypepack_doesnt_leak_free_types
|
||||||
TypeInferFunctions.check_function_bodies
|
|
||||||
TypeInferFunctions.complicated_return_types_require_an_explicit_annotation
|
TypeInferFunctions.complicated_return_types_require_an_explicit_annotation
|
||||||
TypeInferFunctions.concrete_functions_are_not_supertypes_of_function
|
TypeInferFunctions.concrete_functions_are_not_supertypes_of_function
|
||||||
TypeInferFunctions.dont_assert_when_the_tarjan_limit_is_exceeded_during_generalization
|
TypeInferFunctions.dont_assert_when_the_tarjan_limit_is_exceeded_during_generalization
|
||||||
@ -476,15 +462,12 @@ TypeInferFunctions.infer_generic_function_function_argument_overloaded
|
|||||||
TypeInferFunctions.infer_generic_lib_function_function_argument
|
TypeInferFunctions.infer_generic_lib_function_function_argument
|
||||||
TypeInferFunctions.infer_return_type_from_selected_overload
|
TypeInferFunctions.infer_return_type_from_selected_overload
|
||||||
TypeInferFunctions.infer_return_value_type
|
TypeInferFunctions.infer_return_value_type
|
||||||
TypeInferFunctions.infer_that_function_does_not_return_a_table
|
|
||||||
TypeInferFunctions.inferred_higher_order_functions_are_quantified_at_the_right_time3
|
TypeInferFunctions.inferred_higher_order_functions_are_quantified_at_the_right_time3
|
||||||
TypeInferFunctions.instantiated_type_packs_must_have_a_non_null_scope
|
TypeInferFunctions.instantiated_type_packs_must_have_a_non_null_scope
|
||||||
TypeInferFunctions.list_all_overloads_if_no_overload_takes_given_argument_count
|
TypeInferFunctions.list_all_overloads_if_no_overload_takes_given_argument_count
|
||||||
TypeInferFunctions.list_only_alternative_overloads_that_match_argument_count
|
TypeInferFunctions.list_only_alternative_overloads_that_match_argument_count
|
||||||
TypeInferFunctions.luau_subtyping_is_np_hard
|
TypeInferFunctions.luau_subtyping_is_np_hard
|
||||||
TypeInferFunctions.mutual_recursion
|
|
||||||
TypeInferFunctions.no_lossy_function_type
|
TypeInferFunctions.no_lossy_function_type
|
||||||
TypeInferFunctions.num_is_solved_after_num_or_str
|
|
||||||
TypeInferFunctions.occurs_check_failure_in_function_return_type
|
TypeInferFunctions.occurs_check_failure_in_function_return_type
|
||||||
TypeInferFunctions.other_things_are_not_related_to_function
|
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
|
||||||
@ -500,10 +483,8 @@ TypeInferFunctions.too_many_arguments
|
|||||||
TypeInferFunctions.too_many_arguments_error_location
|
TypeInferFunctions.too_many_arguments_error_location
|
||||||
TypeInferFunctions.too_many_return_values_in_parentheses
|
TypeInferFunctions.too_many_return_values_in_parentheses
|
||||||
TypeInferFunctions.too_many_return_values_no_function
|
TypeInferFunctions.too_many_return_values_no_function
|
||||||
TypeInferFunctions.toposort_doesnt_break_mutual_recursion
|
|
||||||
TypeInferLoops.cli_68448_iterators_need_not_accept_nil
|
TypeInferLoops.cli_68448_iterators_need_not_accept_nil
|
||||||
TypeInferLoops.dcr_iteration_explore_raycast_minimization
|
TypeInferLoops.dcr_iteration_explore_raycast_minimization
|
||||||
TypeInferLoops.dcr_iteration_fragmented_keys
|
|
||||||
TypeInferLoops.dcr_iteration_on_never_gives_never
|
TypeInferLoops.dcr_iteration_on_never_gives_never
|
||||||
TypeInferLoops.dcr_xpath_candidates
|
TypeInferLoops.dcr_xpath_candidates
|
||||||
TypeInferLoops.for_in_loop
|
TypeInferLoops.for_in_loop
|
||||||
@ -516,6 +497,7 @@ TypeInferLoops.for_in_loop_with_incompatible_args_to_iterator
|
|||||||
TypeInferLoops.for_in_loop_with_next
|
TypeInferLoops.for_in_loop_with_next
|
||||||
TypeInferLoops.for_in_with_an_iterator_of_type_any
|
TypeInferLoops.for_in_with_an_iterator_of_type_any
|
||||||
TypeInferLoops.for_in_with_generic_next
|
TypeInferLoops.for_in_with_generic_next
|
||||||
|
TypeInferLoops.for_in_with_just_one_iterator_is_ok
|
||||||
TypeInferLoops.for_loop
|
TypeInferLoops.for_loop
|
||||||
TypeInferLoops.ipairs_produces_integral_indices
|
TypeInferLoops.ipairs_produces_integral_indices
|
||||||
TypeInferLoops.iterate_over_free_table
|
TypeInferLoops.iterate_over_free_table
|
||||||
@ -545,7 +527,6 @@ 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.dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon
|
||||||
TypeInferOOP.inferred_methods_of_free_tables_have_the_same_level_as_the_enclosing_table
|
TypeInferOOP.inferred_methods_of_free_tables_have_the_same_level_as_the_enclosing_table
|
||||||
TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory
|
TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory
|
||||||
TypeInferOOP.method_depends_on_table
|
|
||||||
TypeInferOOP.methods_are_topologically_sorted
|
TypeInferOOP.methods_are_topologically_sorted
|
||||||
TypeInferOOP.object_constructor_can_refer_to_method_of_self
|
TypeInferOOP.object_constructor_can_refer_to_method_of_self
|
||||||
TypeInferOOP.promise_type_error_too_complex
|
TypeInferOOP.promise_type_error_too_complex
|
||||||
|
Loading…
Reference in New Issue
Block a user