Sync to upstream/release/685 (#1940)

Another week, another release!

## Analysis
- Do not warn on unknown `require`s in non-strict mode.
- Improve `Luau::dump`'s output for `DenseHashMap`.
- Raise type checking errors when we would otherwise be leaking internal
error types from modules.
- Fix a crash that would sometimes occur when calling a function with an
incomplete type.
- Replace uses of `FFlag::LuauSolverV2` in `ClonePublicInterface` with a
`solverMode` field.
- Limit the number of constraints that can be dynamically created to
fail more gracefully in complex cases.
- Fix #1932.

---------

Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Sora Kanosue <skanosue@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
Varun Saini 2025-08-01 14:44:50 -07:00 committed by GitHub
parent eaab9c4363
commit f3f3bf8f72
Signed by: DevComp
GPG key ID: B5690EEEBB952194
28 changed files with 594 additions and 310 deletions

View file

@ -102,6 +102,11 @@ struct ConstraintSolver
// scope tree.
std::vector<std::unique_ptr<Constraint>> solverConstraints;
// Ticks downward toward zero each time a new constraint is pushed into
// solverConstraints. When this counter reaches zero, the type inference
// engine reports a CodeTooComplex error and aborts.
size_t solverConstraintLimit = 0;
// This includes every constraint that has not been fully solved.
// A constraint can be both blocked and unsolved, for instance.
std::vector<NotNull<const Constraint>> unsolvedConstraints;

View file

@ -113,7 +113,8 @@ struct FrontendOptions
bool applyInternalLimitScaling = false;
// An optional callback which is called for every *dirty* module was checked
// Is multi-threaded typechecking is used, this callback might be called from multiple threads and has to be thread-safe
// If multi-threaded typechecking is used, this callback might be called
// from multiple threads and has to be thread-safe
std::function<void(const SourceModule& sourceModule, const Luau::Module& module)> customModuleCheck;
bool collectTypeAllocationStats = false;

View file

@ -359,6 +359,9 @@ inline constexpr char kLuauPrint[] = "_luau_print";
// a constraint solving incomplete error to test semantics around that specific
// error.
inline constexpr char kLuauForceConstraintSolvingIncomplete[] = "_luau_force_constraint_solving_incomplete";
// `_luau_blocked_type` will cause us to always mint a blocked type that does
// not get emplaced by constraint solving.
inline constexpr char kLuauBlockedType[] = "_luau_blocked_type";
} // namespace Luau

View file

@ -34,7 +34,7 @@ struct UnifierCounters
struct UnifierSharedState
{
UnifierSharedState(InternalErrorReporter* iceHandler)
explicit UnifierSharedState(InternalErrorReporter* iceHandler)
: iceHandler(iceHandler)
{
}

View file

@ -1465,6 +1465,12 @@ struct AstJsonEncoder : public AstVisitor
return false;
}
bool visit(class AstTypeOptional* node) override
{
write(node);
return false;
}
bool visit(class AstTypeUnion* node) override
{
write(node);

View file

@ -6,7 +6,7 @@
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauForceSimplifyConstraint)
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
namespace Luau
{
@ -66,7 +66,7 @@ struct ReferenceCountInitializer_DEPRECATED : TypeOnceVisitor
bool visit(TypeId, const TypeFunctionInstanceType& tfit) override
{
if (FFlag::LuauForceSimplifyConstraint)
if (FFlag::LuauForceSimplifyConstraint2)
return tfit.function->canReduceGenerics;
else
return FFlag::LuauEagerGeneralization4 && traverseIntoTypeFunctions;
@ -121,7 +121,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
bool visit(TypeId, const TypeFunctionInstanceType& tfit) override
{
if (FFlag::LuauForceSimplifyConstraint)
if (FFlag::LuauForceSimplifyConstraint2)
return tfit.function->canReduceGenerics;
else
return FFlag::LuauEagerGeneralization4 && traverseIntoTypeFunctions;

View file

@ -41,7 +41,6 @@ LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment)
@ -51,6 +50,7 @@ LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAGVARIABLE(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAGVARIABLE(LuauInferActualIfElseExprType)
LUAU_FASTFLAGVARIABLE(LuauDoNotPrototypeTableIndex)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
namespace Luau
{
@ -1292,7 +1292,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFor* for_)
visit(forScope, for_->body);
if (FFlag::LuauDfgAllowUpdatesInLoops)
scope->inheritAssignments(forScope);
return ControlFlow::None;
@ -1353,7 +1352,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatForIn* forI
visit(loopScope, forIn->body);
Checkpoint end = checkpoint(this);
if (FFlag::LuauDfgAllowUpdatesInLoops)
scope->inheritAssignments(loopScope);
// This iter constraint must dispatch first.
@ -1379,7 +1377,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatWhile* whil
visit(whileScope, while_->body);
if (FFlag::LuauDfgAllowUpdatesInLoops)
scope->inheritAssignments(whileScope);
return ControlFlow::None;
@ -1393,7 +1390,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatRepeat* rep
check(repeatScope, repeat->condition);
if (FFlag::LuauDfgAllowUpdatesInLoops)
scope->inheritAssignments(repeatScope);
return ControlFlow::None;
@ -1997,6 +1993,14 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte
}
);
if (FFlag::LuauLimitDynamicConstraintSolving)
{
// If we don't emplace an error type here, then later we'll be
// exposing a blocked type in this file's type interface. This
// is _normally_ harmless.
emplaceType<BoundType>(asMutable(bindingIt->second.type), builtinTypes->errorType);
}
return ControlFlow::None;
}
}
@ -3771,6 +3775,10 @@ TypeId ConstraintGenerator::resolveReferenceType(
else
return resolveType_(scope, ref->parameters.data[0].type, inTypeArguments);
}
else if (FFlag::LuauLimitDynamicConstraintSolving && ref->name == "_luau_blocked_type")
{
return arena->addType(BlockedType{});
}
}
std::optional<TypeFun> alias;

View file

@ -26,11 +26,13 @@
#include <algorithm>
#include <utility>
LUAU_FASTINTVARIABLE(LuauSolverConstraintLimit, 1000)
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
LUAU_FASTFLAGVARIABLE(DebugLuauAssertOnForcedConstraint)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverIncludeDependencies)
LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch)
@ -41,8 +43,10 @@ LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeCheckFunctionCalls)
LUAU_FASTFLAGVARIABLE(LuauUseOrderedTypeSetsInConstraints)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauAvoidExcessiveTypeCopying)
LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint)
LUAU_FASTFLAGVARIABLE(LuauForceSimplifyConstraint2)
LUAU_FASTFLAGVARIABLE(LuauCollapseShouldNotCrash)
LUAU_FASTFLAGVARIABLE(LuauContainsAnyGenericFollowBeforeChecking)
LUAU_FASTFLAGVARIABLE(LuauLimitDynamicConstraintSolving)
namespace Luau
{
@ -333,6 +337,7 @@ ConstraintSolver::ConstraintSolver(
, rootScope(constraintSet.rootScope)
, currentModuleName(std::move(moduleName))
, dfg(dfg)
, solverConstraintLimit(FInt::LuauSolverConstraintLimit)
, moduleResolver(moduleResolver)
, requireCycles(std::move(requireCycles))
, logger(logger)
@ -367,6 +372,7 @@ ConstraintSolver::ConstraintSolver(
, rootScope(rootScope)
, currentModuleName(std::move(moduleName))
, dfg(dfg)
, solverConstraintLimit(FInt::LuauSolverConstraintLimit)
, moduleResolver(moduleResolver)
, requireCycles(std::move(requireCycles))
, logger(logger)
@ -461,6 +467,11 @@ void ConstraintSolver::run()
if (limits.cancellationToken && limits.cancellationToken->requested())
throwUserCancelError();
// If we were _given_ a limit, and the current limit has hit zero, ]
// then early exit from constraint solving.
if (FFlag::LuauLimitDynamicConstraintSolving && FInt::LuauSolverConstraintLimit > 0 && solverConstraintLimit == 0)
break;
std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{};
StepSnapshot snapshot;
@ -1528,7 +1539,9 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
auto it = begin(t);
auto endIt = end(t);
LUAU_ASSERT(it != endIt);
if (FFlag::LuauCollapseShouldNotCrash && it == endIt)
return std::nullopt;
TypeId fst = follow(*it);
while (it != endIt)
{
@ -2940,14 +2953,14 @@ bool ConstraintSolver::tryDispatch(const SimplifyConstraint& c, NotNull<const Co
FindAllUnionMembers finder;
finder.traverse(target);
// Clip this comment with LuauForceSimplifyConstraint
// Clip this comment with LuauForceSimplifyConstraint2
//
// The flagging logic is roughly: if `LuauForceSimplifyConstraint` is
// The flagging logic is roughly: if `LuauForceSimplifyConstraint2` is
// _not_ set, then we ignore the input `force`, as the RHS of the &&
// is always true. Otherwise, when the flag is set, the RHS of the &&
// is equivalent to `!force`: we only block on types when we're not
// being force solved.
if (!finder.blockedTys.empty() && !(FFlag::LuauForceSimplifyConstraint && force))
if (!finder.blockedTys.empty() && !(FFlag::LuauForceSimplifyConstraint2 && force))
{
for (TypeId ty : finder.blockedTys)
block(ty, constraint);
@ -2961,6 +2974,18 @@ bool ConstraintSolver::tryDispatch(const SimplifyConstraint& c, NotNull<const Co
continue;
result = simplifyUnion(constraint->scope, constraint->location, result, ty);
}
if (FFlag::LuauForceSimplifyConstraint2)
{
// If we forced, then there _may_ be blocked types, and we should
// include those in the union as well.
for (TypeId ty : finder.blockedTys)
{
ty = follow(ty);
if (ty == target)
continue;
result = simplifyUnion(constraint->scope, constraint->location, result, ty);
}
}
emplaceType<BoundType>(asMutable(target), result);
return true;
}
@ -3832,6 +3857,17 @@ NotNull<Constraint> ConstraintSolver::pushConstraint(NotNull<Scope> scope, const
solverConstraints.push_back(std::move(c));
unsolvedConstraints.emplace_back(borrow);
if (FFlag::LuauLimitDynamicConstraintSolving)
{
if (solverConstraintLimit > 0)
{
--solverConstraintLimit;
if (solverConstraintLimit == 0)
reportError(CodeTooComplex{}, location);
}
}
return borrow;
}
@ -3899,7 +3935,7 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
if (!isReferenceCountedType(target))
return;
if (FFlag::LuauForceSimplifyConstraint)
if (FFlag::LuauForceSimplifyConstraint2)
{
// This can happen in the _very_ specific case of:
//

View file

@ -14,7 +14,6 @@
LUAU_FASTFLAG(DebugLuauFreezeArena)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
LUAU_FASTFLAGVARIABLE(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAG(LuauFragmentAutocompleteTracksRValueRefinements)
LUAU_FASTFLAGVARIABLE(LuauDfgForwardNilFromAndOr)
@ -157,33 +156,13 @@ void DfgScope::inherit(const DfgScope* childScope)
bool DfgScope::canUpdateDefinition(Symbol symbol) const
{
if (FFlag::LuauDfgAllowUpdatesInLoops)
return true;
for (const DfgScope* current = this; current; current = current->parent)
{
if (current->bindings.find(symbol))
return true;
else if (current->scopeType == DfgScope::Loop)
return false;
}
// NOTE: Vestigial as of clipping LuauDfgAllowUpdatesInLoops
return true;
}
bool DfgScope::canUpdateDefinition(DefId def, const std::string& key) const
{
if (FFlag::LuauDfgAllowUpdatesInLoops)
return true;
for (const DfgScope* current = this; current; current = current->parent)
{
if (auto props = current->props.find(def))
return true;
else if (current->scopeType == DfgScope::Loop)
return false;
}
// NOTE: Vestigial as of clipping LuauDfgAllowUpdatesInLoops
return true;
}
@ -541,9 +520,6 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
// allow a string to flow into a position that expects.
DfgScope* whileScope = makeChildScope(DfgScope::Loop);
if (FFlag::LuauDfgAllowUpdatesInLoops)
{
ControlFlow cf;
{
PushScope ps{scopeStack, whileScope};
@ -558,22 +534,6 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
join(scope, scope, whileScope);
return ControlFlow::None;
}
else
{
{
PushScope ps{scopeStack, whileScope};
visitExpr(w->condition);
visit(w->body);
}
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->inherit(whileScope);
else
currentScope_DEPRECATED()->inherit(whileScope);
return ControlFlow::None;
}
}
ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
@ -582,8 +542,6 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
// does not consider the _second_ loop iteration.
DfgScope* repeatScope = makeChildScope(DfgScope::Loop);
if (FFlag::LuauDfgAllowUpdatesInLoops)
{
ControlFlow cf;
{
@ -601,22 +559,6 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
// control flow, but if it's throws or returns, then we need to
// return _that_ to the parent.
return matches(cf, ControlFlow::Breaks | ControlFlow::Continues) ? ControlFlow::None : cf;
}
else
{
{
PushScope ps{scopeStack, repeatScope};
visitBlockWithoutChildScope(r->body);
visitExpr(r->condition);
}
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->inherit(repeatScope);
else
currentScope_DEPRECATED()->inherit(repeatScope);
return ControlFlow::None;
}
}
ControlFlow DataFlowGraphBuilder::visit(AstStatBreak* b)
@ -694,9 +636,6 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
if (f->step)
visitExpr(f->step);
if (FFlag::LuauDfgAllowUpdatesInLoops)
{
ControlFlow cf;
{
PushScope ps{scopeStack, forScope};
@ -719,43 +658,12 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
join(scope, scope, forScope);
return ControlFlow::None;
}
else
{
{
PushScope ps{scopeStack, forScope};
if (f->var->annotation)
visitType(f->var->annotation);
DefId def = defArena->freshCell(f->var, f->var->location);
graph.localDefs[f->var] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[f->var] = def;
else
currentScope_DEPRECATED()->bindings[f->var] = def;
captures[f->var].allVersions.push_back(def);
// TODO(controlflow): entry point has a back edge from exit point
visit(f->body);
}
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->inherit(forScope);
else
currentScope_DEPRECATED()->inherit(forScope);
return ControlFlow::None;
}
}
ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
{
DfgScope* forScope = makeChildScope(DfgScope::Loop);
if (FFlag::LuauDfgAllowUpdatesInLoops)
{
ControlFlow cf;
{
PushScope ps{scopeStack, forScope};
@ -789,40 +697,6 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
join(scope, scope, forScope);
return ControlFlow::None;
}
else
{
{
PushScope ps{scopeStack, forScope};
for (AstLocal* local : f->vars)
{
if (local->annotation)
visitType(local->annotation);
DefId def = defArena->freshCell(local, local->location);
graph.localDefs[local] = def;
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->bindings[local] = def;
else
currentScope_DEPRECATED()->bindings[local] = def;
captures[local].allVersions.push_back(def);
}
// TODO(controlflow): entry point has a back edge from exit point
// We're gonna need a `visitExprList` and `visitVariadicExpr` (function calls and `...`)
for (AstExpr* e : f->values)
visitExpr(e);
visit(f->body);
}
if (FFlag::LuauDfgScopeStackNotNull)
currentScope()->inherit(forScope);
else
currentScope_DEPRECATED()->inherit(forScope);
return ControlFlow::None;
}
}
ControlFlow DataFlowGraphBuilder::visit(AstStatAssign* a)

View file

@ -49,6 +49,7 @@ LUAU_FASTFLAGVARIABLE(LuauTrackTypeAllocations)
LUAU_FASTFLAGVARIABLE(LuauUseWorkspacePropToChooseSolver)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
LUAU_FASTFLAGVARIABLE(DebugLuauAlwaysShowConstraintSolvingIncomplete)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
namespace Luau
{
@ -1653,12 +1654,50 @@ ModulePtr check(
sourceModule.root->visit(&etv);
}
// NOTE: This used to be done prior to cloning the public interface, but
// we now replace "internal" types with `*error-type*`.
if (FFlag::LuauLimitDynamicConstraintSolving)
{
if (FFlag::DebugLuauForbidInternalTypes)
{
InternalTypeFinder finder;
// `result->returnType` is not filled in yet, so we
// traverse the return type of the root module.
finder.traverse(result->getModuleScope()->returnType);
for (const auto& [_, binding] : result->exportedTypeBindings)
finder.traverse(binding.type);
for (const auto& [_, ty] : result->astTypes)
finder.traverse(ty);
for (const auto& [_, ty] : result->astExpectedTypes)
finder.traverse(ty);
for (const auto& [_, tp] : result->astTypePacks)
finder.traverse(tp);
for (const auto& [_, ty] : result->astResolvedTypes)
finder.traverse(ty);
for (const auto& [_, ty] : result->astOverloadResolvedTypes)
finder.traverse(ty);
for (const auto& [_, tp] : result->astResolvedTypePacks)
finder.traverse(tp);
}
}
unfreeze(result->interfaceTypes);
if (FFlag::LuauUseWorkspacePropToChooseSolver)
result->clonePublicInterface(builtinTypes, *iceHandler, SolverMode::New);
else
result->clonePublicInterface_DEPRECATED(builtinTypes, *iceHandler);
if (!FFlag::LuauLimitDynamicConstraintSolving)
{
if (FFlag::DebugLuauForbidInternalTypes)
{
InternalTypeFinder finder;
@ -1686,6 +1725,7 @@ ModulePtr check(
for (const auto& [_, tp] : result->astResolvedTypePacks)
finder.traverse(tp);
}
}
// It would be nice if we could freeze the arenas before doing type
// checking, but we'll have to do some work to get there.

View file

@ -15,6 +15,8 @@
#include <algorithm>
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauUseWorkspacePropToChooseSolver)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
namespace Luau
{
@ -95,6 +97,9 @@ struct ClonePublicInterface : Substitution
{
NotNull<BuiltinTypes> builtinTypes;
NotNull<Module> module;
// NOTE: This can be made non-optional after
// LuauUseWorkspacePropToChooseSolver is clipped.
std::optional<SolverMode> solverMode{std::nullopt};
ClonePublicInterface(const TxnLog* log, NotNull<BuiltinTypes> builtinTypes, Module* module)
: Substitution(log, &module->interfaceTypes)
@ -104,6 +109,20 @@ struct ClonePublicInterface : Substitution
LUAU_ASSERT(module);
}
ClonePublicInterface(const TxnLog* log, NotNull<BuiltinTypes> builtinTypes, Module* module, SolverMode solverMode)
: Substitution(log, &module->interfaceTypes)
, builtinTypes(builtinTypes)
, module(module)
, solverMode(solverMode)
{
LUAU_ASSERT(module);
}
bool isNewSolver() const
{
return FFlag::LuauSolverV2 || (FFlag::LuauUseWorkspacePropToChooseSolver && solverMode == SolverMode::New);
}
bool isDirty(TypeId ty) override
{
if (ty->owningArena == &module->internalTypes)
@ -157,11 +176,30 @@ struct ClonePublicInterface : Substitution
else if (TableType* ttv = getMutable<TableType>(result))
{
ttv->level = TypeLevel{0, 0};
if (FFlag::LuauSolverV2)
if (isNewSolver())
ttv->scope = nullptr;
}
if (FFlag::LuauSolverV2)
if (isNewSolver())
{
if (FFlag::LuauLimitDynamicConstraintSolving)
{
if (is<FreeType, BlockedType, PendingExpansionType>(ty))
{
module->errors.emplace_back(
Location{}, // Not amazing but the best we can do.
module->name,
InternalError{"An internal type is escaping this module; please report this bug at "
"https://github.com/luau-lang/luau/issues"}
);
result = builtinTypes->errorType;
}
else if (auto genericty = getMutable<GenericType>(result))
{
genericty->scope = nullptr;
}
}
else
{
if (auto freety = getMutable<FreeType>(result))
{
@ -178,14 +216,34 @@ struct ClonePublicInterface : Substitution
genericty->scope = nullptr;
}
}
}
return result;
}
TypePackId clean(TypePackId tp) override
{
if (FFlag::LuauSolverV2)
if (isNewSolver())
{
if (FFlag::LuauLimitDynamicConstraintSolving)
{
if (is<FreeTypePack, BlockedTypePack>(tp))
{
module->errors.emplace_back(
Location{},
module->name,
InternalError{"An internal type pack is escaping this module; please report this bug at "
"https://github.com/luau-lang/luau/issues"}
);
return builtinTypes->errorTypePack;
}
auto clonedTp = clone(tp);
if (auto gtp = getMutable<GenericTypePack>(clonedTp))
gtp->scope = nullptr;
return clonedTp;
}
auto clonedTp = clone(tp);
if (auto ftp = getMutable<FreeTypePack>(clonedTp))
{
@ -200,6 +258,7 @@ struct ClonePublicInterface : Substitution
else if (auto gtp = getMutable<GenericTypePack>(clonedTp))
gtp->scope = nullptr;
return clonedTp;
}
else
{
@ -325,7 +384,7 @@ void Module::clonePublicInterface(NotNull<BuiltinTypes> builtinTypes, InternalEr
std::optional<TypePackId> varargPack = mode == SolverMode::New ? std::nullopt : moduleScope->varargPack;
TxnLog log;
ClonePublicInterface clonePublicInterface{&log, builtinTypes, this};
ClonePublicInterface clonePublicInterface{&log, builtinTypes, this, mode};
returnType = clonePublicInterface.cloneTypePack(returnType);

View file

@ -25,6 +25,7 @@ LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictMoreUnknownSymbols)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictNoErrorsPassingNever)
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictSuppressesDynamicRequireErrors)
namespace Luau
{
@ -1272,6 +1273,22 @@ void checkNonStrict(
typeChecker.visit(sourceModule.root);
unfreeze(module->interfaceTypes);
copyErrors(module->errors, module->interfaceTypes, builtinTypes);
if (FFlag::LuauNewNonStrictSuppressesDynamicRequireErrors)
{
module->errors.erase(
std::remove_if(
module->errors.begin(),
module->errors.end(),
[](auto err)
{
return get<UnknownRequire>(err) != nullptr;
}
),
module->errors.end()
);
}
freeze(module->interfaceTypes);
}

View file

@ -23,6 +23,7 @@ LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
LUAU_FASTFLAGVARIABLE(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAGVARIABLE(LuauMissingFollowMappedGenericPacks)
namespace Luau
{
@ -975,7 +976,10 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2)
{
const TypePack* tp = get<TypePack>(*other);
if (const VariadicTypePack* vtp = tp ? get<VariadicTypePack>(tp->tail) : nullptr; vtp && vtp->hidden)
if (const VariadicTypePack* vtp = tp
? get<VariadicTypePack>(
FFlag::LuauMissingFollowMappedGenericPacks ? follow(tp->tail) : tp->tail)
: nullptr; vtp && vtp->hidden)
{
TypePackId taillessTp = arena->addTypePack(tp->head);
results.push_back(isCovariantWith(env, taillessTp, superTailPack, scope)
@ -1066,7 +1070,10 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypePackId
if (FFlag::LuauReturnMappedGenericPacksFromSubtyping2)
{
const TypePack* tp = get<TypePack>(*other);
if (const VariadicTypePack* vtp = tp ? get<VariadicTypePack>(tp->tail) : nullptr; vtp && vtp->hidden)
if (const VariadicTypePack* vtp = tp
? get<VariadicTypePack>(
FFlag::LuauMissingFollowMappedGenericPacks ? follow(tp->tail) : tp->tail)
: nullptr; vtp && vtp->hidden)
{
TypePackId taillessTp = arena->addTypePack(tp->head);
results.push_back(isCovariantWith(env, subTailPack, taillessTp, scope)

View file

@ -1916,7 +1916,7 @@ std::string dump(DenseHashMap<TypeId, TypeId>& types)
ToStringOptions& opts = dumpOptions();
for (const auto& [key, value] : types)
{
if (s.length() == 1)
if (s.length() > 1)
s += ", ";
s += toString(key, opts) + " : " + toString(value, opts);
}
@ -1930,7 +1930,7 @@ std::string dump(DenseHashMap<TypePackId, TypePackId>& types)
ToStringOptions& opts = dumpOptions();
for (const auto& [key, value] : types)
{
if (s.length() == 1)
if (s.length() > 1)
s += ", ";
s += toString(key, opts) + " : " + toString(value, opts);
}

View file

@ -41,6 +41,8 @@ LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(LuauInferActualIfElseExprType)
LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
LUAU_FASTFLAGVARIABLE(LuauIceLess)
namespace Luau
{
@ -1551,7 +1553,15 @@ void TypeChecker2::visitCall(AstExprCall* call)
{
AstExprIndexName* indexExpr = call->func->as<AstExprIndexName>();
if (!indexExpr)
{
if (FFlag::LuauIceLess)
{
reportError(InternalError{"method call expression has no 'self'"}, call->location);
return;
}
else
ice->ice("method call expression has no 'self'");
}
args.head.push_back(lookupType(indexExpr->expr));
argExprs.push_back(indexExpr->expr);
@ -1950,12 +1960,26 @@ void TypeChecker2::visit(AstExprFunction* fn)
}
else if (!normalizedFnTy->hasFunctions())
{
if (FFlag::LuauIceLess)
{
reportError(InternalError{"Internal error: Lambda has non-function type " + toString(inferredFnTy)}, fn->location);
return;
}
else
ice->ice("Internal error: Lambda has non-function type " + toString(inferredFnTy), fn->location);
}
else
{
if (1 != normalizedFnTy->functions.parts.size())
{
if (FFlag::LuauIceLess)
{
reportError(InternalError{"Unexpected: Lambda has unexpected type " + toString(inferredFnTy)}, fn->location);
return;
}
else
ice->ice("Unexpected: Lambda has unexpected type " + toString(inferredFnTy), fn->location);
}
const FunctionType* inferredFtv = get<FunctionType>(normalizedFnTy->functions.parts.front());
LUAU_ASSERT(inferredFtv);
@ -2591,6 +2615,11 @@ TypeId TypeChecker2::flattenPack(TypePackId pack)
return builtinTypes->errorType;
else if (finite(pack) && size(pack) == 0)
return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil`
else if (FFlag::LuauIceLess)
{
reportError(InternalError{"flattenPack got a weird pack!"}, Location{});
return builtinTypes->errorType; // todo test this
}
else
ice->ice("flattenPack got a weird pack!");
}
@ -2648,7 +2677,7 @@ void TypeChecker2::visit(AstTypeReference* ty)
{
// No further validation is necessary in this case. The main logic for
// _luau_print is contained in lookupAnnotation.
if (FFlag::DebugLuauMagicTypes && (ty->name == kLuauPrint || ty->name == kLuauForceConstraintSolvingIncomplete))
if (FFlag::DebugLuauMagicTypes && (ty->name == kLuauPrint || ty->name == kLuauForceConstraintSolvingIncomplete || ty->name == kLuauBlockedType))
return;
for (const AstTypeOrPack& param : ty->parameters)
@ -2938,7 +2967,15 @@ Reasonings TypeChecker2::explainReasonings_(TID subTy, TID superTy, Location loc
: traverse_DEPRECATED(superTy, reasoning.superPath, builtinTypes);
if (!optSubLeaf || !optSuperLeaf)
{
if (FFlag::LuauIceLess)
{
reportError(InternalError{"Subtyping test returned a reasoning with an invalid path"}, location);
return {}; // TOOD test this
}
else
ice->ice("Subtyping test returned a reasoning with an invalid path", location);
}
const TypeOrPack& subLeaf = *optSubLeaf;
const TypeOrPack& superLeaf = *optSuperLeaf;
@ -2950,7 +2987,15 @@ Reasonings TypeChecker2::explainReasonings_(TID subTy, TID superTy, Location loc
auto superLeafTp = get<TypePackId>(superLeaf);
if (!subLeafTy && !superLeafTy && !subLeafTp && !superLeafTp)
{
if (FFlag::LuauIceLess)
{
reportError(InternalError{"Subtyping test returned a reasoning where one path ends at a type and the other ends at a pack."}, location);
return {}; // TODO test this?
}
else
ice->ice("Subtyping test returned a reasoning where one path ends at a type and the other ends at a pack.", location);
}
std::string relation = "a subtype of";
if (reasoning.variance == SubtypingVariance::Invariant)

View file

@ -592,4 +592,18 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypePackWithDefault")
CHECK(toJson(root->body.data[0]) == expected);
}
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeOptional")
{
AstStatBlock* root = expectParse(R"(
type Foo = string?
)");
CHECK(1 == root->body.size);
std::string_view expected =
R"({"type":"AstStatTypeAlias","location":"1,12 - 1,30","name":"Foo","generics":[],"genericPacks":[],"value":{"type":"AstTypeUnion","location":"1,23 - 1,30","types":[{"type":"AstTypeReference","location":"1,23 - 1,29","name":"string","nameLocation":"1,23 - 1,29","parameters":[]},{"type":"AstTypeOptional","location":"1,29 - 1,30"}]},"exported":false})";
CHECK(toJson(root->body.data[0]) == expected);
}
TEST_SUITE_END();

View file

@ -13,7 +13,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
struct DataFlowGraphFixture
{
@ -129,8 +128,6 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi")
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_while")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
dfg(R"(
local x
@ -170,8 +167,6 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_while")
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_repeat")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
dfg(R"(
local x
@ -210,8 +205,6 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_repeat")
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
dfg(R"(
local x
@ -251,8 +244,6 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for")
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_not_owned_by_for_in")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
dfg(R"(
local x
@ -292,8 +283,6 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_local_owned_by_for_in")
TEST_CASE_FIXTURE(DataFlowGraphFixture, "mutate_preexisting_property_not_owned_by_while")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
dfg(R"(
local t = {}
t.x = 5

View file

@ -18,6 +18,7 @@
LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks)
LUAU_FASTFLAG(LuauNewNonStrictMoreUnknownSymbols)
LUAU_FASTFLAG(LuauNewNonStrictNoErrorsPassingNever)
LUAU_FASTFLAG(LuauNewNonStrictSuppressesDynamicRequireErrors)
using namespace Luau;
@ -814,5 +815,51 @@ TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_one_sided_conditionals")
CHECK_EQ(err->context, UnknownSymbol::Context::Binding);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "new_non_strict_should_suppress_dynamic_require_errors")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauNewNonStrictSuppressesDynamicRequireErrors, true}};
// Avoid warning about dynamic requires in new nonstrict mode
CheckResult result = check(Mode::Nonstrict, R"(
function passThrough(module)
require(module)
end
)");
LUAU_REQUIRE_ERROR_COUNT(0, result);
// We should still warn about dynamic requires in strict mode
result = check(Mode::Strict, R"(
function passThrough(module)
require(module)
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
const UnknownRequire* req = get<UnknownRequire>(result.errors[0]);
CHECK(req != nullptr);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "new_non_strict_should_suppress_unknown_require_errors")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauNewNonStrictSuppressesDynamicRequireErrors, true}};
// Avoid warning about dynamic requires in new nonstrict mode
CheckResult result = check(Mode::Nonstrict, R"(
require(script.NonExistent)
require("@self/NonExistent")
)");
LUAU_REQUIRE_ERROR_COUNT(0, result);
// We should still warn about dynamic requires in strict mode
result = check(Mode::Strict, R"(
require(script.NonExistent)
require("@self/NonExistent")
)");
LUAU_REQUIRE_ERROR_COUNT(2, result);
const UnknownRequire* req1 = get<UnknownRequire>(result.errors[0]);
CHECK(req1 != nullptr);
const UnknownRequire* req2 = get<UnknownRequire>(result.errors[1]);
CHECK(req2 != nullptr);
}
TEST_SUITE_END();

View file

@ -17,11 +17,15 @@
using namespace Luau;
LUAU_FASTINT(LuauSolverConstraintLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauIceLess)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(LuauSimplifyAnyAndUnion)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
struct LimitFixture : BuiltinsFixture
{
@ -333,4 +337,36 @@ TEST_CASE_FIXTURE(LimitFixture, "Signal_exerpt" * doctest::timeout(0.5))
(void)result;
}
TEST_CASE_FIXTURE(Fixture, "limit_number_of_dynamically_created_constraints")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauLimitDynamicConstraintSolving, true},
};
constexpr const char* src = R"(
type Array<T> = {T}
type Hello = Array<Array<Array<Array<Array<Array<Array<Array<Array<Array<number>>>>>>>>>>
)";
{
ScopedFastInt sfi{FInt::LuauSolverConstraintLimit, 1};
CheckResult result = check(src);
LUAU_CHECK_ERROR(result, CodeTooComplex);
}
{
ScopedFastInt sfi{FInt::LuauSolverConstraintLimit, 1000};
CheckResult result = check(src);
LUAU_CHECK_NO_ERRORS(result);
}
{
ScopedFastInt sfi{FInt::LuauSolverConstraintLimit, 0};
CheckResult result = check(src);
LUAU_CHECK_NO_ERRORS(result);
}
}
TEST_SUITE_END();

View file

@ -14,7 +14,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps)
TEST_SUITE_BEGIN("TypeInferAnyError");
@ -305,8 +304,6 @@ TEST_CASE_FIXTURE(Fixture, "chain_calling_error_type_yields_error")
TEST_CASE_FIXTURE(BuiltinsFixture, "replace_every_free_type_when_unifying_a_complex_function_with_any")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
CheckResult result = check(R"(
local a: any
local b

View file

@ -23,6 +23,7 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauCollapseShouldNotCrash)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauFormatUseLastPosition)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
@ -3306,4 +3307,42 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "generic_function_statement")
CHECK_EQ("a", toString(requireTypeAtPosition({9, 21})));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "function_calls_should_not_crash")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
// crash only happens right now with eager generalization off
{FFlag::LuauEagerGeneralization4, false},
{FFlag::LuauCollapseShouldNotCrash, true},
};
CheckResult result = check(R"(
return {
StartAPI = function()
local pointers = {}
local API = {}
local function getRealEnvResult(PointerOrPath)
if pointers[PointerOrPath] then
return pointers[PointerOrPath]
end
end
API.OnInvoke = function()
local realEnvResult, isResultPointer = getRealEnvResult(FunctionInEnvToRunPath)
return realEnvResult(table.unpack(args, 2, args.n))
if TableInEnvPath and type(TableInEnvPath) == 'string' then
local realEnvResult, isResultPointer = getRealEnvResult(TableInEnvPath)
return getmetatable(realEnvResult)
end
local realEnvResult, isResultPointer = getRealEnvResult(TableInEnvPath)
local metaTableInEnv = getmetatable(realEnvResult)
local result = metaTableInEnv[FuncToRun](realEnvResult,table.unpack(args, 3, args.n))
end
end
}
)");
// no expected behavior here beyond not crashing
}
TEST_SUITE_END();

View file

@ -16,7 +16,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
TEST_SUITE_BEGIN("TypeInferLoops");
@ -183,10 +182,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next")
}
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_loop_with_next_and_multiple_elements")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSimplifyOutOfLine2, true},
{FFlag::LuauDfgAllowUpdatesInLoops, true},
};
ScopedFastFlag _{FFlag::LuauSimplifyOutOfLine2, true};
CheckResult result = check(R"(
local n
@ -1106,8 +1102,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dcr_iteration_on_never_gives_never")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
CheckResult result = check(R"(
local iter: never
local ans
@ -1329,8 +1323,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_require")
TEST_CASE_FIXTURE(Fixture, "oss_1480")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
type Part = { Parent: Part? }
type Instance = Part
@ -1346,8 +1338,6 @@ TEST_CASE_FIXTURE(Fixture, "oss_1480")
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1413")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local function KahanSum(values: {number}): number
local sum: number = 0
@ -1401,10 +1391,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "while_loop_error_in_body")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag sffs[] = {
{FFlag::LuauDfgAllowUpdatesInLoops, true},
};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local function foo()
local x = ""
@ -1421,10 +1407,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "while_loop_error_in_body")
TEST_CASE_FIXTURE(BuiltinsFixture, "while_loop_assign_different_type")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauDfgAllowUpdatesInLoops, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local function takesString(_: string) end
@ -1445,8 +1428,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "while_loop_assign_different_type")
TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_loop_assignment")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local x = nil
repeat
@ -1460,8 +1441,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_loop_assignment")
TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_loop_assignment_with_break")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local x = nil
repeat
@ -1475,8 +1454,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_loop_assignment_with_break")
TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_unconditionally_fires_error")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local x = nil
repeat
@ -1495,8 +1472,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_is_linearish")
if (!FFlag::LuauSolverV2)
return;
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local x = nil
if math.random () > 0.5 then
@ -1515,10 +1490,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "repeat_is_linearish")
TEST_CASE_FIXTURE(Fixture, "ensure_local_in_loop_does_not_escape")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauDfgAllowUpdatesInLoops, true},
};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local x = 42
repeat

View file

@ -14,6 +14,8 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauLimitDynamicConstraintSolving)
using namespace Luau;
@ -847,4 +849,22 @@ return wrapper(test2, 1, "")
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "internal_types_are_scrubbed_from_module")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::DebugLuauMagicTypes, true},
{FFlag::LuauLimitDynamicConstraintSolving, true}
};
fileResolver.source["game/A"] = R"(
return function(): _luau_blocked_type return nil :: any end
)";
CheckResult result = getFrontend().check("game/A");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<InternalError>(result.errors[0]));
CHECK("(...any) -> *error-type*" == toString(getFrontend().moduleResolver.getModule("game/A")->returnType));
}
TEST_SUITE_END();

View file

@ -18,7 +18,6 @@ LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAG(LuauSolverAgnosticStringification)
TEST_SUITE_BEGIN("ProvisionalTests");
@ -1340,10 +1339,8 @@ TEST_CASE_FIXTURE(Fixture, "we_cannot_infer_functions_that_return_inconsistently
TEST_CASE_FIXTURE(Fixture, "loop_unsoundness")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauDfgAllowUpdatesInLoops, true},
};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
// This is a tactical unsoundness we're introducing to resolve issues around
// cyclic types. You can see that if this loop were to run more than once,
// we'd error as we'd try to call a number.

View file

@ -20,7 +20,9 @@ LUAU_FASTFLAG(LuauNormalizationReorderFreeTypeIntersect)
LUAU_FASTFLAG(LuauRefineTablesWithReadType)
LUAU_FASTFLAG(LuauRefineNoRefineAlways)
LUAU_FASTFLAG(LuauDoNotPrototypeTableIndex)
LUAU_FASTFLAG(LuauForceSimplifyConstraint)
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauSimplifyOutOfLine2)
using namespace Luau;
@ -2251,7 +2253,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "luau_polyfill_isindexkey_refine_conjunction"
{FFlag::LuauSolverV2, true},
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauForceSimplifyConstraint, true},
{FFlag::LuauForceSimplifyConstraint2, true},
};
CheckResult result = check(R"(
@ -2900,4 +2902,31 @@ TEST_CASE_FIXTURE(Fixture, "cli_120460_table_access_on_phi_node")
)"));
}
TEST_CASE_FIXTURE(Fixture, "force_simplify_constraint_doesnt_drop_blocked_type")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck2, true},
{FFlag::LuauForceSimplifyConstraint2, true},
{FFlag::LuauSimplifyOutOfLine2, true},
};
CheckResult results = check(R"(
local function track(instance): boolean
local isBasePart = instance:IsA("BasePart")
local isCharacter = false
if not isBasePart then
isCharacter = instance:FindFirstChildOfClass("Humanoid") and instance:FindFirstChild("HumanoidRootPart")
end
-- A verison of `SimplifyConstraint` mucked up the fact that this
-- is `boolean | and<unknown, unknown>`, and claimed it was only
-- `boolean`.
return isCharacter
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, results);
REQUIRE(get<TypeMismatch>(results.errors[0]));
}
TEST_SUITE_END();

View file

@ -35,10 +35,12 @@ LUAU_FASTFLAG(LuauInferPolarityOfReadWriteProperties)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauInferActualIfElseExprType)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauForceSimplifyConstraint)
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
LUAU_FASTFLAG(LuauPushFunctionTypesInFunctionStatement)
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauNewNonStrictSuppressSoloConstraintSolvingIncomplete)
LUAU_FASTFLAG(LuauReturnMappedGenericPacksFromSubtyping2)
LUAU_FASTFLAG(LuauMissingFollowMappedGenericPacks)
using namespace Luau;
@ -1902,7 +1904,6 @@ end
TEST_CASE_FIXTURE(Fixture, "fuzzer_derived_unsound_loops")
{
ScopedFastFlag _{FFlag::LuauDfgAllowUpdatesInLoops, true};
LUAU_REQUIRE_NO_ERRORS(check(R"(
for _ in ... do
repeat
@ -2529,7 +2530,7 @@ TEST_CASE_FIXTURE(Fixture, "simplify_constraint_can_force")
{
ScopedFastFlag sff[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauForceSimplifyConstraint, true},
{FFlag::LuauForceSimplifyConstraint2, true},
{FFlag::LuauSimplifyOutOfLine2, true},
// NOTE: Feel free to clip this test when this flag is clipped.
{FFlag::LuauPushFunctionTypesInFunctionStatement, false},
@ -2589,4 +2590,48 @@ TEST_CASE_FIXTURE(Fixture, "non_standalone_constraint_solving_incomplete_is_hidd
CHECK(get<TypeMismatch>(results.errors[1]));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_missing_type_pack_follow")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauReturnMappedGenericPacksFromSubtyping2, true},
{FFlag::LuauMissingFollowMappedGenericPacks, true},
};
LUAU_REQUIRE_ERRORS(check(R"(
local _ = {[0]=_,}
while _ do
do
local l2 = require(module0)
end
end
do end
function _(l0:typeof(_),l0,l0)
local l0 = require(module0)
_()(l0(),_,_(_())((_)))
do end
end
_()(_(if nil then _))("",_,_(_,(_)))
do end
)"));
LUAU_REQUIRE_ERRORS(check(R"(
local _ = {_,}
while _ do
do
do end
end
end
_ = nil
function _(l0,l0,l0)
local l0 = require(module0)
_()(_(),_,_(_())(_,true)(_,_),l0)
do end
end
_()(_())("",_.n0,_,_(_,true,(_)))
do end
)"));
}
TEST_SUITE_END();

View file

@ -6,7 +6,6 @@
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauEagerGeneralization4)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
using namespace Luau;
@ -710,7 +709,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring")
TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring_in_loop")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauDfgAllowUpdatesInLoops, true}};
ScopedFastFlag _{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
--!strict

View file

@ -9,7 +9,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauEagerGeneralization4);
LUAU_FASTFLAG(LuauStuckTypeFunctionsStillDispatch);
LUAU_FASTFLAG(LuauForceSimplifyConstraint)
LUAU_FASTFLAG(LuauForceSimplifyConstraint2)
TEST_SUITE_BEGIN("TypeInferUnknownNever");
@ -333,7 +333,7 @@ TEST_CASE_FIXTURE(Fixture, "dont_unify_operands_if_one_of_the_operand_is_never_i
ScopedFastFlag sffs[] = {
{FFlag::LuauEagerGeneralization4, true},
{FFlag::LuauStuckTypeFunctionsStillDispatch, true},
{FFlag::LuauForceSimplifyConstraint, true},
{FFlag::LuauForceSimplifyConstraint2, true},
};
CheckResult result = check(R"(