Sync to upstream/release/675 (#1845)

## General 
- Introduce `Frontend::parseModules` for parsing a group of modules at
once.
- Support chained function types in the CST.

## New Type Solver
- Enable write-only table properties (described in [this
RFC](https://rfcs.luau.org/property-writeonly.html)).
- Disable singleton inference for large tables to improve performance.
- Fix a bug that occurs when we try to expand a type alias to itself.
- Catch cancelation during the type-checking phase in addition to during
constraint solving.
- Fix stringification of the empty type pack: `()`.
- Improve errors for calls being rejected on the primitive `function`
type.
- Rework generalization: We now generalize types as soon as the last
constraint relating to them is finished. We think this will reduce the
number of cases where type inference fails to complete and reduce the
number of instances where `*blocked*` types appear in the inference
result.

## VM/Runtime
- Dynamically disable native execution for functions that incur a
slowdown (relative to bytecode execution).
- Improve names for `thread`/`closure`/`proto` in the Luau heap dump.

---

Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Talha Pathan <tpathan@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Menarul Alam <malam@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
This commit is contained in:
Varun Saini 2025-05-27 14:24:46 -07:00 committed by GitHub
parent 92cce5776c
commit 5965818283
Signed by: DevComp
GPG key ID: B5690EEEBB952194
70 changed files with 3153 additions and 925 deletions

View file

@ -175,6 +175,10 @@ private:
std::vector<TypeId> unionsToSimplify; std::vector<TypeId> unionsToSimplify;
// Used to keep track of when we are inside a large table and should
// opt *not* to do type inference for singletons.
size_t largeTableDepth = 0;
/** /**
* Fabricates a new free type belonging to a given scope. * Fabricates a new free type belonging to a given scope.
* @param scope the scope the free type belongs to. * @param scope the scope the free type belongs to.

View file

@ -225,7 +225,7 @@ public:
bool tryDispatch(const IterableConstraint& c, NotNull<const Constraint> constraint, bool force); bool tryDispatch(const IterableConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint); bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint); bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint); bool tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const TableCheckConstraint& c, NotNull<const Constraint> constraint); bool tryDispatch(const TableCheckConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint); bool tryDispatch(const FunctionCheckConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint); bool tryDispatch(const PrimitiveTypeConstraint& c, NotNull<const Constraint> constraint);

View file

@ -166,6 +166,7 @@ struct Frontend
// Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking // Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking
void parse(const ModuleName& name); void parse(const ModuleName& name);
void parseModules(const std::vector<ModuleName>& name);
// Parse and typecheck module graph // Parse and typecheck module graph
CheckResult check(const ModuleName& name, std::optional<FrontendOptions> optionOverride = {}); // new shininess CheckResult check(const ModuleName& name, std::optional<FrontendOptions> optionOverride = {}); // new shininess

View file

@ -63,7 +63,7 @@ struct OverloadResolver
InsertionOrderedMap<TypeId, std::pair<OverloadResolver::Analysis, size_t>> resolution; InsertionOrderedMap<TypeId, std::pair<OverloadResolver::Analysis, size_t>> resolution;
std::pair<OverloadResolver::Analysis, TypeId> selectOverload(TypeId ty, TypePackId args); std::pair<OverloadResolver::Analysis, TypeId> selectOverload(TypeId ty, TypePackId args, bool useFreeTypeBounds);
void resolve(TypeId fnTy, const TypePack* args, AstExpr* selfExpr, const std::vector<AstExpr*>* argExprs); void resolve(TypeId fnTy, const TypePack* args, AstExpr* selfExpr, const std::vector<AstExpr*>* argExprs);
private: private:

View file

@ -72,6 +72,11 @@ struct SubtypingResult
/// isSubtype is false, depending on the input types. /// isSubtype is false, depending on the input types.
SubtypingReasonings reasoning{kEmptyReasoning}; SubtypingReasonings reasoning{kEmptyReasoning};
// If this subtype result required testing free types, we might be making
// assumptions about what the free type eventually resolves to. If so,
// those assumptions are recorded here.
std::vector<SubtypeConstraint> assumedConstraints;
SubtypingResult& andAlso(const SubtypingResult& other); SubtypingResult& andAlso(const SubtypingResult& other);
SubtypingResult& orElse(const SubtypingResult& other); SubtypingResult& orElse(const SubtypingResult& other);
SubtypingResult& withBothComponent(TypePath::Component component); SubtypingResult& withBothComponent(TypePath::Component component);

View file

@ -24,7 +24,6 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINT(LuauTypeInferIterationLimit) LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames) LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteUsesModuleForTypeCompatibility)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows) LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
@ -163,8 +162,6 @@ static bool checkTypeMatch(
UnifierSharedState unifierState(&iceReporter); UnifierSharedState unifierState(&iceReporter);
SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes); SimplifierPtr simplifier = newSimplifier(NotNull{typeArena}, builtinTypes);
Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}}; Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}};
if (FFlag::LuauAutocompleteUsesModuleForTypeCompatibility)
{
if (module.checkedInNewSolver) if (module.checkedInNewSolver)
{ {
TypeCheckLimits limits; TypeCheckLimits limits;
@ -199,43 +196,6 @@ static bool checkTypeMatch(
return unifier.canUnify(subTy, superTy).empty(); return unifier.canUnify(subTy, superTy).empty();
} }
}
else
{
if (FFlag::LuauSolverV2)
{
TypeCheckLimits limits;
TypeFunctionRuntime typeFunctionRuntime{
NotNull{&iceReporter}, NotNull{&limits}
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
Subtyping subtyping{
builtinTypes,
NotNull{typeArena},
NotNull{simplifier.get()},
NotNull{&normalizer},
NotNull{&typeFunctionRuntime},
NotNull{&iceReporter}
};
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
}
else
{
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
// Cost of normalization can be too high for autocomplete response time requirements
unifier.normalize = false;
unifier.checkInhabited = false;
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
return unifier.canUnify(subTy, superTy).empty();
}
}
} }
static TypeCorrectKind checkTypeCorrectKind( static TypeCorrectKind checkTypeCorrectKind(

View file

@ -30,7 +30,7 @@
*/ */
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3) LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2) LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition) LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
@ -310,8 +310,8 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
TypeArena& arena = globals.globalTypes; TypeArena& arena = globals.globalTypes;
NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes; NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauNonReentrantGeneralization3 Scope* globalScope = nullptr; // NotNull<Scope> when removing FFlag::LuauEagerGeneralization
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
globalScope = globals.globalScope.get(); globalScope = globals.globalScope.get();
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2)

View file

@ -12,8 +12,6 @@ LUAU_FASTFLAG(LuauSolverV2)
// For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit. // For each `Luau::clone` call, we will clone only up to N amount of types _and_ packs, as controlled by this limit.
LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000) LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
LUAU_FASTFLAGVARIABLE(LuauClonedTableAndFunctionTypesMustHaveScopes)
LUAU_FASTFLAGVARIABLE(LuauDoNotClonePersistentBindings)
namespace Luau namespace Luau
{ {
@ -514,10 +512,7 @@ public:
free->scope = replacementForNullScope; free->scope = replacementForNullScope;
} }
else if (auto tt = getMutable<TableType>(target)) else if (auto tt = getMutable<TableType>(target))
{
if (FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes)
tt->scope = replacementForNullScope; tt->scope = replacementForNullScope;
}
(*types)[ty] = target; (*types)[ty] = target;
queue.emplace_back(target); queue.emplace_back(target);
@ -733,7 +728,7 @@ Binding cloneIncremental(const Binding& binding, TypeArena& dest, CloneState& cl
b.deprecatedSuggestion = binding.deprecatedSuggestion; b.deprecatedSuggestion = binding.deprecatedSuggestion;
b.documentationSymbol = binding.documentationSymbol; b.documentationSymbol = binding.documentationSymbol;
b.location = binding.location; b.location = binding.location;
b.typeId = FFlag::LuauDoNotClonePersistentBindings && binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId); b.typeId = binding.typeId->persistent ? binding.typeId : cloner.clone(binding.typeId);
return b; return b;
} }

View file

@ -3,7 +3,7 @@
#include "Luau/Constraint.h" #include "Luau/Constraint.h"
#include "Luau/VisitType.h" #include "Luau/VisitType.h"
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
namespace Luau namespace Luau
{ {
@ -17,8 +17,8 @@ Constraint::Constraint(NotNull<Scope> scope, const Location& location, Constrain
struct ReferenceCountInitializer : TypeOnceVisitor struct ReferenceCountInitializer : TypeOnceVisitor
{ {
DenseHashSet<TypeId>* result; DenseHashSet<TypeId>* result;
bool traverseIntoTypeFunctions = true;
explicit ReferenceCountInitializer(DenseHashSet<TypeId>* result) explicit ReferenceCountInitializer(DenseHashSet<TypeId>* result)
: result(result) : result(result)
@ -51,7 +51,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
bool visit(TypeId, const TypeFunctionInstanceType&) override bool visit(TypeId, const TypeFunctionInstanceType&) override
{ {
return FFlag::DebugLuauGreedyGeneralization; return FFlag::LuauEagerGeneralization && traverseIntoTypeFunctions;
} }
}; };
@ -104,10 +104,12 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
{ {
rci.traverse(fchc->argsPack); rci.traverse(fchc->argsPack);
} }
else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::DebugLuauGreedyGeneralization) else if (auto fcc = get<FunctionCallConstraint>(*this); fcc && FFlag::LuauEagerGeneralization)
{ {
rci.traverseIntoTypeFunctions = false;
rci.traverse(fcc->fn); rci.traverse(fcc->fn);
rci.traverse(fcc->argsPack); rci.traverse(fcc->argsPack);
rci.traverseIntoTypeFunctions = true;
} }
else if (auto ptc = get<PrimitiveTypeConstraint>(*this)) else if (auto ptc = get<PrimitiveTypeConstraint>(*this))
{ {
@ -116,12 +118,12 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
else if (auto hpc = get<HasPropConstraint>(*this)) else if (auto hpc = get<HasPropConstraint>(*this))
{ {
rci.traverse(hpc->resultType); rci.traverse(hpc->resultType);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
rci.traverse(hpc->subjectType); rci.traverse(hpc->subjectType);
} }
else if (auto hic = get<HasIndexerConstraint>(*this)) else if (auto hic = get<HasIndexerConstraint>(*this))
{ {
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
rci.traverse(hic->subjectType); rci.traverse(hic->subjectType);
rci.traverse(hic->resultType); rci.traverse(hic->resultType);
// `HasIndexerConstraint` should not mutate `indexType`. // `HasIndexerConstraint` should not mutate `indexType`.

View file

@ -33,8 +33,8 @@
LUAU_FASTINT(LuauCheckRecursionLimit) LUAU_FASTINT(LuauCheckRecursionLimit)
LUAU_FASTFLAG(DebugLuauLogSolverToJson) LUAU_FASTFLAG(DebugLuauLogSolverToJson)
LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations) LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
@ -42,12 +42,15 @@ LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation) LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions) LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation) LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation)
LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine) LUAU_FASTFLAGVARIABLE(LuauSimplifyOutOfLine)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables)
LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500)
namespace Luau namespace Luau
{ {
@ -251,7 +254,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
rootScope->location = block->location; rootScope->location = block->location;
module->astScopes[block] = NotNull{scope.get()}; module->astScopes[block] = NotNull{scope.get()};
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.emplace_back(); DEPRECATED_interiorTypes.emplace_back();
@ -287,7 +290,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
} }
); );
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); scope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); scope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
@ -306,7 +309,7 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
} }
); );
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.pop_back(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); DEPRECATED_interiorTypes.pop_back();
@ -344,13 +347,13 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
// We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes // We prepopulate global data in the resumeScope to avoid writing data into the old modules scopes
prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block); prepopulateGlobalScopeForFragmentTypecheck(globalScope, resumeScope, block);
// Pre // Pre
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.emplace_back(); DEPRECATED_interiorTypes.emplace_back();
visitBlockWithoutChildScope(resumeScope, block); visitBlockWithoutChildScope(resumeScope, block);
// Post // Post
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.pop_back(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); DEPRECATED_interiorTypes.pop_back();
@ -380,12 +383,12 @@ void ConstraintGenerator::visitFragmentRoot(const ScopePtr& resumeScope, AstStat
TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity) TypeId ConstraintGenerator::freshType(const ScopePtr& scope, Polarity polarity)
{ {
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity); auto ft = Luau::freshType(arena, builtinTypes, scope.get(), polarity);
interiorFreeTypes.back().types.push_back(ft); interiorFreeTypes.back().types.push_back(ft);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
freeTypes.insert(ft); freeTypes.insert(ft);
return ft; return ft;
@ -402,7 +405,7 @@ TypePackId ConstraintGenerator::freshTypePack(const ScopePtr& scope, Polarity po
{ {
FreeTypePack f{scope.get(), polarity}; FreeTypePack f{scope.get(), polarity};
TypePackId result = arena->addTypePack(TypePackVar{std::move(f)}); TypePackId result = arena->addTypePack(TypePackVar{std::move(f)});
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.back().typePacks.push_back(result); interiorFreeTypes.back().typePacks.push_back(result);
return result; return result;
} }
@ -1393,7 +1396,7 @@ 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->name->location}; sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->name->location};
bool sigFullyDefined = FFlag::DebugLuauGreedyGeneralization ? false : !hasFreeType(sig.signature); bool sigFullyDefined = FFlag::LuauEagerGeneralization ? false : !hasFreeType(sig.signature);
if (sigFullyDefined) if (sigFullyDefined)
emplaceType<BoundType>(asMutable(functionType), sig.signature); emplaceType<BoundType>(asMutable(functionType), sig.signature);
@ -1453,7 +1456,7 @@ 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 = FFlag::DebugLuauGreedyGeneralization ? false : !hasFreeType(sig.signature); bool sigFullyDefined = FFlag::LuauEagerGeneralization ? false : !hasFreeType(sig.signature);
DefId def = dfg->getDef(function->name); DefId def = dfg->getDef(function->name);
@ -1767,7 +1770,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
// Place this function as a child of the non-type function scope // Place this function as a child of the non-type function scope
scope->children.push_back(NotNull{sig.signatureScope.get()}); scope->children.push_back(NotNull{sig.signatureScope.get()});
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{}); DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
@ -1785,7 +1788,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
} }
); );
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
@ -1794,7 +1797,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatTypeFunctio
sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back()); sig.signatureScope->interiorFreeTypes = std::move(DEPRECATED_interiorTypes.back());
getMutable<BlockedType>(generalizedTy)->setOwner(gc); getMutable<BlockedType>(generalizedTy)->setOwner(gc);
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.pop_back(); interiorFreeTypes.pop_back();
else else
DEPRECATED_interiorTypes.pop_back(); DEPRECATED_interiorTypes.pop_back();
@ -2358,14 +2361,10 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
argEndCheckpoint, argEndCheckpoint,
this, this,
[checkConstraint, callConstraint](const ConstraintPtr& constraint) [checkConstraint, callConstraint](const ConstraintPtr& constraint)
{
if (!(FFlag::DebugLuauGreedyGeneralization && get<PrimitiveTypeConstraint>(*constraint)))
{ {
constraint->dependencies.emplace_back(checkConstraint); constraint->dependencies.emplace_back(checkConstraint);
callConstraint->dependencies.emplace_back(constraint.get()); callConstraint->dependencies.emplace_back(constraint.get());
} }
}
); );
return InferencePack{rets, {refinementArena.variadic(returnRefinements)}}; return InferencePack{rets, {refinementArena.variadic(returnRefinements)}};
@ -2457,8 +2456,17 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
if (forceSingleton) if (forceSingleton)
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})}; return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
// Consider a table like:
//
// local DICTIONARY = { "aback", "abacus", "abandon", --[[ so on and so forth ]] }
//
// The intent is (probably) not for this to be an array-like table with a massive
// union for the value, but instead a `{ string }`.
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && largeTableDepth > 0)
return Inference{builtinTypes->stringType};
TypeId freeTy = nullptr; TypeId freeTy = nullptr;
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
freeTy = freshType(scope, Polarity::Positive); freeTy = freshType(scope, Polarity::Positive);
FreeType* ft = getMutable<FreeType>(freeTy); FreeType* ft = getMutable<FreeType>(freeTy);
@ -2484,8 +2492,22 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
if (forceSingleton) if (forceSingleton)
return Inference{singletonType}; return Inference{singletonType};
// Consider a table like:
//
// local FLAGS = {
// Foo = true,
// Bar = false,
// Baz = true,
// -- so on and so forth
// }
//
// The intent is (probably) not for this to be a table where each element
// is potentially `true` or `false` as a singleton, but just `boolean`.
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && largeTableDepth > 0)
return Inference{builtinTypes->booleanType};
TypeId freeTy = nullptr; TypeId freeTy = nullptr;
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
freeTy = freshType(scope, Polarity::Positive); freeTy = freshType(scope, Polarity::Positive);
FreeType* ft = getMutable<FreeType>(freeTy); FreeType* ft = getMutable<FreeType>(freeTy);
@ -2646,7 +2668,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
Checkpoint startCheckpoint = checkpoint(this); Checkpoint startCheckpoint = checkpoint(this);
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType); FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.emplace_back(); interiorFreeTypes.emplace_back();
else else
DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{}); DEPRECATED_interiorTypes.push_back(std::vector<TypeId>{});
@ -2664,7 +2686,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprFunction* fun
} }
); );
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types); sig.signatureScope->interiorFreeTypes = std::move(interiorFreeTypes.back().types);
sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks); sig.signatureScope->interiorFreeTypePacks = std::move(interiorFreeTypes.back().typePacks);
@ -3168,7 +3190,10 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
ttv->definitionLocation = expr->location; ttv->definitionLocation = expr->location;
ttv->scope = scope.get(); ttv->scope = scope.get();
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
largeTableDepth++;
if (FFlag::LuauEagerGeneralization)
interiorFreeTypes.back().types.push_back(ty); interiorFreeTypes.back().types.push_back(ty);
else else
DEPRECATED_interiorTypes.back().push_back(ty); DEPRECATED_interiorTypes.back().push_back(ty);
@ -3276,6 +3301,9 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprTable* expr,
); );
} }
if (FFlag::LuauDisablePrimitiveInferenceInLargeTables && FInt::LuauPrimitiveInferenceInTableLimit > 0 && expr->items.size > size_t(FInt::LuauPrimitiveInferenceInTableLimit))
largeTableDepth--;
return Inference{ty}; return Inference{ty};
} }
@ -3425,7 +3453,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
LUAU_ASSERT(nullptr != varargPack); LUAU_ASSERT(nullptr != varargPack);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
// Some of the types in argTypes will eventually be generics, and some // Some of the types in argTypes will eventually be generics, and some
// will not. The ones that are not generic will be pruned when // will not. The ones that are not generic will be pruned when
@ -3490,7 +3518,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
if (expectedType && get<FreeType>(*expectedType)) if (expectedType && get<FreeType>(*expectedType))
bindFreeType(*expectedType, actualFunctionType); bindFreeType(*expectedType, actualFunctionType);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
scopeToFunction[signatureScope.get()] = actualFunctionType; scopeToFunction[signatureScope.get()] = actualFunctionType;
return { return {
@ -3625,8 +3653,11 @@ TypeId ConstraintGenerator::resolveTableType(const ScopePtr& scope, AstType* ty,
p.readTy = propTy; p.readTy = propTy;
break; break;
case AstTableAccess::Write: case AstTableAccess::Write:
if (!FFlag::LuauEnableWriteOnlyProperties)
{
reportError(*prop.accessLocation, GenericError{"write keyword is illegal here"}); reportError(*prop.accessLocation, GenericError{"write keyword is illegal here"});
p.readTy = propTy; p.readTy = propTy;
}
p.writeTy = propTy; p.writeTy = propTy;
break; break;
default: default:

View file

@ -33,12 +33,12 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogBindings)
LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500) LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock) LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauDeprecatedAttribute) LUAU_FASTFLAG(LuauDeprecatedAttribute)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall) LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion) LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion2)
LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult) LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult)
LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2)
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations) LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
@ -418,7 +418,7 @@ void ConstraintSolver::run()
} }
// Free types that have no constraints at all can be generalized right away. // Free types that have no constraints at all can be generalized right away.
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
for (TypeId ty : constraintSet.freeTypes) for (TypeId ty : constraintSet.freeTypes)
{ {
@ -479,7 +479,7 @@ void ConstraintSolver::run()
// expansion types, etc, so we need to follow it. // expansion types, etc, so we need to follow it.
ty = follow(ty); ty = follow(ty);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
if (seen.contains(ty)) if (seen.contains(ty))
continue; continue;
@ -498,7 +498,7 @@ void ConstraintSolver::run()
if (refCount <= 1) if (refCount <= 1)
unblock(ty, Location{}); unblock(ty, Location{});
if (FFlag::DebugLuauGreedyGeneralization && refCount == 0) if (FFlag::LuauEagerGeneralization && refCount == 0)
generalizeOneType(ty); generalizeOneType(ty);
} }
} }
@ -676,7 +676,7 @@ void ConstraintSolver::initFreeTypeTracking()
auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0); auto [refCount, _] = unresolvedConstraints.try_insert(ty, 0);
refCount += 1; refCount += 1;
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr); auto [it, fresh] = mutatedFreeTypeToConstraint.try_emplace(ty, nullptr);
it->second.insert(c.get()); it->second.insert(c.get());
@ -691,7 +691,7 @@ void ConstraintSolver::initFreeTypeTracking()
} }
// Also check flag integrity while we're here // Also check flag integrity while we're here
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
LUAU_ASSERT(FFlag::LuauSubtypeGenericsAndNegations); LUAU_ASSERT(FFlag::LuauSubtypeGenericsAndNegations);
LUAU_ASSERT(FFlag::LuauNoMoreInjectiveTypeFunctions); LUAU_ASSERT(FFlag::LuauNoMoreInjectiveTypeFunctions);
@ -739,7 +739,7 @@ void ConstraintSolver::bind(NotNull<const Constraint> constraint, TypeId ty, Typ
constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed constraint, ty, constraint->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed
); // FIXME? Is this the right polarity? ); // FIXME? Is this the right polarity?
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
trackInteriorFreeType(constraint->scope, ty); trackInteriorFreeType(constraint->scope, ty);
return; return;
@ -806,7 +806,7 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
else if (auto taec = get<TypeAliasExpansionConstraint>(*constraint)) else if (auto taec = get<TypeAliasExpansionConstraint>(*constraint))
success = tryDispatch(*taec, constraint); success = tryDispatch(*taec, constraint);
else if (auto fcc = get<FunctionCallConstraint>(*constraint)) else if (auto fcc = get<FunctionCallConstraint>(*constraint))
success = tryDispatch(*fcc, constraint); success = tryDispatch(*fcc, constraint, force);
else if (auto fcc = get<FunctionCheckConstraint>(*constraint)) else if (auto fcc = get<FunctionCheckConstraint>(*constraint))
success = tryDispatch(*fcc, constraint); success = tryDispatch(*fcc, constraint);
else if (auto tcc = get<TableCheckConstraint>(*constraint)) else if (auto tcc = get<TableCheckConstraint>(*constraint))
@ -900,7 +900,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
{ {
for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access) for (TypeId ty : *constraint->scope->interiorFreeTypes) // NOLINT(bugprone-unchecked-optional-access)
{ {
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
ty = follow(ty); ty = follow(ty);
if (auto freeTy = get<FreeType>(ty)) if (auto freeTy = get<FreeType>(ty))
@ -922,7 +922,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
} }
} }
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
if (constraint->scope->interiorFreeTypePacks) if (constraint->scope->interiorFreeTypePacks)
{ {
@ -942,7 +942,7 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
} }
} }
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
if (c.noGenerics) if (c.noGenerics)
{ {
@ -1158,8 +1158,17 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
{ {
auto cTarget = follow(c.target); auto cTarget = follow(c.target);
LUAU_ASSERT(get<PendingExpansionType>(cTarget)); LUAU_ASSERT(get<PendingExpansionType>(cTarget));
// We do this check here to ensure that we don't bind an alias to itself
if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2 && occursCheck(cTarget, result))
{
reportError(OccursCheckFailed{}, constraint->location);
bind(constraint, cTarget, builtinTypes->errorRecoveryType());
}
else
{
shiftReferences(cTarget, result); shiftReferences(cTarget, result);
bind(constraint, cTarget, result); bind(constraint, cTarget, result);
}
}; };
std::optional<TypeFun> tf = (petv->prefix) ? constraint->scope->lookupImportedType(petv->prefix->value, petv->name.value) std::optional<TypeFun> tf = (petv->prefix) ? constraint->scope->lookupImportedType(petv->prefix->value, petv->name.value)
@ -1238,20 +1247,8 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
// instantiate the alias again, since the instantiation should be // instantiate the alias again, since the instantiation should be
// deterministic. // deterministic.
if (TypeId* cached = instantiatedAliases.find(signature)) if (TypeId* cached = instantiatedAliases.find(signature))
{
// However, we might now be revealing a malformed mutually recursive
// alias. `instantiatedAliases` can change from underneath us in a
// way that can cause a cached type id to bind to itself if we don't
// do this check.
if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion && occursCheck(follow(c.target), *cached))
{
reportError(OccursCheckFailed{}, constraint->location);
bindResult(errorRecoveryType());
}
else
{ {
bindResult(*cached); bindResult(*cached);
}
return true; return true;
} }
@ -1385,13 +1382,13 @@ void ConstraintSolver::fillInDiscriminantTypes(NotNull<const Constraint> constra
} }
} }
bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint) bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint, bool force)
{ {
TypeId fn = follow(c.fn); TypeId fn = follow(c.fn);
TypePackId argsPack = follow(c.argsPack); TypePackId argsPack = follow(c.argsPack);
TypePackId result = follow(c.result); TypePackId result = follow(c.result);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
if (isBlocked(fn)) if (isBlocked(fn))
return block(c.fn, constraint); return block(c.fn, constraint);
@ -1521,7 +1518,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
NotNull{&limits}, NotNull{&limits},
constraint->location constraint->location
}; };
auto [status, overload] = resolver.selectOverload(fn, argsPack); auto [status, overload] = resolver.selectOverload(fn, argsPack, /*useFreeTypeBounds*/ force);
TypeId overloadToUse = fn; TypeId overloadToUse = fn;
if (status == OverloadResolver::Analysis::Ok) if (status == OverloadResolver::Analysis::Ok)
overloadToUse = overload; overloadToUse = overload;
@ -1531,7 +1528,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy); const bool occursCheckPassed = u2.unify(overloadToUse, inferredTy);
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
for (TypeId freeTy : u2.newFreshTypes) for (TypeId freeTy : u2.newFreshTypes)
trackInteriorFreeType(constraint->scope, freeTy); trackInteriorFreeType(constraint->scope, freeTy);
@ -1945,7 +1942,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
TypeId upperBound = TypeId upperBound =
arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed}); arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed});
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
TypeId sr = follow(simplifyIntersection(constraint->scope, constraint->location, ft->upperBound, upperBound)); TypeId sr = follow(simplifyIntersection(constraint->scope, constraint->location, ft->upperBound, upperBound));
@ -1976,7 +1973,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed}; FreeType freeResult{tt->scope, builtinTypes->neverType, builtinTypes->unknownType, Polarity::Mixed};
emplace<FreeType>(constraint, resultType, freeResult); emplace<FreeType>(constraint, resultType, freeResult);
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
trackInteriorFreeType(constraint->scope, resultType); trackInteriorFreeType(constraint->scope, resultType);
tt->indexer = TableIndexer{indexType, resultType}; tt->indexer = TableIndexer{indexType, resultType};
@ -2166,7 +2163,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
{ {
auto lhsFreeUpperBound = follow(lhsFree->upperBound); auto lhsFreeUpperBound = follow(lhsFree->upperBound);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue); const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
if (!blocked.empty()) if (!blocked.empty())
@ -3067,7 +3064,7 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
{ {
const TypeId upperBound = follow(ft->upperBound); const TypeId upperBound = follow(ft->upperBound);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound)) if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
{ {
@ -3535,7 +3532,7 @@ void ConstraintSolver::shiftReferences(TypeId source, TypeId target)
// Any constraint that might have mutated source may now mutate target // Any constraint that might have mutated source may now mutate target
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
auto it = mutatedFreeTypeToConstraint.find(source); auto it = mutatedFreeTypeToConstraint.find(source);
if (it != mutatedFreeTypeToConstraint.end()) if (it != mutatedFreeTypeToConstraint.end())

View file

@ -18,7 +18,6 @@ LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow) LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow)
LUAU_FASTFLAGVARIABLE(LuauDfgMatchCGScopes)
LUAU_FASTFLAGVARIABLE(LuauDfgAllowUpdatesInLoops) LUAU_FASTFLAGVARIABLE(LuauDfgAllowUpdatesInLoops)
namespace Luau namespace Luau
@ -1240,18 +1239,9 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprUnary* u)
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprBinary* b)
{ {
visitExpr(b->left); visitExpr(b->left);
if (FFlag::LuauDfgMatchCGScopes)
{
PushScope _{scopeStack, makeChildScope()};
visitExpr(b->right); visitExpr(b->right);
return {defArena->freshCell(Symbol{}, b->location), nullptr};
}
else
{
visitExpr(b->right);
return {defArena->freshCell(Symbol{}, b->location), nullptr};
}
return {defArena->freshCell(Symbol{}, b->location), nullptr};
} }
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
@ -1264,29 +1254,9 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprTypeAssertion* t)
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIfElse* i) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIfElse* i)
{ {
if (FFlag::LuauDfgMatchCGScopes)
{
// In the constraint generator, the condition, consequence, and
// alternative all have distinct scopes.
{
PushScope _{scopeStack, makeChildScope()};
visitExpr(i->condition);
}
{
PushScope _{scopeStack, makeChildScope()};
visitExpr(i->trueExpr);
}
{
PushScope _{scopeStack, makeChildScope()};
visitExpr(i->falseExpr);
}
}
else
{
visitExpr(i->condition); visitExpr(i->condition);
visitExpr(i->trueExpr); visitExpr(i->trueExpr);
visitExpr(i->falseExpr); visitExpr(i->falseExpr);
}
return {defArena->freshCell(Symbol{}, i->location), nullptr}; return {defArena->freshCell(Symbol{}, i->location), nullptr};
} }

View file

@ -18,7 +18,9 @@
#include <unordered_set> #include <unordered_set>
LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10) LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive)
static std::string wrongNumberOfArgsString( static std::string wrongNumberOfArgsString(
size_t expectedCount, size_t expectedCount,
@ -446,6 +448,12 @@ struct ErrorConverter
return err; return err;
} }
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
{
if (auto primitiveTy = get<PrimitiveType>(follow(e.ty)); primitiveTy && primitiveTy->type == PrimitiveType::Function)
return "The type " + toString(e.ty) + " is not precise enough for us to determine the appropriate result type of this call.";
}
return "Cannot call a value of type " + toString(e.ty); return "Cannot call a value of type " + toString(e.ty);
} }
std::string operator()(const Luau::ExtraInformation& e) const std::string operator()(const Luau::ExtraInformation& e) const
@ -655,7 +663,7 @@ struct ErrorConverter
} }
// binary operators // binary operators
const auto binaryOps = FFlag::DebugLuauGreedyGeneralization ? kBinaryOps : DEPRECATED_kBinaryOps; const auto binaryOps = FFlag::LuauEagerGeneralization ? kBinaryOps : DEPRECATED_kBinaryOps;
if (auto binaryString = binaryOps.find(tfit->function->name); binaryString != binaryOps.end()) if (auto binaryString = binaryOps.find(tfit->function->name); binaryString != binaryOps.end())
{ {
std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types "; std::string result = "Operator '" + std::string(binaryString->second) + "' could not be applied to operands of types ";
@ -710,7 +718,7 @@ struct ErrorConverter
"'"; "'";
} }
if ((FFlag::DebugLuauGreedyGeneralization ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name)) if ((FFlag::LuauEagerGeneralization ? kUnreachableTypeFunctions : DEPRECATED_kUnreachableTypeFunctions).count(tfit->function->name))
{ {
return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" + return "Type function instance " + Luau::toString(e.ty) + " is uninhabited\n" +
"This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues"; "This is likely to be a bug, please report it at https://github.com/luau-lang/luau/issues";

View file

@ -29,10 +29,6 @@ LUAU_FASTINT(LuauTypeInferIterationLimit);
LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete) LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete)
LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes)
LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings)
LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection) LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection) LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak) LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
@ -1261,11 +1257,8 @@ FragmentAutocompleteStatusResult tryFragmentAutocomplete(
StringCompletionCallback stringCompletionCB StringCompletionCallback stringCompletionCB
) )
{ {
if (FFlag::LuauBetterCursorInCommentDetection)
{
if (isWithinComment(context.freshParse.commentLocations, cursorPosition)) if (isWithinComment(context.freshParse.commentLocations, cursorPosition))
return {FragmentAutocompleteStatus::Success, std::nullopt}; return {FragmentAutocompleteStatus::Success, std::nullopt};
}
// TODO: we should calculate fragmentEnd position here, by using context.newAstRoot and cursorPosition // TODO: we should calculate fragmentEnd position here, by using context.newAstRoot and cursorPosition
try try
{ {

View file

@ -39,12 +39,13 @@ LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(LuauInferInNoCheckMode) LUAU_FASTFLAG(LuauInferInNoCheckMode)
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3) LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes) LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode) LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode) LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
LUAU_FASTFLAGVARIABLE(LuauNewSolverTypecheckCatchTimeouts)
namespace Luau namespace Luau
{ {
@ -408,6 +409,38 @@ void Frontend::parse(const ModuleName& name)
parseGraph(buildQueue, name, false); parseGraph(buildQueue, name, false);
} }
void Frontend::parseModules(const std::vector<ModuleName>& names)
{
LUAU_TIMETRACE_SCOPE("Frontend::parseModules", "Frontend");
DenseHashSet<Luau::ModuleName> seen{{}};
for (const ModuleName& name : names)
{
if (seen.contains(name))
continue;
if (auto it = sourceNodes.find(name); it != sourceNodes.end() && !it->second->hasDirtySourceModule())
{
seen.insert(name);
continue;
}
std::vector<ModuleName> queue;
parseGraph(
queue,
name,
false,
[&seen](const ModuleName& name)
{
return seen.contains(name);
}
);
seen.insert(name);
}
}
CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOptions> optionOverride) CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOptions> optionOverride)
{ {
LUAU_TIMETRACE_SCOPE("Frontend::check", "Frontend"); LUAU_TIMETRACE_SCOPE("Frontend::check", "Frontend");
@ -1403,13 +1436,13 @@ ModulePtr check(
requireCycles requireCycles
}; };
// FIXME: Delete this flag when clipping FFlag::DebugLuauGreedyGeneralization. // FIXME: Delete this flag when clipping FFlag::LuauEagerGeneralization.
// //
// This optional<> only exists so that we can run one constructor when the flag // This optional<> only exists so that we can run one constructor when the flag
// is set, and another when it is unset. // is set, and another when it is unset.
std::optional<ConstraintSolver> cs; std::optional<ConstraintSolver> cs;
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
ConstraintSet constraintSet = cg.run(sourceModule.root); ConstraintSet constraintSet = cg.run(sourceModule.root);
result->errors = std::move(constraintSet.errors); result->errors = std::move(constraintSet.errors);
@ -1495,6 +1528,52 @@ ModulePtr check(
for (auto& [name, tf] : result->exportedTypeBindings) for (auto& [name, tf] : result->exportedTypeBindings)
tf.type = builtinTypes->errorRecoveryType(); tf.type = builtinTypes->errorRecoveryType();
} }
else if (FFlag::LuauNewSolverTypecheckCatchTimeouts)
{
try
{
switch (mode)
{
case Mode::Nonstrict:
Luau::checkNonStrict(
builtinTypes,
NotNull{simplifier.get()},
NotNull{&typeFunctionRuntime},
iceHandler,
NotNull{&unifierState},
NotNull{&dfg},
NotNull{&limits},
sourceModule,
result.get()
);
break;
case Mode::Definition:
// fallthrough intentional
case Mode::Strict:
Luau::check(
builtinTypes,
NotNull{simplifier.get()},
NotNull{&typeFunctionRuntime},
NotNull{&unifierState},
NotNull{&limits},
logger.get(),
sourceModule,
result.get()
);
break;
case Mode::NoCheck:
break;
};
}
catch (const TimeLimitError&)
{
result->timeout = true;
}
catch (const UserCancelError&)
{
result->cancelled = true;
}
}
else else
{ {
switch (mode) switch (mode)

View file

@ -14,9 +14,9 @@
#include "Luau/Substitution.h" #include "Luau/Substitution.h"
#include "Luau/VisitType.h" #include "Luau/VisitType.h"
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAGVARIABLE(LuauNonReentrantGeneralization3) LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization)
namespace Luau namespace Luau
{ {
@ -469,7 +469,7 @@ struct FreeTypeSearcher : TypeVisitor
bool visit(TypeId ty, const FreeType& ft) override bool visit(TypeId ty, const FreeType& ft) override
{ {
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
if (!subsumes(scope, ft.scope)) if (!subsumes(scope, ft.scope))
return true; return true;
@ -520,7 +520,7 @@ struct FreeTypeSearcher : TypeVisitor
if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope)) if ((tt.state == TableState::Free || tt.state == TableState::Unsealed) && subsumes(scope, tt.scope))
{ {
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
unsealedTables.insert(ty); unsealedTables.insert(ty);
else else
{ {
@ -543,6 +543,40 @@ struct FreeTypeSearcher : TypeVisitor
} }
for (const auto& [_name, prop] : tt.props) for (const auto& [_name, prop] : tt.props)
{
if (FFlag::LuauEnableWriteOnlyProperties)
{
if (prop.isReadOnly())
{
traverse(*prop.readTy);
}
else if (prop.isWriteOnly())
{
Polarity p = polarity;
polarity = Polarity::Negative;
traverse(*prop.writeTy);
polarity = p;
}
else if (prop.isShared())
{
Polarity p = polarity;
polarity = Polarity::Mixed;
traverse(prop.type());
polarity = p;
}
else
{
LUAU_ASSERT(prop.isReadWrite() && !prop.isShared());
traverse(*prop.readTy);
Polarity p = polarity;
polarity = Polarity::Negative;
traverse(*prop.writeTy);
polarity = p;
}
}
else
{ {
if (prop.isReadOnly()) if (prop.isReadOnly())
traverse(*prop.readTy); traverse(*prop.readTy);
@ -556,10 +590,11 @@ struct FreeTypeSearcher : TypeVisitor
polarity = p; polarity = p;
} }
} }
}
if (tt.indexer) if (tt.indexer)
{ {
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
// {[K]: V} is equivalent to three functions: get, set, and iterate // {[K]: V} is equivalent to three functions: get, set, and iterate
// //
@ -617,7 +652,7 @@ struct FreeTypeSearcher : TypeVisitor
if (!subsumes(scope, ftp.scope)) if (!subsumes(scope, ftp.scope))
return true; return true;
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
GeneralizationParams<TypePackId>& params = typePacks[tp]; GeneralizationParams<TypePackId>& params = typePacks[tp];
++params.useCount; ++params.useCount;
@ -1241,7 +1276,7 @@ GeneralizationResult<TypeId> generalizeType(
if (!hasLowerBound && !hasUpperBound) if (!hasLowerBound && !hasUpperBound)
{ {
if (!isWithinFunction || (!FFlag::DebugLuauGreedyGeneralization && (params.polarity != Polarity::Mixed && params.useCount == 1))) if (!isWithinFunction || (!FFlag::LuauEagerGeneralization && (params.polarity != Polarity::Mixed && params.useCount == 1)))
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType); emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
else else
{ {
@ -1273,7 +1308,7 @@ GeneralizationResult<TypeId> generalizeType(
if (follow(lb) != freeTy) if (follow(lb) != freeTy)
emplaceType<BoundType>(asMutable(freeTy), lb); emplaceType<BoundType>(asMutable(freeTy), lb);
else if (!isWithinFunction || (!FFlag::DebugLuauGreedyGeneralization && params.useCount == 1)) else if (!isWithinFunction || (!FFlag::LuauEagerGeneralization && params.useCount == 1))
emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType); emplaceType<BoundType>(asMutable(freeTy), builtinTypes->unknownType);
else else
{ {
@ -1390,7 +1425,7 @@ std::optional<TypeId> generalize(
FreeTypeSearcher fts{scope, cachedTypes}; FreeTypeSearcher fts{scope, cachedTypes};
fts.traverse(ty); fts.traverse(ty);
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
FunctionType* functionTy = getMutable<FunctionType>(ty); FunctionType* functionTy = getMutable<FunctionType>(ty);
auto pushGeneric = [&](TypeId t) auto pushGeneric = [&](TypeId t)
@ -1520,6 +1555,40 @@ struct GenericCounter : TypeVisitor
const Polarity previous = polarity; const Polarity previous = polarity;
for (const auto& [_name, prop] : tt.props) for (const auto& [_name, prop] : tt.props)
{
if (FFlag::LuauEnableWriteOnlyProperties)
{
if (prop.isReadOnly())
{
traverse(*prop.readTy);
}
else if (prop.isWriteOnly())
{
Polarity p = polarity;
polarity = Polarity::Negative;
traverse(*prop.writeTy);
polarity = p;
}
else if (prop.isShared())
{
Polarity p = polarity;
polarity = Polarity::Mixed;
traverse(prop.type());
polarity = p;
}
else
{
LUAU_ASSERT(prop.isReadWrite() && !prop.isShared());
traverse(*prop.readTy);
Polarity p = polarity;
polarity = Polarity::Negative;
traverse(*prop.writeTy);
polarity = p;
}
}
else
{ {
if (prop.isReadOnly()) if (prop.isReadOnly())
traverse(*prop.readTy); traverse(*prop.readTy);
@ -1527,9 +1596,11 @@ struct GenericCounter : TypeVisitor
{ {
LUAU_ASSERT(prop.isShared()); LUAU_ASSERT(prop.isShared());
Polarity p = polarity;
polarity = Polarity::Mixed; polarity = Polarity::Mixed;
traverse(prop.type()); traverse(prop.type());
polarity = previous; polarity = p;
}
} }
} }
@ -1582,7 +1653,7 @@ void pruneUnnecessaryGenerics(
TypeId ty TypeId ty
) )
{ {
if (!FFlag::DebugLuauGreedyGeneralization) if (!FFlag::LuauEagerGeneralization)
return; return;
ty = follow(ty); ty = follow(ty);

View file

@ -5,7 +5,7 @@
#include "Luau/Scope.h" #include "Luau/Scope.h"
#include "Luau/VisitType.h" #include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
namespace Luau namespace Luau
{ {
@ -133,7 +133,7 @@ struct InferPolarity : TypeVisitor
template<typename TID> template<typename TID>
static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty) static void inferGenericPolarities_(NotNull<TypeArena> arena, NotNull<Scope> scope, TID ty)
{ {
if (!FFlag::LuauNonReentrantGeneralization3) if (!FFlag::LuauEagerGeneralization)
return; return;
InferPolarity infer{arena, scope}; InferPolarity infer{arena, scope};

View file

@ -40,7 +40,7 @@ OverloadResolver::OverloadResolver(
{ {
} }
std::pair<OverloadResolver::Analysis, TypeId> OverloadResolver::selectOverload(TypeId ty, TypePackId argsPack) std::pair<OverloadResolver::Analysis, TypeId> OverloadResolver::selectOverload(TypeId ty, TypePackId argsPack, bool useFreeTypeBounds)
{ {
auto tryOne = [&](TypeId f) auto tryOne = [&](TypeId f)
{ {
@ -51,6 +51,9 @@ std::pair<OverloadResolver::Analysis, TypeId> OverloadResolver::selectOverload(T
SubtypingResult r = subtyping.isSubtype(argsPack, ftv->argTypes, scope); SubtypingResult r = subtyping.isSubtype(argsPack, ftv->argTypes, scope);
subtyping.variance = variance; subtyping.variance = variance;
if (!useFreeTypeBounds && !r.assumedConstraints.empty())
return false;
if (r.isSubtype) if (r.isSubtype)
return true; return true;
} }
@ -461,7 +464,7 @@ static std::optional<TypeId> selectOverload(
{ {
auto resolver = auto resolver =
std::make_unique<OverloadResolver>(builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location); std::make_unique<OverloadResolver>(builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location);
auto [status, overload] = resolver->selectOverload(fn, argsPack); auto [status, overload] = resolver->selectOverload(fn, argsPack, /*useFreeTypeBounds*/ false);
if (status == OverloadResolver::Analysis::Ok) if (status == OverloadResolver::Analysis::Ok)
return overload; return overload;

View file

@ -21,6 +21,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100) LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations) LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations)
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
LUAU_FASTFLAG(LuauEagerGeneralization)
namespace Luau namespace Luau
{ {
@ -680,6 +681,44 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
result.isSubtype = ok; result.isSubtype = ok;
result.isCacheable = false; result.isCacheable = false;
} }
else if (auto pair = get2<FreeType, FreeType>(subTy, superTy); FFlag::LuauEagerGeneralization && pair)
{
// Any two free types are potentially subtypes of one another because
// both of them could be narrowed to never.
result = {true};
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
}
else if (auto superFree = get<FreeType>(superTy); superFree && FFlag::LuauEagerGeneralization)
{
// Given SubTy <: (LB <: SuperTy <: UB)
//
// If SubTy <: UB, then it is possible that SubTy <: SuperTy.
// If SubTy </: UB, then it is definitely the case that SubTy </: SuperTy.
//
// It's always possible for SuperTy's upper bound to later be
// constrained, so this relation may not actually hold.
result = isCovariantWith(env, subTy, superFree->upperBound, scope);
if (result.isSubtype)
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
}
else if (auto subFree = get<FreeType>(subTy); subFree && FFlag::LuauEagerGeneralization)
{
// Given (LB <: SubTy <: UB) <: SuperTy
//
// If UB <: SuperTy, then it is certainly the case that SubTy <: SuperTy.
// If SuperTy <: UB and LB <: SuperTy, then it is possible that UB will later be narrowed such that SubTy <: SuperTy.
// If LB </: SuperTy, then SubTy </: SuperTy
if (isCovariantWith(env, subFree->lowerBound, superTy, scope).isSubtype)
{
result = {true};
result.assumedConstraints.emplace_back(SubtypeConstraint{subTy, superTy});
}
else
result = {false};
}
else if (auto p = get2<NegationType, NegationType>(subTy, superTy)) else if (auto p = get2<NegationType, NegationType>(subTy, superTy))
result = isCovariantWith(env, p.first->ty, p.second->ty, scope).withBothComponent(TypePath::TypeField::Negated); result = isCovariantWith(env, p.first->ty, p.second->ty, scope).withBothComponent(TypePath::TypeField::Negated);
else if (auto subNegation = get<NegationType>(subTy)) else if (auto subNegation = get<NegationType>(subTy))
@ -1411,8 +1450,24 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
{ {
SubtypingResult result{true}; SubtypingResult result{true};
if (FFlag::LuauEagerGeneralization)
{
if (subTable->props.empty() && !subTable->indexer && subTable->state == TableState::Sealed && superTable->indexer)
{
// While it is certainly the case that {} </: {T}, the story is a little bit different for {| |} <: {T}
// The shape of an unsealed tabel is still in flux, so it is probably the case that the unsealed table
// will later gain the necessary indexer as type inference proceeds.
//
// Unsealed tables are always sealed by the time inference completes, so this should never affect the
// type checking phase.
return {false};
}
}
else
{
if (subTable->props.empty() && !subTable->indexer && superTable->indexer) if (subTable->props.empty() && !subTable->indexer && superTable->indexer)
return {false}; return {false};
}
for (const auto& [name, superProp] : superTable->props) for (const auto& [name, superProp] : superTable->props)
{ {
@ -1451,6 +1506,12 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl
{ {
if (subTable->indexer) if (subTable->indexer)
result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope)); result.andAlso(isInvariantWith(env, *subTable->indexer, *superTable->indexer, scope));
else if (FFlag::LuauEagerGeneralization && subTable->state != TableState::Sealed)
{
// As above, we assume that {| |} <: {T} because the unsealed table
// on the left will eventually gain the necessary indexer.
return {true};
}
else else
return {false}; return {false};
} }

View file

@ -42,6 +42,7 @@ LUAU_FASTFLAGVARIABLE(LuauStringPartLengthLimit)
*/ */
LUAU_FASTINTVARIABLE(DebugLuauVerboseTypeNames, 0) LUAU_FASTINTVARIABLE(DebugLuauVerboseTypeNames, 0)
LUAU_FASTFLAGVARIABLE(DebugLuauToStringNoLexicalSort) LUAU_FASTFLAGVARIABLE(DebugLuauToStringNoLexicalSort)
LUAU_FASTFLAGVARIABLE(LuauFixEmptyTypePackStringification)
namespace Luau namespace Luau
{ {
@ -479,6 +480,9 @@ struct TypeStringifier
bool wrap = !singleTp && get<TypePack>(follow(tp)); bool wrap = !singleTp && get<TypePack>(follow(tp));
if (FFlag::LuauFixEmptyTypePackStringification)
wrap &= !isEmpty(tp);
if (wrap) if (wrap)
state.emit("("); state.emit("(");
@ -687,14 +691,18 @@ struct TypeStringifier
state.emit("("); state.emit("(");
if (state.opts.functionTypeArguments) if (FFlag::LuauFixEmptyTypePackStringification && isEmpty(ftv.argTypes))
{
// if we've got an empty argument pack, we're done.
}
else if (state.opts.functionTypeArguments)
stringify(ftv.argTypes, ftv.argNames); stringify(ftv.argTypes, ftv.argNames);
else else
stringify(ftv.argTypes); stringify(ftv.argTypes);
state.emit(") -> "); state.emit(") -> ");
bool plural = true; bool plural = FFlag::LuauFixEmptyTypePackStringification ? !isEmpty(ftv.retTypes) : true;
auto retBegin = begin(ftv.retTypes); auto retBegin = begin(ftv.retTypes);
auto retEnd = end(ftv.retTypes); auto retEnd = end(ftv.retTypes);
@ -1235,6 +1243,16 @@ struct TypePackStringifier
return; return;
} }
if (FFlag::LuauFixEmptyTypePackStringification)
{
if (tp.head.empty() && (!tp.tail || isEmpty(*tp.tail)))
{
state.emit("()");
state.unsee(&tp);
return;
}
}
bool first = true; bool first = true;
for (const auto& typeId : tp.head) for (const auto& typeId : tp.head)
@ -1782,6 +1800,22 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp
state.emit("): "); state.emit("): ");
if (FFlag::LuauFixEmptyTypePackStringification)
{
size_t retSize = size(ftv.retTypes);
bool hasTail = !finite(ftv.retTypes);
bool wrap = get<TypePack>(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize > 1);
if (wrap)
state.emit("(");
tvs.stringify(ftv.retTypes);
if (wrap)
state.emit(")");
}
else
{
size_t retSize = size(ftv.retTypes); size_t retSize = size(ftv.retTypes);
bool hasTail = !finite(ftv.retTypes); bool hasTail = !finite(ftv.retTypes);
bool wrap = get<TypePack>(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize != 1); bool wrap = get<TypePack>(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize != 1);
@ -1793,6 +1827,7 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp
if (wrap) if (wrap)
state.emit(")"); state.emit(")");
}
return result.name; return result.name;
} }

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,6 @@ LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0) LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAGVARIABLE(LuauFreeTypesMustHaveBounds)
namespace Luau namespace Luau
{ {

View file

@ -3,7 +3,6 @@
#include "Luau/TypeArena.h" #include "Luau/TypeArena.h"
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena); LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena);
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
namespace Luau namespace Luau
{ {

View file

@ -13,6 +13,7 @@
#include <string> #include <string>
LUAU_FASTFLAG(LuauStoreCSTData2)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
static char* allocateString(Luau::Allocator& allocator, std::string_view contents) static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
@ -307,7 +308,8 @@ public:
std::optional<AstArgumentName>* arg = &argNames.data[i++]; std::optional<AstArgumentName>* arg = &argNames.data[i++];
if (el) if (el)
new (arg) std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), Location())); new (arg)
std::optional<AstArgumentName>(AstArgumentName(AstName(el->name.c_str()), FFlag::LuauStoreCSTData2 ? Location() : el->location));
else else
new (arg) std::optional<AstArgumentName>(); new (arg) std::optional<AstArgumentName>();
} }

View file

@ -33,6 +33,7 @@ LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats) LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck) LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors) LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases) LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases)
LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck)
@ -2983,12 +2984,24 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
Set<std::optional<std::string> > missingKeys{{}}; Set<std::optional<std::string> > missingKeys{{}};
for (const auto& [name, prop] : expectedTableType->props) for (const auto& [name, prop] : expectedTableType->props)
{
if (FFlag::LuauEnableWriteOnlyProperties)
{
if (prop.readTy)
{
if (!isOptional(*prop.readTy))
missingKeys.insert(name);
}
}
else
{ {
LUAU_ASSERT(!prop.isWriteOnly()); LUAU_ASSERT(!prop.isWriteOnly());
auto readTy = *prop.readTy; auto readTy = *prop.readTy;
if (!isOptional(readTy)) if (!isOptional(readTy))
missingKeys.insert(name); missingKeys.insert(name);
} }
}
bool isArrayLike = false; bool isArrayLike = false;
if (expectedTableType->indexer) if (expectedTableType->indexer)
@ -3022,6 +3035,19 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
// If there's not an indexer, then by width subtyping we can just do nothing :) // If there's not an indexer, then by width subtyping we can just do nothing :)
} }
else else
{
if (FFlag::LuauEnableWriteOnlyProperties)
{
// If the type has a read type, then we have an expected type for it, otherwise, we actually don't
// care what's assigned to it because the only allowed behavior is writing to that property.
if (expectedIt->second.readTy)
{
module->astExpectedTypes[item.value] = *expectedIt->second.readTy;
isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy);
}
}
else
{ {
// TODO: What do we do for write only props? // TODO: What do we do for write only props?
LUAU_ASSERT(expectedIt->second.readTy); LUAU_ASSERT(expectedIt->second.readTy);
@ -3030,6 +3056,7 @@ bool TypeChecker2::testPotentialLiteralIsSubtype(AstExpr* expr, TypeId expectedT
isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy); isSubtype &= testPotentialLiteralIsSubtype(item.value, *expectedIt->second.readTy);
} }
} }
}
else if (item.kind == AstExprTable::Item::List) else if (item.kind == AstExprTable::Item::List)
{ {
if (!isArrayLike) if (!isArrayLike)

View file

@ -47,8 +47,8 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1); LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies) LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
@ -283,7 +283,7 @@ struct TypeFunctionReducer
} }
else if (is<GenericType>(ty)) else if (is<GenericType>(ty))
{ {
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
return SkipTestResult::Generic; return SkipTestResult::Generic;
else else
return SkipTestResult::Irreducible; return SkipTestResult::Irreducible;
@ -305,7 +305,7 @@ struct TypeFunctionReducer
} }
else if (is<GenericTypePack>(ty)) else if (is<GenericTypePack>(ty))
{ {
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
return SkipTestResult::Generic; return SkipTestResult::Generic;
else else
return SkipTestResult::Irreducible; return SkipTestResult::Irreducible;
@ -1101,7 +1101,7 @@ TypeFunctionReductionResult<TypeId> unmTypeFunction(
if (isPending(operandTy, ctx->solver)) if (isPending(operandTy, ctx->solver))
return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {operandTy}, {}};
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
operandTy = follow(operandTy); operandTy = follow(operandTy);
std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy); std::shared_ptr<const NormalizedType> normTy = ctx->normalizer->normalize(operandTy);
@ -1698,7 +1698,7 @@ TypeFunctionReductionResult<TypeId> orTypeFunction(
return {rhsTy, Reduction::MaybeOk, {}, {}}; return {rhsTy, Reduction::MaybeOk, {}, {}};
// check to see if both operand types are resolved enough, and wait to reduce if not // check to see if both operand types are resolved enough, and wait to reduce if not
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy)) if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
@ -1745,7 +1745,7 @@ static TypeFunctionReductionResult<TypeId> comparisonTypeFunction(
if (lhsTy == instance || rhsTy == instance) if (lhsTy == instance || rhsTy == instance)
return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}}; return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}};
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy)) if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}}; return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
@ -2119,7 +2119,7 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
for (size_t i = 1; i < typeParams.size(); i++) for (size_t i = 1; i < typeParams.size(); i++)
discriminantTypes.push_back(follow(typeParams.at(i))); discriminantTypes.push_back(follow(typeParams.at(i)));
const bool targetIsPending = FFlag::DebugLuauGreedyGeneralization const bool targetIsPending = FFlag::LuauEagerGeneralization
? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy) ? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
: isPending(targetTy, ctx->solver); : isPending(targetTy, ctx->solver);
@ -2206,7 +2206,7 @@ TypeFunctionReductionResult<TypeId> refineTypeFunction(
if (is<TableType>(target) || isSimpleDiscriminant(discriminant)) if (is<TableType>(target) || isSimpleDiscriminant(discriminant))
{ {
SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant); SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
// Simplification considers free and generic types to be // Simplification considers free and generic types to be
// 'blocking', but that's not suitable for refine<>. // 'blocking', but that's not suitable for refine<>.
@ -3335,7 +3335,7 @@ BuiltinTypeFunctions::BuiltinTypeFunctions()
, ltFunc{"lt", ltTypeFunction} , ltFunc{"lt", ltTypeFunction}
, leFunc{"le", leTypeFunction} , leFunc{"le", leTypeFunction}
, eqFunc{"eq", eqTypeFunction} , eqFunc{"eq", eqTypeFunction}
, refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::DebugLuauGreedyGeneralization} , refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::LuauEagerGeneralization}
, singletonFunc{"singleton", singletonTypeFunction} , singletonFunc{"singleton", singletonTypeFunction}
, unionFunc{"union", unionTypeFunction} , unionFunc{"union", unionTypeFunction}
, intersectFunc{"intersect", intersectTypeFunction} , intersectFunc{"intersect", intersectTypeFunction}

View file

@ -14,7 +14,6 @@
#include <type_traits> #include <type_traits>
LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAGVARIABLE(LuauDisableNewSolverAssertsInMixedMode)
// Maximum number of steps to follow when traversing a path. May not always // Maximum number of steps to follow when traversing a path. May not always
// equate to the number of components in a path, depending on the traversal // equate to the number of components in a path, depending on the traversal
@ -157,16 +156,12 @@ Path PathBuilder::build()
PathBuilder& PathBuilder::readProp(std::string name) PathBuilder& PathBuilder::readProp(std::string name)
{ {
if (!FFlag::LuauDisableNewSolverAssertsInMixedMode)
LUAU_ASSERT(FFlag::LuauSolverV2);
components.push_back(Property{std::move(name), true}); components.push_back(Property{std::move(name), true});
return *this; return *this;
} }
PathBuilder& PathBuilder::writeProp(std::string name) PathBuilder& PathBuilder::writeProp(std::string name)
{ {
if (!FFlag::LuauDisableNewSolverAssertsInMixedMode)
LUAU_ASSERT(FFlag::LuauSolverV2);
components.push_back(Property{std::move(name), false}); components.push_back(Property{std::move(name), false});
return *this; return *this;
} }

View file

@ -12,8 +12,7 @@
#include <algorithm> #include <algorithm>
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs) LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs)
namespace Luau namespace Luau
@ -307,7 +306,7 @@ TypePack extendTypePack(
TypePack newPack; TypePack newPack;
newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity); newPack.tail = arena.freshTypePack(ftp->scope, ftp->polarity);
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
trackInteriorFreeTypePack(ftp->scope, *newPack.tail); trackInteriorFreeTypePack(ftp->scope, *newPack.tail);
if (FFlag::LuauSolverV2) if (FFlag::LuauSolverV2)
@ -572,8 +571,6 @@ std::vector<TypeId> findBlockedArgTypesIn(AstExprCall* expr, NotNull<DenseHashMa
void trackInteriorFreeType(Scope* scope, TypeId ty) void trackInteriorFreeType(Scope* scope, TypeId ty)
{ {
if (!FFlag::LuauDisableNewSolverAssertsInMixedMode)
LUAU_ASSERT(FFlag::LuauSolverV2);
for (; scope; scope = scope->parent.get()) for (; scope; scope = scope->parent.get())
{ {
if (scope->interiorFreeTypes) if (scope->interiorFreeTypes)
@ -591,7 +588,7 @@ void trackInteriorFreeType(Scope* scope, TypeId ty)
void trackInteriorFreeTypePack(Scope* scope, TypePackId tp) void trackInteriorFreeTypePack(Scope* scope, TypePackId tp)
{ {
LUAU_ASSERT(tp); LUAU_ASSERT(tp);
if (!FFlag::LuauNonReentrantGeneralization3) if (!FFlag::LuauEagerGeneralization)
return; return;
for (; scope; scope = scope->parent.get()) for (; scope; scope = scope->parent.get())

View file

@ -18,8 +18,8 @@
#include <optional> #include <optional>
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
namespace Luau namespace Luau
{ {
@ -329,12 +329,12 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
for (TypePackId genericPack : subFn->genericPacks) for (TypePackId genericPack : subFn->genericPacks)
{ {
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
genericPack = follow(genericPack); genericPack = follow(genericPack);
// TODO: Clip this follow() with DebugLuauGreedyGeneralization // TODO: Clip this follow() with LuauEagerGeneralization
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack)); const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
if (gen) if (gen)
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity); genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
@ -413,6 +413,16 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable)
{ {
const Property& superProp = superPropOpt->second; const Property& superProp = superPropOpt->second;
if (FFlag::LuauEnableWriteOnlyProperties)
{
if (subProp.readTy && superProp.readTy)
result &= unify(*subProp.readTy, *superProp.readTy);
if (subProp.writeTy && superProp.writeTy)
result &= unify(*superProp.writeTy, *subProp.writeTy);
}
else
{
if (subProp.isReadOnly() && superProp.isReadOnly()) if (subProp.isReadOnly() && superProp.isReadOnly())
result &= unify(*subProp.readTy, *superPropOpt->second.readTy); result &= unify(*subProp.readTy, *superPropOpt->second.readTy);
else if (subProp.isReadOnly()) else if (subProp.isReadOnly())
@ -426,6 +436,7 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable)
} }
} }
} }
}
auto subTypeParamsIter = subTable->instantiatedTypeParams.begin(); auto subTypeParamsIter = subTable->instantiatedTypeParams.begin();
auto superTypeParamsIter = superTable->instantiatedTypeParams.begin(); auto superTypeParamsIter = superTable->instantiatedTypeParams.begin();
@ -454,7 +465,7 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable)
{ {
result &= unify(subTable->indexer->indexType, superTable->indexer->indexType); result &= unify(subTable->indexer->indexType, superTable->indexer->indexType);
result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType); result &= unify(subTable->indexer->indexResultType, superTable->indexer->indexResultType);
if (FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauEagerGeneralization)
{ {
// FIXME: We can probably do something more efficient here. // FIXME: We can probably do something more efficient here.
result &= unify(superTable->indexer->indexType, subTable->indexer->indexType); result &= unify(superTable->indexer->indexType, subTable->indexer->indexType);

View file

@ -245,6 +245,8 @@ private:
}; };
TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin); TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
// Remove with FFlagLuauStoreCSTData2
AstTableIndexer* parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin);
AstTypeOrPack parseFunctionType(bool allowPack, const AstArray<AstAttr*>& attributes); AstTypeOrPack parseFunctionType(bool allowPack, const AstArray<AstAttr*>& attributes);
AstType* parseFunctionTypeTail( AstType* parseFunctionTypeTail(

File diff suppressed because it is too large Load diff

View file

@ -121,6 +121,8 @@ void create(lua_State* L, SharedCodeGenContext* codeGenContext);
// Enable or disable native execution according to `enabled` argument // Enable or disable native execution according to `enabled` argument
void setNativeExecutionEnabled(lua_State* L, bool enabled); void setNativeExecutionEnabled(lua_State* L, bool enabled);
void disableNativeExecutionForFunction(lua_State* L, const int level) noexcept;
// Given a name, this function must return the index of the type which matches the type array used all CompilationOptions and AssemblyOptions // Given a name, this function must return the index of the type which matches the type array used all CompilationOptions and AssemblyOptions
// If the type is unknown, 0xff has to be returned // If the type is unknown, 0xff has to be returned
using UserdataRemapperCallback = uint8_t(void* context, const char* name, size_t nameLength); using UserdataRemapperCallback = uint8_t(void* context, const char* name, size_t nameLength);

View file

@ -12,6 +12,8 @@
#include "lstate.h" #include "lstate.h"
LUAU_DYNAMIC_FASTFLAG(AddReturnExectargetCheck);
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -179,6 +181,14 @@ void emitReturn(AssemblyBuilderA64& build, ModuleHelpers& helpers)
build.ldr(x1, mem(rClosure, offsetof(Closure, l.p))); // cl->l.p aka proto build.ldr(x1, mem(rClosure, offsetof(Closure, l.p))); // cl->l.p aka proto
if (DFFlag::AddReturnExectargetCheck)
{
// Get new instruction location
CODEGEN_ASSERT(offsetof(Proto, exectarget) == offsetof(Proto, execdata) + 8);
build.ldp(x3, x4, mem(x1, offsetof(Proto, execdata)));
build.cbz(x4, helpers.exitContinueVmClearNativeFlag);
}
CODEGEN_ASSERT(offsetof(Proto, code) == offsetof(Proto, k) + 8); CODEGEN_ASSERT(offsetof(Proto, code) == offsetof(Proto, k) + 8);
build.ldp(rConstants, rCode, mem(x1, offsetof(Proto, k))); // proto->k, proto->code build.ldp(rConstants, rCode, mem(x1, offsetof(Proto, k))); // proto->k, proto->code
@ -188,9 +198,12 @@ void emitReturn(AssemblyBuilderA64& build, ModuleHelpers& helpers)
build.ldr(x2, mem(x2, offsetof(CallInfo, savedpc))); // cip->savedpc build.ldr(x2, mem(x2, offsetof(CallInfo, savedpc))); // cip->savedpc
build.sub(x2, x2, rCode); build.sub(x2, x2, rCode);
if (!DFFlag::AddReturnExectargetCheck)
{
// Get new instruction location and jump to it // Get new instruction location and jump to it
CODEGEN_ASSERT(offsetof(Proto, exectarget) == offsetof(Proto, execdata) + 8); CODEGEN_ASSERT(offsetof(Proto, exectarget) == offsetof(Proto, execdata) + 8);
build.ldp(x3, x4, mem(x1, offsetof(Proto, execdata))); build.ldp(x3, x4, mem(x1, offsetof(Proto, execdata)));
}
build.ldr(w2, mem(x3, x2)); build.ldr(w2, mem(x3, x2));
build.add(x4, x4, x2); build.add(x4, x4, x2);
build.br(x4); build.br(x4);

View file

@ -671,6 +671,21 @@ void setNativeExecutionEnabled(lua_State* L, bool enabled)
L->global->ecb.enter = enabled ? onEnter : onEnterDisabled; L->global->ecb.enter = enabled ? onEnter : onEnterDisabled;
} }
void disableNativeExecutionForFunction(lua_State* L, const int level) noexcept
{
CODEGEN_ASSERT(unsigned(level) < unsigned(L->ci - L->base_ci));
const CallInfo* ci = L->ci - level;
const TValue* o = ci->func;
CODEGEN_ASSERT(ttisfunction(o));
Proto* proto = clvalue(o)->l.p;
CODEGEN_ASSERT(proto);
CODEGEN_ASSERT(proto->codeentry != proto->code);
onDestroyFunction(L, proto);
}
static uint8_t userdataRemapperWrap(lua_State* L, const char* str, size_t len) static uint8_t userdataRemapperWrap(lua_State* L, const char* str, size_t len)
{ {
if (BaseCodeGenContext* codegenCtx = getCodeGenContext(L)) if (BaseCodeGenContext* codegenCtx = getCodeGenContext(L))

View file

@ -14,6 +14,9 @@
#include <utility> #include <utility>
LUAU_DYNAMIC_FASTFLAGVARIABLE(AddReturnExectargetCheck, false);
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -458,6 +461,7 @@ void emitReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers)
// Registers alive: r9 (cip) // Registers alive: r9 (cip)
RegisterX64 proto = rcx; RegisterX64 proto = rcx;
RegisterX64 execdata = rbx; RegisterX64 execdata = rbx;
RegisterX64 exectarget = r10;
// Change closure // Change closure
build.mov(rax, qword[cip + offsetof(CallInfo, func)]); build.mov(rax, qword[cip + offsetof(CallInfo, func)]);
@ -471,6 +475,13 @@ void emitReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers)
build.test(byte[cip + offsetof(CallInfo, flags)], LUA_CALLINFO_NATIVE); build.test(byte[cip + offsetof(CallInfo, flags)], LUA_CALLINFO_NATIVE);
build.jcc(ConditionX64::Zero, helpers.exitContinueVm); // Continue in interpreter if function has no native data build.jcc(ConditionX64::Zero, helpers.exitContinueVm); // Continue in interpreter if function has no native data
if (DFFlag::AddReturnExectargetCheck)
{
build.mov(exectarget, qword[proto + offsetof(Proto, exectarget)]);
build.test(exectarget, exectarget);
build.jcc(ConditionX64::Zero, helpers.exitContinueVmClearNativeFlag);
}
// Change constants // Change constants
build.mov(rConstants, qword[proto + offsetof(Proto, k)]); build.mov(rConstants, qword[proto + offsetof(Proto, k)]);
@ -486,7 +497,15 @@ void emitReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers)
// Get new instruction location and jump to it // Get new instruction location and jump to it
build.mov(edx, dword[execdata + rax]); build.mov(edx, dword[execdata + rax]);
if (DFFlag::AddReturnExectargetCheck)
{
build.add(rdx, exectarget);
}
else
{
build.add(rdx, qword[proto + offsetof(Proto, exectarget)]); build.add(rdx, qword[proto + offsetof(Proto, exectarget)]);
}
build.jmp(rdx); build.jmp(rdx);
} }

View file

@ -15,6 +15,7 @@
#include <stdio.h> #include <stdio.h>
LUAU_FASTFLAG(LuauCurrentLineBounds) LUAU_FASTFLAG(LuauCurrentLineBounds)
LUAU_FASTFLAGVARIABLE(LuauHeapNameDetails)
static void validateobjref(global_State* g, GCObject* f, GCObject* t) static void validateobjref(global_State* g, GCObject* f, GCObject* t)
{ {
@ -728,10 +729,20 @@ static void enumclosure(EnumContext* ctx, Closure* cl)
char buf[LUA_IDSIZE]; char buf[LUA_IDSIZE];
if (FFlag::LuauHeapNameDetails)
{
if (p->source)
snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined, getstr(p->source));
else
snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined);
}
else
{
if (p->source) if (p->source)
snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "", p->linedefined, getstr(p->source)); snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "", p->linedefined, getstr(p->source));
else else
snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "", p->linedefined); snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "", p->linedefined);
}
enumnode(ctx, obj2gco(cl), sizeLclosure(cl->nupvalues), buf); enumnode(ctx, obj2gco(cl), sizeLclosure(cl->nupvalues), buf);
} }
@ -799,10 +810,21 @@ static void enumthread(EnumContext* ctx, lua_State* th)
char buf[LUA_IDSIZE]; char buf[LUA_IDSIZE];
if (FFlag::LuauHeapNameDetails)
{
if (p->source)
snprintf(buf, sizeof(buf), "thread at %s:%d %s", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined, getstr(p->source));
else
snprintf(buf, sizeof(buf), "thread at %s:%d", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined);
}
else
{
if (p->source) if (p->source)
snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "", p->linedefined, getstr(p->source)); snprintf(buf, sizeof(buf), "%s:%d %s", p->debugname ? getstr(p->debugname) : "", p->linedefined, getstr(p->source));
else else
snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "", p->linedefined); snprintf(buf, sizeof(buf), "%s:%d", p->debugname ? getstr(p->debugname) : "", p->linedefined);
}
enumnode(ctx, obj2gco(th), size, buf); enumnode(ctx, obj2gco(th), size, buf);
} }
@ -835,7 +857,21 @@ static void enumproto(EnumContext* ctx, Proto* p)
ctx->edge(ctx->context, enumtopointer(obj2gco(p)), p->execdata, "[native]"); ctx->edge(ctx->context, enumtopointer(obj2gco(p)), p->execdata, "[native]");
} }
if (FFlag::LuauHeapNameDetails)
{
char buf[LUA_IDSIZE];
if (p->source)
snprintf(buf, sizeof(buf), "proto %s:%d %s", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined, getstr(p->source));
else
snprintf(buf, sizeof(buf), "proto %s:%d", p->debugname ? getstr(p->debugname) : "unnamed", p->linedefined);
enumnode(ctx, obj2gco(p), size, buf);
}
else
{
enumnode(ctx, obj2gco(p), size, p->source ? getstr(p->source) : NULL); enumnode(ctx, obj2gco(p), size, p->source ? getstr(p->source) : NULL);
}
if (p->sizek) if (p->sizek)
enumedges(ctx, obj2gco(p), p->k, p->sizek, "constants"); enumedges(ctx, obj2gco(p), p->k, p->sizek, "constants");

View file

@ -19,7 +19,7 @@
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2) LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
using namespace Luau; using namespace Luau;
@ -4454,9 +4454,9 @@ TEST_CASE_FIXTURE(ACExternTypeFixture, "ac_dont_overflow_on_recursive_union")
auto ac = autocomplete('1'); auto ac = autocomplete('1');
if (FFlag::LuauSolverV2 && FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization)
{ {
// This `if` statement is because `LuauNonReentrantGeneralization3` // This `if` statement is because `LuauEagerGeneralization`
// sets some flags // sets some flags
CHECK(ac.entryMap.count("BaseMethod") > 0); CHECK(ac.entryMap.count("BaseMethod") > 0);
CHECK(ac.entryMap.count("Method") > 0); CHECK(ac.entryMap.count("Method") > 0);

View file

@ -40,6 +40,7 @@ LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
LUAU_FASTFLAG(LuauYieldableContinuations) LUAU_FASTFLAG(LuauYieldableContinuations)
LUAU_FASTFLAG(LuauCurrentLineBounds) LUAU_FASTFLAG(LuauCurrentLineBounds)
LUAU_FASTFLAG(LuauLoadNoOomThrow) LUAU_FASTFLAG(LuauLoadNoOomThrow)
LUAU_FASTFLAG(LuauHeapNameDetails)
static lua_CompileOptions defaultOptions() static lua_CompileOptions defaultOptions()
{ {
@ -2280,6 +2281,8 @@ TEST_CASE("StringConversion")
TEST_CASE("GCDump") TEST_CASE("GCDump")
{ {
ScopedFastFlag luauHeapNameDetails{FFlag::LuauHeapNameDetails, true};
// internal function, declared in lgc.h - not exposed via lua.h // internal function, declared in lgc.h - not exposed via lua.h
extern void luaC_dump(lua_State * L, void* file, const char* (*categoryName)(lua_State* L, uint8_t memcat)); extern void luaC_dump(lua_State * L, void* file, const char* (*categoryName)(lua_State* L, uint8_t memcat));
extern void luaC_enumheap( extern void luaC_enumheap(
@ -2320,7 +2323,19 @@ TEST_CASE("GCDump")
lua_State* CL = lua_newthread(L); lua_State* CL = lua_newthread(L);
lua_pushstring(CL, "local x x = {} local function f() x[1] = math.abs(42) end function foo() coroutine.yield() end foo() return f"); lua_pushstring(CL, R"(
local x
x = {}
local function f()
x[1] = math.abs(42)
end
function foo()
coroutine.yield()
end
foo()
return f
)");
lua_pushstring(CL, "=GCDump");
lua_loadstring(CL); lua_loadstring(CL);
lua_resume(CL, nullptr, 0); lua_resume(CL, nullptr, 0);
@ -2365,8 +2380,19 @@ TEST_CASE("GCDump")
{ {
EnumContext& context = *(EnumContext*)ctx; EnumContext& context = *(EnumContext*)ctx;
if (name)
{
std::string_view sv{name};
if (tt == LUA_TUSERDATA) if (tt == LUA_TUSERDATA)
CHECK(strcmp(name, "u42") == 0); CHECK(sv == "u42");
else if (tt == LUA_TPROTO)
CHECK((sv == "proto unnamed:1 =GCDump" || sv == "proto foo:7 =GCDump" || sv == "proto f:4 =GCDump"));
else if (tt == LUA_TFUNCTION)
CHECK((sv == "test" || sv == "unnamed:1 =GCDump" || sv == "foo:7 =GCDump" || sv == "f:4 =GCDump"));
else if (tt == LUA_TTHREAD)
CHECK(sv == "thread at unnamed:1 =GCDump");
}
context.nodes[gco] = {gco, tt, memcat, size, name ? name : ""}; context.nodes[gco] = {gco, tt, memcat, size, name ? name : ""};
}, },

View file

@ -234,8 +234,6 @@ TEST_CASE_FIXTURE(DifferFixture, "right_cyclic_table_left_table_property_wrong")
TEST_CASE_FIXTURE(DifferFixture, "equal_table_two_cyclic_tables_are_not_different") TEST_CASE_FIXTURE(DifferFixture, "equal_table_two_cyclic_tables_are_not_different")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
local function id<a>(x: a): a local function id<a>(x: a): a
return x return x
@ -1473,8 +1471,6 @@ TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "equal_metatable")
TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "metatable_normal") TEST_CASE_FIXTURE(DifferFixtureWithBuiltins, "metatable_normal")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
local metaFoo = { local metaFoo = {
metaBar = 5 metaBar = 5

View file

@ -26,13 +26,6 @@ using namespace Luau;
LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTINT(LuauParseErrorLimit)
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking) LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
LUAU_FASTFLAG(LuauAutocompleteUsesModuleForTypeCompatibility)
LUAU_FASTFLAG(LuauBetterCursorInCommentDetection)
LUAU_FASTFLAG(LuauAllFreeTypesHaveScopes)
LUAU_FASTFLAG(LuauClonedTableAndFunctionTypesMustHaveScopes)
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
LUAU_FASTFLAG(LuauCloneTypeAliasBindings)
LUAU_FASTFLAG(LuauDoNotClonePersistentBindings)
LUAU_FASTFLAG(LuauBetterScopeSelection) LUAU_FASTFLAG(LuauBetterScopeSelection)
LUAU_FASTFLAG(LuauBlockDiffFragmentSelection) LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak) LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
@ -67,14 +60,8 @@ struct FragmentAutocompleteFixtureImpl : BaseType
{ {
static_assert(std::is_base_of_v<Fixture, BaseType>, "BaseType must be a descendant of Fixture"); static_assert(std::is_base_of_v<Fixture, BaseType>, "BaseType must be a descendant of Fixture");
ScopedFastFlag luauAllFreeTypesHaveScopes{FFlag::LuauAllFreeTypesHaveScopes, true};
ScopedFastFlag luauClonedTableAndFunctionTypesMustHaveScopes{FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes, true};
ScopedFastFlag luauDisableNewSolverAssertsInMixedMode{FFlag::LuauDisableNewSolverAssertsInMixedMode, true};
ScopedFastFlag luauCloneTypeAliasBindings{FFlag::LuauCloneTypeAliasBindings, true};
ScopedFastFlag luauDoNotClonePersistentBindings{FFlag::LuauDoNotClonePersistentBindings, true};
ScopedFastFlag luauBetterScopeSelection{FFlag::LuauBetterScopeSelection, true}; ScopedFastFlag luauBetterScopeSelection{FFlag::LuauBetterScopeSelection, true};
ScopedFastFlag luauBlockDiffFragmentSelection{FFlag::LuauBlockDiffFragmentSelection, true}; ScopedFastFlag luauBlockDiffFragmentSelection{FFlag::LuauBlockDiffFragmentSelection, true};
ScopedFastFlag luauAutocompleteUsesModuleForTypeCompatibility{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true};
ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true}; ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true};
ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true}; ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true};
ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true}; ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true};
@ -2526,7 +2513,6 @@ l
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "do_not_recommend_results_in_multiline_comment") TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "do_not_recommend_results_in_multiline_comment")
{ {
ScopedFastFlag sff = {FFlag::LuauBetterCursorInCommentDetection, true};
std::string source = R"(--[[ std::string source = R"(--[[
)"; )";
std::string dest = R"(--[[ std::string dest = R"(--[[
@ -2547,7 +2533,6 @@ a
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments_simple") TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments_simple")
{ {
ScopedFastFlag sff = {FFlag::LuauBetterCursorInCommentDetection, true};
const std::string source = R"( const std::string source = R"(
-- sel -- sel
-- retur -- retur
@ -2583,7 +2568,6 @@ bar
baz baz
]] ]]
)"; )";
ScopedFastFlag sff{FFlag::LuauBetterCursorInCommentDetection, true};
autocompleteFragmentInBothSolvers( autocompleteFragmentInBothSolvers(
source, source,
source, source,
@ -2628,7 +2612,6 @@ baz
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments") TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "no_recs_for_comments")
{ {
ScopedFastFlag sff = {FFlag::LuauBetterCursorInCommentDetection, true};
const std::string source = R"( const std::string source = R"(
-- sel -- sel
-- retur -- retur
@ -2708,7 +2691,6 @@ if x == 5
local x = 5 local x = 5
if x == 5 then -- a comment if x == 5 then -- a comment
)"; )";
ScopedFastFlag sff = {FFlag::LuauBetterCursorInCommentDetection, true};
autocompleteFragmentInBothSolvers( autocompleteFragmentInBothSolvers(
source, source,
updated, updated,
@ -2964,7 +2946,6 @@ return module
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "ice_caused_by_mixed_mode_use") TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "ice_caused_by_mixed_mode_use")
{ {
ScopedFastFlag sff{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true};
const std::string source = const std::string source =
std::string("--[[\n\tPackage link auto-generated by Rotriever\n]]\nlocal PackageIndex = script.Parent._Index\n\nlocal Package = ") + std::string("--[[\n\tPackage link auto-generated by Rotriever\n]]\nlocal PackageIndex = script.Parent._Index\n\nlocal Package = ") +
"require(PackageIndex[\"ReactOtter\"][\"ReactOtter\"])\n\nexport type Goal = Package.Goal\nexport type SpringOptions " + "require(PackageIndex[\"ReactOtter\"][\"ReactOtter\"])\n\nexport type Goal = Package.Goal\nexport type SpringOptions " +

View file

@ -15,7 +15,7 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(DebugLuauForbidInternalTypes) LUAU_FASTFLAG(DebugLuauForbidInternalTypes)
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall) LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
@ -226,7 +226,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "('a) -> 'a")
TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})") TEST_CASE_FIXTURE(GeneralizationFixture, "(t1, (t1 <: 'b)) -> () where t1 = ('a <: (t1 <: 'b) & {number} & {number})")
{ {
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true}; ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
TableType tt; TableType tt;
tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType}; tt.indexer = TableIndexer{builtinTypes.numberType, builtinTypes.numberType};
@ -260,7 +260,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: number | string)) -> string?")
TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()") TEST_CASE_FIXTURE(GeneralizationFixture, "(('a <: {'b})) -> ()")
{ {
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true}; ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
auto [aTy, aFree] = freshType(); auto [aTy, aFree] = freshType();
auto [bTy, bFree] = freshType(); auto [bTy, bFree] = freshType();

View file

@ -8,13 +8,13 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauNonReentrantGeneralization3); LUAU_FASTFLAG(LuauEagerGeneralization);
TEST_SUITE_BEGIN("InferPolarity"); TEST_SUITE_BEGIN("InferPolarity");
TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }") TEST_CASE_FIXTURE(Fixture, "T where T = { m: <a>(a) -> T }")
{ {
ScopedFastFlag sff{FFlag::LuauNonReentrantGeneralization3, true}; ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
TypeArena arena; TypeArena arena;
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack); ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);

View file

@ -10,7 +10,7 @@
LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LintRedundantNativeAttribute); LUAU_FASTFLAG(LintRedundantNativeAttribute);
LUAU_FASTFLAG(LuauDeprecatedAttribute); LUAU_FASTFLAG(LuauDeprecatedAttribute);
LUAU_FASTFLAG(LuauNonReentrantGeneralization3); LUAU_FASTFLAG(LuauEagerGeneralization);
using namespace Luau; using namespace Luau;
@ -1942,7 +1942,7 @@ print(foo:bar(2.0))
TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperations") TEST_CASE_FIXTURE(BuiltinsFixture, "TableOperations")
{ {
// FIXME: For now this flag causes a stack overflow on Windows. // FIXME: For now this flag causes a stack overflow on Windows.
ScopedFastFlag _{FFlag::LuauNonReentrantGeneralization3, false}; ScopedFastFlag _{FFlag::LuauEagerGeneralization, false};
LintResult result = lint(R"( LintResult result = lint(R"(
local t = {} local t = {}

View file

@ -280,8 +280,6 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
TEST_CASE_FIXTURE(Fixture, "clone_free_types") TEST_CASE_FIXTURE(Fixture, "clone_free_types")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
TypeArena arena; TypeArena arena;
TypeId freeTy = freshType(NotNull{&arena}, builtinTypes, nullptr); TypeId freeTy = freshType(NotNull{&arena}, builtinTypes, nullptr);
TypePackVar freeTp(FreeTypePack{TypeLevel{}}); TypePackVar freeTp(FreeTypePack{TypeLevel{}});

View file

@ -14,8 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTINT(LuauNormalizeIntersectionLimit) LUAU_FASTINT(LuauNormalizeIntersectionLimit)
LUAU_FASTINT(LuauNormalizeUnionLimit) LUAU_FASTINT(LuauNormalizeUnionLimit)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3)
LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget) LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
LUAU_FASTFLAG(LuauSimplifyOutOfLine) LUAU_FASTFLAG(LuauSimplifyOutOfLine)
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
@ -1177,57 +1176,6 @@ end
)"); )");
} }
#if 0
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_limit_function_intersection_complexity")
{
ScopedFastInt luauTypeInferRecursionLimit{FInt::LuauTypeInferRecursionLimit, 80};
ScopedFastInt luauNormalizeIntersectionLimit{FInt::LuauNormalizeIntersectionLimit, 50};
ScopedFastInt luauNormalizeUnionLimit{FInt::LuauNormalizeUnionLimit, 20};
ScopedFastFlag _[] = {
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
{FFlag::DebugLuauGreedyGeneralization, true},
{FFlag::LuauSubtypeGenericsAndNegations, true},
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
};
CheckResult result = check(R"(
function _(_).readu32(l0)
return ({[_(_(_))]=_,[_(if _ then _)]=_,n0=_,})[_],nil
end
_(_)[_(n32)] %= _(_(_))
)");
LUAU_REQUIRE_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_propagate_normalization_failures")
{
ScopedFastInt luauNormalizeIntersectionLimit{FInt::LuauNormalizeIntersectionLimit, 50};
ScopedFastInt luauNormalizeUnionLimit{FInt::LuauNormalizeUnionLimit, 20};
ScopedFastFlag _[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
{FFlag::LuauSimplifyOutOfLine, true},
{FFlag::LuauNonReentrantGeneralization3, false},
{FFlag::DebugLuauGreedyGeneralization, true},
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
{FFlag::LuauSubtypeGenericsAndNegations, true},
};
CheckResult result = check(R"(
function _(_,"").readu32(l0)
return ({[_(_(_))]=_,[_(if _ then _,_())]=_,[""]=_,})[_],nil
end
_().readu32 %= _(_(_(_),_))
)");
LUAU_REQUIRE_ERRORS(result);
}
#endif
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_flatten_type_pack_cycle") TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_flatten_type_pack_cycle")
{ {
ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, true}}; ScopedFastFlag sff[] = {{FFlag::LuauSolverV2, true}};
@ -1258,7 +1206,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_union_type_pack_cycle")
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}, {FFlag::LuauNoMoreInjectiveTypeFunctions, true},
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}, {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
{FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true}, {FFlag::LuauClipVariadicAnysFromArgsToGenericFuncs2, true},
{FFlag::DebugLuauGreedyGeneralization, true} {FFlag::LuauEagerGeneralization, true}
}; };
ScopedFastInt sfi{FInt::LuauTypeInferRecursionLimit, 0}; ScopedFastInt sfi{FInt::LuauTypeInferRecursionLimit, 0};

View file

@ -20,6 +20,7 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
LUAU_FASTFLAG(LuauParseStringIndexer) LUAU_FASTFLAG(LuauParseStringIndexer)
LUAU_FASTFLAG(LuauDeclareExternType) LUAU_FASTFLAG(LuauDeclareExternType)
LUAU_FASTFLAG(LuauStoreCSTData2)
LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix) LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix)
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
@ -1260,8 +1261,6 @@ until false
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_local_function") TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_local_function")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
try try
{ {
parse(R"(-- i am line 1 parse(R"(-- i am line 1
@ -1295,8 +1294,6 @@ end
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_failsafe_earlier") TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_failsafe_earlier")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
try try
{ {
parse(R"(-- i am line 1 parse(R"(-- i am line 1
@ -2902,6 +2899,8 @@ TEST_CASE_FIXTURE(Fixture, "function_start_locations_are_before_attributes")
TEST_CASE_FIXTURE(Fixture, "for_loop_with_single_var_has_comma_positions_of_size_zero") TEST_CASE_FIXTURE(Fixture, "for_loop_with_single_var_has_comma_positions_of_size_zero")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
ParseOptions parseOptions; ParseOptions parseOptions;
parseOptions.storeCstData = true; parseOptions.storeCstData = true;
@ -3364,8 +3363,6 @@ TEST_CASE_FIXTURE(Fixture, "AstName_comparison")
TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery") TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
try try
{ {
parse(R"( parse(R"(

View file

@ -17,6 +17,7 @@
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations) LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
LUAU_FASTFLAG(LuauEagerGeneralization)
using namespace Luau; using namespace Luau;
@ -1647,4 +1648,19 @@ TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation")
CHECK(result.isSubtype); CHECK(result.isSubtype);
} }
TEST_CASE_FIXTURE(SubtypeFixture, "free_types_might_be_subtypes")
{
ScopedFastFlag sff{FFlag::LuauEagerGeneralization, true};
TypeId argTy = arena.freshType(builtinTypes, moduleScope.get());
FreeType* freeArg = getMutable<FreeType>(argTy);
REQUIRE(freeArg);
freeArg->lowerBound = arena.addType(SingletonType{StringSingleton{"five"}});
freeArg->upperBound = builtinTypes->stringType;
SubtypingResult result = isSubtype(builtinTypes->stringType, argTy);
CHECK(result.isSubtype);
REQUIRE(1 == result.assumedConstraints.size());
}
TEST_SUITE_END(); TEST_SUITE_END();

View file

@ -322,8 +322,6 @@ n3 [label="TableType 3"];
TEST_CASE_FIXTURE(Fixture, "free") TEST_CASE_FIXTURE(Fixture, "free")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
Type type{TypeVariant{FreeType{TypeLevel{0, 0}, builtinTypes->neverType, builtinTypes->unknownType}}}; Type type{TypeVariant{FreeType{TypeLevel{0, 0}, builtinTypes->neverType, builtinTypes->unknownType}}};
ToDotOptions opts; ToDotOptions opts;
opts.showPointers = false; opts.showPointers = false;

View file

@ -6,6 +6,7 @@
#include "Fixture.h" #include "Fixture.h"
#include "Luau/TypeChecker2.h" #include "Luau/TypeChecker2.h"
#include "Luau/TypePack.h"
#include "ScopedFlags.h" #include "ScopedFlags.h"
#include "doctest.h" #include "doctest.h"
@ -14,6 +15,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction) LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAttributeSyntax) LUAU_FASTFLAG(LuauAttributeSyntax)
LUAU_FASTFLAG(LuauFixEmptyTypePackStringification)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
TEST_SUITE_BEGIN("ToString"); TEST_SUITE_BEGIN("ToString");
@ -492,6 +494,17 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_array_uses_array_syntax")
CHECK_EQ("{string}", toString(Type{ttv})); CHECK_EQ("{string}", toString(Type{ttv}));
} }
TEST_CASE_FIXTURE(Fixture, "the_empty_type_pack_should_be_parenthesized")
{
ScopedFastFlag sff{FFlag::LuauFixEmptyTypePackStringification, true};
TypePackVar emptyTypePack{TypePack{}};
CHECK_EQ(toString(&emptyTypePack), "()");
auto unitToUnit = Type{FunctionType{&emptyTypePack, &emptyTypePack}};
CHECK_EQ(toString(&unitToUnit), "() -> ()");
}
TEST_CASE_FIXTURE(Fixture, "generic_packs_are_stringified_differently_from_generic_types") TEST_CASE_FIXTURE(Fixture, "generic_packs_are_stringified_differently_from_generic_types")
{ {

View file

@ -12,8 +12,10 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauStoreCSTData2)
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions) LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions)
LUAU_FASTFLAG(LuauCSTForReturnTypeFunctionTail)
TEST_SUITE_BEGIN("TranspilerTests"); TEST_SUITE_BEGIN("TranspilerTests");
@ -47,6 +49,7 @@ TEST_CASE("string_literals_containing_utf8")
TEST_CASE("if_stmt_spaces_around_tokens") TEST_CASE("if_stmt_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string one = R"( if This then Once() end)"; const std::string one = R"( if This then Once() end)";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
@ -95,15 +98,31 @@ TEST_CASE("elseif_chains_indent_sensibly")
TEST_CASE("strips_type_annotations") TEST_CASE("strips_type_annotations")
{ {
const std::string code = R"( local s: string= 'hello there' )"; const std::string code = R"( local s: string= 'hello there' )";
if (FFlag::LuauStoreCSTData2)
{
const std::string expected = R"( local s = 'hello there' )"; const std::string expected = R"( local s = 'hello there' )";
CHECK_EQ(expected, transpile(code).code); CHECK_EQ(expected, transpile(code).code);
}
else
{
const std::string expected = R"( local s = 'hello there' )";
CHECK_EQ(expected, transpile(code).code);
}
} }
TEST_CASE("strips_type_assertion_expressions") TEST_CASE("strips_type_assertion_expressions")
{ {
const std::string code = R"( local s= some_function() :: any+ something_else() :: number )"; const std::string code = R"( local s= some_function() :: any+ something_else() :: number )";
if (FFlag::LuauStoreCSTData2)
{
const std::string expected = R"( local s= some_function() + something_else() )"; const std::string expected = R"( local s= some_function() + something_else() )";
CHECK_EQ(expected, transpile(code).code); CHECK_EQ(expected, transpile(code).code);
}
else
{
const std::string expected = R"( local s= some_function() + something_else() )";
CHECK_EQ(expected, transpile(code).code);
}
} }
TEST_CASE("function_taking_ellipsis") TEST_CASE("function_taking_ellipsis")
@ -130,6 +149,7 @@ TEST_CASE("for_loop")
TEST_CASE("for_loop_spaces_around_tokens") TEST_CASE("for_loop_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string one = R"( for index = 1, 10 do call(index) end )"; const std::string one = R"( for index = 1, 10 do call(index) end )";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
@ -154,6 +174,7 @@ TEST_CASE("for_in_loop")
TEST_CASE("for_in_loop_spaces_around_tokens") TEST_CASE("for_in_loop_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string one = R"( for k, v in ipairs(x) do end )"; const std::string one = R"( for k, v in ipairs(x) do end )";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
@ -172,6 +193,7 @@ TEST_CASE("for_in_loop_spaces_around_tokens")
TEST_CASE("for_in_single_variable") TEST_CASE("for_in_single_variable")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string one = R"( for key in pairs(x) do end )"; const std::string one = R"( for key in pairs(x) do end )";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
} }
@ -184,6 +206,7 @@ TEST_CASE("while_loop")
TEST_CASE("while_loop_spaces_around_tokens") TEST_CASE("while_loop_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string one = R"( while f(x) do print() end )"; const std::string one = R"( while f(x) do print() end )";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
@ -205,6 +228,7 @@ TEST_CASE("repeat_until_loop")
TEST_CASE("repeat_until_loop_condition_on_new_line") TEST_CASE("repeat_until_loop_condition_on_new_line")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
repeat repeat
print() print()
@ -236,6 +260,7 @@ TEST_CASE("local_assignment")
TEST_CASE("local_assignment_spaces_around_tokens") TEST_CASE("local_assignment_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string one = R"( local x = 1 )"; const std::string one = R"( local x = 1 )";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
@ -269,6 +294,7 @@ TEST_CASE("local_function")
TEST_CASE("local_function_spaces_around_tokens") TEST_CASE("local_function_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string one = R"( local function p(o, m, ...) end )"; const std::string one = R"( local function p(o, m, ...) end )";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
@ -287,6 +313,7 @@ TEST_CASE("function")
TEST_CASE("function_spaces_around_tokens") TEST_CASE("function_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string two = R"( function p(o, m, ...) end )"; const std::string two = R"( function p(o, m, ...) end )";
CHECK_EQ(two, transpile(two).code); CHECK_EQ(two, transpile(two).code);
@ -315,6 +342,7 @@ TEST_CASE("function_spaces_around_tokens")
TEST_CASE("function_with_types_spaces_around_tokens") TEST_CASE("function_with_types_spaces_around_tokens")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true},
{FFlag::LuauStoreLocalAnnotationColonPositions, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true},
}; };
@ -375,7 +403,7 @@ TEST_CASE("function_with_types_spaces_around_tokens")
TEST_CASE("returns_spaces_around_tokens") TEST_CASE("returns_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string one = R"( return 1 )"; const std::string one = R"( return 1 )";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
@ -388,7 +416,7 @@ TEST_CASE("returns_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( type Foo = string )"; std::string code = R"( type Foo = string )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -437,7 +465,7 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "type_alias_with_defaults_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "type_alias_with_defaults_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( type Foo<X = string, Z... = ...any> = string )"; std::string code = R"( type Foo<X = string, Z... = ...any> = string )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -498,7 +526,7 @@ TEST_CASE("table_literal_closing_brace_at_correct_position")
TEST_CASE("table_literal_with_semicolon_separators") TEST_CASE("table_literal_with_semicolon_separators")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
local t = { x = 1; y = 2 } local t = { x = 1; y = 2 }
)"; )";
@ -508,7 +536,7 @@ TEST_CASE("table_literal_with_semicolon_separators")
TEST_CASE("table_literal_with_trailing_separators") TEST_CASE("table_literal_with_trailing_separators")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
local t = { x = 1, y = 2, } local t = { x = 1, y = 2, }
)"; )";
@ -518,7 +546,7 @@ TEST_CASE("table_literal_with_trailing_separators")
TEST_CASE("table_literal_with_spaces_around_separator") TEST_CASE("table_literal_with_spaces_around_separator")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
local t = { x = 1 , y = 2 } local t = { x = 1 , y = 2 }
)"; )";
@ -528,7 +556,7 @@ TEST_CASE("table_literal_with_spaces_around_separator")
TEST_CASE("table_literal_with_spaces_around_equals") TEST_CASE("table_literal_with_spaces_around_equals")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
local t = { x = 1 } local t = { x = 1 }
)"; )";
@ -538,7 +566,7 @@ TEST_CASE("table_literal_with_spaces_around_equals")
TEST_CASE("table_literal_multiline_with_indexers") TEST_CASE("table_literal_multiline_with_indexers")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
local t = { local t = {
["my first value"] = "x"; ["my first value"] = "x";
@ -566,7 +594,15 @@ TEST_CASE("spaces_between_keywords_even_if_it_pushes_the_line_estimation_off")
// Luau::Parser doesn't exactly preserve the string representation of numbers in Lua, so we can find ourselves // Luau::Parser doesn't exactly preserve the string representation of numbers in Lua, so we can find ourselves
// falling out of sync with the original code. We need to push keywords out so that there's at least one space between them. // falling out of sync with the original code. We need to push keywords out so that there's at least one space between them.
const std::string code = R"( if math.abs(raySlope) < .01 then return 0 end )"; const std::string code = R"( if math.abs(raySlope) < .01 then return 0 end )";
if (FFlag::LuauStoreCSTData2)
{
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
}
else
{
const std::string expected = R"( if math.abs(raySlope) < 0.01 then return 0 end)";
CHECK_EQ(expected, transpile(code).code);
}
} }
TEST_CASE("numbers") TEST_CASE("numbers")
@ -578,26 +614,34 @@ TEST_CASE("numbers")
TEST_CASE("infinity") TEST_CASE("infinity")
{ {
const std::string code = R"( local a = 1e500 local b = 1e400 )"; const std::string code = R"( local a = 1e500 local b = 1e400 )";
if (FFlag::LuauStoreCSTData2)
{
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
}
else
{
const std::string expected = R"( local a = 1e500 local b = 1e500 )";
CHECK_EQ(expected, transpile(code).code);
}
} }
TEST_CASE("numbers_with_separators") TEST_CASE("numbers_with_separators")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( local a = 123_456_789 )"; const std::string code = R"( local a = 123_456_789 )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("hexadecimal_numbers") TEST_CASE("hexadecimal_numbers")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( local a = 0xFFFF )"; const std::string code = R"( local a = 0xFFFF )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("binary_numbers") TEST_CASE("binary_numbers")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( local a = 0b0101 )"; const std::string code = R"( local a = 0b0101 )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
@ -610,28 +654,28 @@ TEST_CASE("single_quoted_strings")
TEST_CASE("double_quoted_strings") TEST_CASE("double_quoted_strings")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( local a = "hello world" )"; const std::string code = R"( local a = "hello world" )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("simple_interp_string") TEST_CASE("simple_interp_string")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( local a = `hello world` )"; const std::string code = R"( local a = `hello world` )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("raw_strings") TEST_CASE("raw_strings")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( local a = [[ hello world ]] )"; const std::string code = R"( local a = [[ hello world ]] )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("raw_strings_with_blocks") TEST_CASE("raw_strings_with_blocks")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( local a = [==[ hello world ]==] )"; const std::string code = R"( local a = [==[ hello world ]==] )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
@ -650,7 +694,7 @@ TEST_CASE("escaped_strings_2")
TEST_CASE("escaped_strings_newline") TEST_CASE("escaped_strings_newline")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
print("foo \ print("foo \
bar") bar")
@ -660,14 +704,14 @@ TEST_CASE("escaped_strings_newline")
TEST_CASE("escaped_strings_raw") TEST_CASE("escaped_strings_raw")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( local x = [=[\v<((do|load)file|require)\s*\(?['"]\zs[^'"]+\ze['"]]=] )"; const std::string code = R"( local x = [=[\v<((do|load)file|require)\s*\(?['"]\zs[^'"]+\ze['"]]=] )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("position_correctly_updated_when_writing_multiline_string") TEST_CASE("position_correctly_updated_when_writing_multiline_string")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
call([[ call([[
testing testing
@ -713,56 +757,56 @@ TEST_CASE("function_call_parentheses_multiple_args_no_space")
TEST_CASE("function_call_parentheses_multiple_args_space_before_commas") TEST_CASE("function_call_parentheses_multiple_args_space_before_commas")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( call(arg1 ,arg3 ,arg3) )"; const std::string code = R"( call(arg1 ,arg3 ,arg3) )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("function_call_spaces_before_parentheses") TEST_CASE("function_call_spaces_before_parentheses")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( call () )"; const std::string code = R"( call () )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("function_call_spaces_within_parentheses") TEST_CASE("function_call_spaces_within_parentheses")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( call( ) )"; const std::string code = R"( call( ) )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("function_call_string_double_quotes") TEST_CASE("function_call_string_double_quotes")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( call "string" )"; const std::string code = R"( call "string" )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("function_call_string_single_quotes") TEST_CASE("function_call_string_single_quotes")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( call 'string' )"; const std::string code = R"( call 'string' )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("function_call_string_no_space") TEST_CASE("function_call_string_no_space")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( call'string' )"; const std::string code = R"( call'string' )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("function_call_table_literal") TEST_CASE("function_call_table_literal")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( call { x = 1 } )"; const std::string code = R"( call { x = 1 } )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
TEST_CASE("function_call_table_literal_no_space") TEST_CASE("function_call_table_literal_no_space")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( call{x=1} )"; const std::string code = R"( call{x=1} )";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
} }
@ -807,7 +851,7 @@ TEST_CASE("emit_a_do_block_in_cases_of_potentially_ambiguous_syntax")
TEST_CASE_FIXTURE(Fixture, "parentheses_multiline") TEST_CASE_FIXTURE(Fixture, "parentheses_multiline")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
local test = ( local test = (
x x
@ -819,6 +863,9 @@ local test = (
TEST_CASE_FIXTURE(Fixture, "stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "stmt_semicolon")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( local test = 1; )"; std::string code = R"( local test = 1; )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -828,6 +875,7 @@ TEST_CASE_FIXTURE(Fixture, "stmt_semicolon")
TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon") TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon")
{ {
ScopedFastFlag sff{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
do do
return; return;
@ -838,6 +886,9 @@ TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon")
TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( std::string code = R"(
if init then if init then
x = string.sub(x, utf8.offset(x, init)); x = string.sub(x, utf8.offset(x, init));
@ -848,6 +899,9 @@ TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon")
TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2") TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( std::string code = R"(
if (t < 1) then return c/2*t*t + b end; if (t < 1) then return c/2*t*t + b end;
)"; )";
@ -856,6 +910,9 @@ TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2")
TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( std::string code = R"(
for i,v in ... do for i,v in ... do
end; end;
@ -865,6 +922,9 @@ TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon")
TEST_CASE_FIXTURE(Fixture, "while_do_semicolon") TEST_CASE_FIXTURE(Fixture, "while_do_semicolon")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( std::string code = R"(
while true do while true do
end; end;
@ -874,6 +934,9 @@ TEST_CASE_FIXTURE(Fixture, "while_do_semicolon")
TEST_CASE_FIXTURE(Fixture, "function_definition_semicolon") TEST_CASE_FIXTURE(Fixture, "function_definition_semicolon")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( std::string code = R"(
function foo() function foo()
end; end;
@ -951,7 +1014,16 @@ TEST_CASE("a_table_key_can_be_the_empty_string")
TEST_CASE("always_emit_a_space_after_local_keyword") TEST_CASE("always_emit_a_space_after_local_keyword")
{ {
std::string code = "do local aZZZZ = Workspace.P1.Shape local bZZZZ = Enum.PartType.Cylinder end"; std::string code = "do local aZZZZ = Workspace.P1.Shape local bZZZZ = Enum.PartType.Cylinder end";
if (FFlag::LuauStoreCSTData2)
{
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
}
else
{
std::string expected = "do local aZZZZ = Workspace.P1 .Shape local bZZZZ= Enum.PartType.Cylinder end";
CHECK_EQ(expected, transpile(code).code);
}
} }
TEST_CASE_FIXTURE(Fixture, "types_should_not_be_considered_cyclic_if_they_are_not_recursive") TEST_CASE_FIXTURE(Fixture, "types_should_not_be_considered_cyclic_if_they_are_not_recursive")
@ -988,13 +1060,23 @@ TEST_CASE_FIXTURE(Fixture, "type_lists_should_be_emitted_correctly")
end end
)"; )";
std::string expected = R"( std::string expected = FFlag::LuauStoreCSTData2 ? R"(
local a:(a:string,b:number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number) local a:(a:string,b:number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number)
end end
local b:(...string)->(...number)=function(...:string): ...number local b:(...string)->(...number)=function(...:string): ...number
end end
local c:()->()=function(): ()
end
)"
: R"(
local a:(string,number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number)
end
local b:(...string)->(...number)=function(...:string): ...number
end
local c:()->()=function(): () local c:()->()=function(): ()
end end
)"; )";
@ -1034,7 +1116,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_assertion")
TEST_CASE_FIXTURE(Fixture, "type_assertion_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "type_assertion_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = "local a = 5 :: number"; std::string code = "local a = 5 :: number";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1051,7 +1133,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else")
TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions") TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = "local a = if 1 then 2 elseif 3 then 4 else 5"; std::string code = "local a = if 1 then 2 elseif 3 then 4 else 5";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
@ -1059,7 +1141,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions")
TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2") TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
local x = if yes local x = if yes
then nil then nil
@ -1075,7 +1157,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2")
TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = "local a = if 1 then 2 else 3"; std::string code = "local a = if 1 then 2 else 3";
CHECK_EQ(code, transpile(code).code); CHECK_EQ(code, transpile(code).code);
@ -1112,7 +1194,7 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_between_else_if") TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_between_else_if")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
return return
if a then "was a" else if a then "was a" else
@ -1140,7 +1222,7 @@ local a: Import.Type
TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( local _: Foo.Type )"; std::string code = R"( local _: Foo.Type )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1169,6 +1251,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreLocalAnnotationColonPositions, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true},
}; };
std::string code = R"( local _: Type )"; std::string code = R"( local _: Type )";
@ -1190,6 +1273,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "transpile_for_loop_annotation_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_for_loop_annotation_spaces_around_tokens")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreLocalAnnotationColonPositions, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true},
}; };
std::string code = R"( for i: number = 1, 10 do end )"; std::string code = R"( for i: number = 1, 10 do end )";
@ -1230,7 +1314,7 @@ local b: Packed<(number, string)>
TEST_CASE_FIXTURE(Fixture, "type_packs_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "type_packs_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( type _ = Packed< T...> )"; std::string code = R"( type _ = Packed< T...> )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1288,7 +1372,11 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_2")
TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3") TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3")
{ {
std::string code = "local a: nil | (string & number)"; std::string code = "local a: nil | (string & number)";
if (FFlag::LuauStoreCSTData2)
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
else
CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code);
} }
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested") TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested")
@ -1308,6 +1396,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested_2")
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function") TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function")
{ {
ScopedFastFlag flags[] = { ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true},
}; };
std::string code = "type FnB<U...> = () -> U... & T"; std::string code = "type FnB<U...> = () -> U... & T";
@ -1317,6 +1406,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function")
TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe") TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = "local a: | string | number"; std::string code = "local a: | string | number";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1326,6 +1418,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe")
TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = "local a: string | number"; std::string code = "local a: string | number";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1335,6 +1430,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand") TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = "local a: & string & number"; std::string code = "local a: & string & number";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1344,6 +1442,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand")
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = "local a: string & number"; std::string code = "local a: string & number";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1353,6 +1454,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection") TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = "local a: string | (Foo & Bar)"; std::string code = "local a: string | (Foo & Bar)";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1377,6 +1481,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection")
TEST_CASE_FIXTURE(Fixture, "transpile_preserve_union_optional_style") TEST_CASE_FIXTURE(Fixture, "transpile_preserve_union_optional_style")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = "local a: string | nil"; std::string code = "local a: string | nil";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1408,7 +1515,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_varargs")
TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string one = "local _ = a.name"; std::string one = "local _ = a.name";
CHECK_EQ(one, transpile(one, {}, true).code); CHECK_EQ(one, transpile(one, {}, true).code);
@ -1421,7 +1528,7 @@ TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "index_name_ends_with_digit") TEST_CASE_FIXTURE(Fixture, "index_name_ends_with_digit")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = "sparkles.Color = Color3.new()"; std::string code = "sparkles.Color = Color3.new()";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
} }
@ -1435,7 +1542,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_index_expr")
TEST_CASE_FIXTURE(Fixture, "index_expr_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "index_expr_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string one = "local _ = a[2]"; std::string one = "local _ = a[2]";
CHECK_EQ(one, transpile(one, {}, true).code); CHECK_EQ(one, transpile(one, {}, true).code);
@ -1479,7 +1586,7 @@ local _ = # e
TEST_CASE_FIXTURE(Fixture, "binary_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "binary_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
local _ = 1+1 local _ = 1+1
local _ = 1 +1 local _ = 1 +1
@ -1521,7 +1628,7 @@ a ..= ' - result'
TEST_CASE_FIXTURE(Fixture, "compound_assignment_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "compound_assignment_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string one = R"( a += 1 )"; std::string one = R"( a += 1 )";
CHECK_EQ(one, transpile(one, {}, true).code); CHECK_EQ(one, transpile(one, {}, true).code);
@ -1538,7 +1645,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_assign_multiple")
TEST_CASE_FIXTURE(Fixture, "transpile_assign_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_assign_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string one = "a = 1"; std::string one = "a = 1";
CHECK_EQ(one, transpile(one).code); CHECK_EQ(one, transpile(one).code);
@ -1574,7 +1681,11 @@ local f: <T,S...>(T, S...)->(number) = foo
TEST_CASE_FIXTURE(Fixture, "transpile_union_reverse") TEST_CASE_FIXTURE(Fixture, "transpile_union_reverse")
{ {
std::string code = "local a: nil | number"; std::string code = "local a: nil | number";
if (FFlag::LuauStoreCSTData2)
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
else
CHECK_EQ("local a: number?", transpile(code, {}, true).code);
} }
TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple") TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple")
@ -1689,6 +1800,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple_types")
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp")
{ {
ScopedFastFlag fflags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( local _ = `hello {name}` )"; std::string code = R"( local _ = `hello {name}` )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1696,6 +1810,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp")
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline")
{ {
ScopedFastFlag fflags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( local _ = `hello { std::string code = R"( local _ = `hello {
name name
}!` )"; }!` )";
@ -1705,6 +1822,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline")
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line")
{ {
ScopedFastFlag fflags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( std::string code = R"(
error( error(
`a {b} c` `a {b} c`
@ -1716,6 +1836,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line")
TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( local _ = `hello \ std::string code = R"( local _ = `hello \
world!` )"; world!` )";
@ -1724,6 +1845,9 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape")
TEST_CASE_FIXTURE(Fixture, "transpile_string_literal_escape") TEST_CASE_FIXTURE(Fixture, "transpile_string_literal_escape")
{ {
ScopedFastFlag fflags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( local _ = ` bracket = \{, backtick = \` = {'ok'} ` )"; std::string code = R"( local _ = ` bracket = \{, backtick = \` = {'ok'} ` )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1738,6 +1862,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_functions")
TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( type function foo() end )"; std::string code = R"( type function foo() end )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1753,6 +1878,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens")
TEST_CASE_FIXTURE(Fixture, "transpile_typeof_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_typeof_spaces_around_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( type X = typeof(x) )"; std::string code = R"( type X = typeof(x) )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1777,14 +1903,14 @@ TEST_CASE("transpile_single_quoted_string_types")
TEST_CASE("transpile_double_quoted_string_types") TEST_CASE("transpile_double_quoted_string_types")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( type a = "hello world" )"; const std::string code = R"( type a = "hello world" )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
} }
TEST_CASE("transpile_raw_string_types") TEST_CASE("transpile_raw_string_types")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( type a = [[ hello world ]] )"; std::string code = R"( type a = [[ hello world ]] )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1794,14 +1920,14 @@ TEST_CASE("transpile_raw_string_types")
TEST_CASE("transpile_escaped_string_types") TEST_CASE("transpile_escaped_string_types")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( type a = "\\b\\t\\n\\\\" )"; const std::string code = R"( type a = "\\b\\t\\n\\\\" )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
} }
TEST_CASE("transpile_type_table_semicolon_separators") TEST_CASE("transpile_type_table_semicolon_separators")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
const std::string code = R"( const std::string code = R"(
type Foo = { type Foo = {
bar: number; bar: number;
@ -1813,7 +1939,7 @@ TEST_CASE("transpile_type_table_semicolon_separators")
TEST_CASE("transpile_type_table_access_modifiers") TEST_CASE("transpile_type_table_access_modifiers")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
type Foo = { type Foo = {
read bar: number, read bar: number,
@ -1834,7 +1960,7 @@ TEST_CASE("transpile_type_table_access_modifiers")
TEST_CASE("transpile_type_table_spaces_between_tokens") TEST_CASE("transpile_type_table_spaces_between_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( type Foo = { bar: number, } )"; std::string code = R"( type Foo = { bar: number, } )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1877,7 +2003,7 @@ TEST_CASE("transpile_type_table_spaces_between_tokens")
TEST_CASE("transpile_type_table_preserve_original_indexer_style") TEST_CASE("transpile_type_table_preserve_original_indexer_style")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
type Foo = { type Foo = {
[number]: string [number]: string
@ -1893,7 +2019,7 @@ TEST_CASE("transpile_type_table_preserve_original_indexer_style")
TEST_CASE("transpile_type_table_preserve_indexer_location") TEST_CASE("transpile_type_table_preserve_indexer_location")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
type Foo = { type Foo = {
[number]: string, [number]: string,
@ -1922,7 +2048,7 @@ TEST_CASE("transpile_type_table_preserve_indexer_location")
TEST_CASE("transpile_type_table_preserve_property_definition_style") TEST_CASE("transpile_type_table_preserve_property_definition_style")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
type Foo = { type Foo = {
["$$typeof1"]: string, ["$$typeof1"]: string,
@ -1934,7 +2060,7 @@ TEST_CASE("transpile_type_table_preserve_property_definition_style")
TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens") TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
type Foo = { type Foo = {
[ "$$typeof1"]: string, [ "$$typeof1"]: string,
@ -1946,6 +2072,9 @@ TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens")
TEST_CASE("transpile_types_preserve_parentheses_style") TEST_CASE("transpile_types_preserve_parentheses_style")
{ {
ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
};
std::string code = R"( type Foo = number )"; std::string code = R"( type Foo = number )";
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
@ -1985,6 +2114,7 @@ end
TEST_CASE("transpile_type_function_unnamed_arguments") TEST_CASE("transpile_type_function_unnamed_arguments")
{ {
ScopedFastFlag flags[] = { ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true},
}; };
std::string code = R"( type Foo = () -> () )"; std::string code = R"( type Foo = () -> () )";
@ -2021,6 +2151,7 @@ TEST_CASE("transpile_type_function_unnamed_arguments")
TEST_CASE("transpile_type_function_named_arguments") TEST_CASE("transpile_type_function_named_arguments")
{ {
ScopedFastFlag flags[] = { ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true},
}; };
std::string code = R"( type Foo = (x: string) -> () )"; std::string code = R"( type Foo = (x: string) -> () )";
@ -2051,6 +2182,7 @@ TEST_CASE("transpile_type_function_named_arguments")
TEST_CASE("transpile_type_function_generics") TEST_CASE("transpile_type_function_generics")
{ {
ScopedFastFlag flags[] = { ScopedFastFlag flags[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true},
}; };
std::string code = R"( type Foo = <X, Y, Z...>() -> () )"; std::string code = R"( type Foo = <X, Y, Z...>() -> () )";
@ -2087,6 +2219,7 @@ TEST_CASE("transpile_type_function_generics")
TEST_CASE("transpile_type_function_return_types") TEST_CASE("transpile_type_function_return_types")
{ {
ScopedFastFlag fflags[] = { ScopedFastFlag fflags[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauStoreReturnTypesAsPackOnAst, true},
}; };
std::string code = R"( type Foo = () -> () )"; std::string code = R"( type Foo = () -> () )";
@ -2132,6 +2265,23 @@ TEST_CASE("transpile_type_function_return_types")
CHECK_EQ(code, transpile(code, {}, true).code); CHECK_EQ(code, transpile(code, {}, true).code);
} }
TEST_CASE("transpile_chained_function_types")
{
ScopedFastFlag fflags[] = {
{FFlag::LuauStoreCSTData2, true},
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
{FFlag::LuauCSTForReturnTypeFunctionTail, true},
};
std::string code = R"( type Foo = () -> () -> () )";
CHECK_EQ(code, transpile(code, {}, true).code);
code = R"( type Foo = () -> () -> () )";
CHECK_EQ(code, transpile(code, {}, true).code);
code = R"( type Foo = () -> () -> () )";
CHECK_EQ(code, transpile(code, {}, true).code);
}
TEST_CASE("fuzzer_nil_optional") TEST_CASE("fuzzer_nil_optional")
{ {
const std::string code = R"( local x: nil? )"; const std::string code = R"( local x: nil? )";
@ -2140,7 +2290,7 @@ TEST_CASE("fuzzer_nil_optional")
TEST_CASE("transpile_function_attributes") TEST_CASE("transpile_function_attributes")
{ {
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
std::string code = R"( std::string code = R"(
@native @native
function foo() function foo()

View file

@ -14,7 +14,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauHasPropProperBlock) LUAU_FASTFLAG(LuauHasPropProperBlock)
LUAU_FASTFLAG(LuauSimplifyOutOfLine) LUAU_FASTFLAG(LuauSimplifyOutOfLine)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
@ -1725,7 +1725,7 @@ struct TFFixture
TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}}; TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}};
const ScopedFastFlag sff[1] = { const ScopedFastFlag sff[1] = {
{FFlag::DebugLuauGreedyGeneralization, true}, {FFlag::LuauEagerGeneralization, true},
}; };
BuiltinTypeFunctions builtinTypeFunctions; BuiltinTypeFunctions builtinTypeFunctions;

View file

@ -9,6 +9,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests"); TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
@ -1986,13 +1987,20 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_bool")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
if (FFlag::LuauEagerGeneralization)
{
// FIXME: CLI-151985
// This test breaks because we can't see that eq<type?, b> is already fully reduced.
return;
}
CheckResult result = check(R"( CheckResult result = check(R"(
type function compare(arg) type function compare(arg)
return types.singleton(types.singleton(false) == arg) return types.singleton(types.singleton(false) == arg)
end end
local function ok(idx: compare<false>): true return idx end local function ok1(idx: compare<false>): true return idx end
local function ok(idx: compare<true>): false return idx end local function ok2(idx: compare<true>): false return idx end
)"); )");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
@ -2002,6 +2010,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_singleton_equality_string")
{ {
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
if (FFlag::LuauEagerGeneralization)
{
// FIXME: CLI-151985
// This test breaks because we can't see that eq<type?, b> is already fully reduced.
return;
}
CheckResult result = check(R"( CheckResult result = check(R"(
type function compare(arg) type function compare(arg)
return types.singleton(types.singleton("") == arg) return types.singleton(types.singleton("") == arg)

View file

@ -12,7 +12,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations) LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2) LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion) LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion2)
LUAU_FASTFLAG(LuauSkipMalformedTypeAliases) LUAU_FASTFLAG(LuauSkipMalformedTypeAliases)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
@ -355,8 +355,6 @@ TEST_CASE_FIXTURE(Fixture, "stringify_type_alias_of_recursive_template_table_typ
// Check that recursive intersection type doesn't generate an OOM // Check that recursive intersection type doesn't generate an OOM
TEST_CASE_FIXTURE(Fixture, "cli_38393_recursive_intersection_oom") TEST_CASE_FIXTURE(Fixture, "cli_38393_recursive_intersection_oom")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
function _(l0:(t0)&((t0)&(((t0)&((t0)->()))->(typeof(_),typeof(# _)))),l39,...):any function _(l0:(t0)&((t0)&(((t0)&((t0)->()))->(typeof(_),typeof(# _)))),l39,...):any
end end
@ -970,9 +968,6 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_locations")
*/ */
TEST_CASE_FIXTURE(BuiltinsFixture, "dont_lose_track_of_PendingExpansionTypes_after_substitution") TEST_CASE_FIXTURE(BuiltinsFixture, "dont_lose_track_of_PendingExpansionTypes_after_substitution")
{ {
// CLI-114134 - We need egraphs to properly simplify these types.
DOES_NOT_PASS_NEW_SOLVER_GUARD();
fileResolver.source["game/ReactCurrentDispatcher"] = R"( fileResolver.source["game/ReactCurrentDispatcher"] = R"(
export type BasicStateAction<S> = ((S) -> S) | S export type BasicStateAction<S> = ((S) -> S) | S
export type Dispatch<A> = (A) -> () export type Dispatch<A> = (A) -> ()
@ -1258,7 +1253,7 @@ TEST_CASE_FIXTURE(Fixture, "exported_type_function_location_is_accessible_on_mod
TEST_CASE_FIXTURE(Fixture, "fuzzer_cursed_type_aliases") TEST_CASE_FIXTURE(Fixture, "fuzzer_cursed_type_aliases")
{ {
ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion, true}; ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2, true};
// This used to crash under the new solver: we would like this to continue // This used to crash under the new solver: we would like this to continue
// to not crash. // to not crash.
@ -1298,5 +1293,16 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_dont_crash_on_duplicate_with_typeof")
CHECK(get<DuplicateTypeDefinition>(result.errors[0])); CHECK(get<DuplicateTypeDefinition>(result.errors[0]));
} }
TEST_CASE_FIXTURE(Fixture, "fuzzer_more_cursed_aliases")
{
ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion2, true};
LUAU_REQUIRE_ERRORS(check(R"(
export type t138 = t0<t138>
export type t0<t0,t10,t10,t109> = t0
)"));
}
TEST_SUITE_END(); TEST_SUITE_END();

View file

@ -11,7 +11,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauTableCloneClonesType3) LUAU_FASTFLAG(LuauTableCloneClonesType3)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
TEST_SUITE_BEGIN("BuiltinTests"); TEST_SUITE_BEGIN("BuiltinTests");
@ -460,7 +460,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_pack_reduce")
)"); )");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauSolverV2 && FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization)
CHECK("{ [number]: string | string | string, n: number }" == toString(requireType("t"))); CHECK("{ [number]: string | string | string, n: number }" == toString(requireType("t")));
else if (FFlag::LuauSolverV2) else if (FFlag::LuauSolverV2)
CHECK_EQ("{ [number]: string, n: number }", toString(requireType("t"))); CHECK_EQ("{ [number]: string, n: number }", toString(requireType("t")));

View file

@ -129,8 +129,6 @@ TEST_CASE_FIXTURE(ExternTypeFixture, "we_can_infer_that_a_parameter_must_be_a_pa
TEST_CASE_FIXTURE(ExternTypeFixture, "we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class") TEST_CASE_FIXTURE(ExternTypeFixture, "we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
function makeClone(o) function makeClone(o)
return BaseClass.Clone(o) return BaseClass.Clone(o)
@ -472,9 +470,6 @@ Type 'number' could not be converted into 'string')";
TEST_CASE_FIXTURE(ExternTypeFixture, "class_type_mismatch_with_name_conflict") TEST_CASE_FIXTURE(ExternTypeFixture, "class_type_mismatch_with_name_conflict")
{ {
// CLI-116433
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
local i = ChildClass.New() local i = ChildClass.New()
type ChildClass = { x: number } type ChildClass = { x: number }

View file

@ -22,7 +22,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
LUAU_FASTFLAG(LuauHasPropProperBlock) LUAU_FASTFLAG(LuauHasPropProperBlock)
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
@ -1038,9 +1038,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_lea
TEST_CASE_FIXTURE(Fixture, "too_many_return_values") TEST_CASE_FIXTURE(Fixture, "too_many_return_values")
{ {
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
@ -1472,9 +1469,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "variadic_any_is_compatible_with_a_generic_Ty
TEST_CASE_FIXTURE(Fixture, "infer_anonymous_function_arguments_outside_call") TEST_CASE_FIXTURE(Fixture, "infer_anonymous_function_arguments_outside_call")
{ {
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
type Table = { x: number, y: number } type Table = { x: number, y: number }
local f: (Table) -> number = function(t) return t.x + t.y end local f: (Table) -> number = function(t) return t.x + t.y end
@ -1694,7 +1688,7 @@ t.f = function(x)
end end
)"); )");
if (FFlag::DebugLuauGreedyGeneralization && FFlag::LuauSolverV2) if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
{ {
// FIXME CLI-151985 // FIXME CLI-151985
LUAU_CHECK_ERROR_COUNT(3, result); LUAU_CHECK_ERROR_COUNT(3, result);
@ -1779,7 +1773,7 @@ t.f = function(x)
end end
)"); )");
if (FFlag::DebugLuauGreedyGeneralization && FFlag::LuauSolverV2) if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
{ {
// FIXME CLI-151985 // FIXME CLI-151985
LUAU_CHECK_ERROR_COUNT(2, result); LUAU_CHECK_ERROR_COUNT(2, result);
@ -1981,10 +1975,15 @@ TEST_CASE_FIXTURE(Fixture, "dont_infer_parameter_types_for_functions_from_their_
CHECK_EQ("<a>(a) -> a", toString(requireType("f"))); CHECK_EQ("<a>(a) -> a", toString(requireType("f")));
if (FFlag::LuauSolverV2) if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
{
LUAU_CHECK_NO_ERRORS(result);
CHECK("<a>({ read p: { read q: a } }) -> (a & ~(false?))?" == toString(requireType("g")));
}
else if (FFlag::LuauSolverV2)
{ {
// FIXME CLI-143852: Depends on interleaving generalization and type function reduction. // FIXME CLI-143852: Depends on interleaving generalization and type function reduction.
LUAU_REQUIRE_ERRORS(result); LUAU_CHECK_ERRORS(result);
CHECK_EQ("({ read p: unknown }) -> (*error-type* | ~(false?))?", toString(requireType("g"))); CHECK_EQ("({ read p: unknown }) -> (*error-type* | ~(false?))?", toString(requireType("g")));
} }
else else
@ -2941,7 +2940,7 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
{ {
// The new solver should ideally be able to do better here, but this is no worse than the old solver. // The new solver should ideally be able to do better here, but this is no worse than the old solver.
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);
auto tm1 = get<TypeMismatch>(result.errors[0]); auto tm1 = get<TypeMismatch>(result.errors[0]);

View file

@ -334,9 +334,6 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed")
TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect") TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
{ {
// CLI-116476 Subtyping between type alias and an equivalent but not named type isn't working.
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
type X = { x: (number) -> number } type X = { x: (number) -> number }
type Y = { y: (string) -> string } type Y = { y: (string) -> string }

View file

@ -325,9 +325,6 @@ TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_error")
TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function") TEST_CASE_FIXTURE(Fixture, "for_in_loop_on_non_function")
{ {
// We report a spuriouus duplicate error here.
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
local bad_iter = 5 local bad_iter = 5

View file

@ -13,7 +13,7 @@
LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2)
@ -787,7 +787,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "leaky_generics")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
CHECK_EQ("(unknown) -> unknown", toString(requireTypeAtPosition({13, 23}))); CHECK_EQ("(unknown) -> unknown", toString(requireTypeAtPosition({13, 23})));
} }

View file

@ -17,7 +17,7 @@
using namespace Luau; using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
TEST_SUITE_BEGIN("TypeInferOperators"); TEST_SUITE_BEGIN("TypeInferOperators");
@ -29,7 +29,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types")
)"); )");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
// FIXME: Regression // FIXME: Regression
CHECK("(string & ~(false?)) | number" == toString(*requireType("s"))); CHECK("(string & ~(false?)) | number" == toString(*requireType("s")));
@ -51,7 +51,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras")
)"); )");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
// FIXME: Regression. // FIXME: Regression.
CHECK("(string & ~(false?)) | number" == toString(*requireType("s"))); CHECK("(string & ~(false?)) | number" == toString(*requireType("s")));
@ -72,7 +72,7 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union")
)"); )");
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
// FIXME: Regression // FIXME: Regression
CHECK("(string & ~(false?)) | string" == toString(requireType("s"))); CHECK("(string & ~(false?)) | string" == toString(requireType("s")));
@ -634,7 +634,20 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "typecheck_unary_minus_error")
local a = -foo local a = -foo
)"); )");
if (FFlag::LuauSolverV2) if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("string" == toString(requireType("a")));
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm);
// FIXME: This error is a bit weird.
CHECK("({ @metatable { __unm: (boolean) -> string }, { value: number } }) -> string" == toString(tm->wantedType, {true}));
CHECK("(boolean) -> string" == toString(tm->givenType));
}
else if (FFlag::LuauSolverV2)
{ {
LUAU_REQUIRE_ERROR_COUNT(2, result); LUAU_REQUIRE_ERROR_COUNT(2, result);

View file

@ -13,6 +13,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauStoreCSTData2)
LUAU_FASTINT(LuauNormalizeCacheLimit) LUAU_FASTINT(LuauNormalizeCacheLimit)
LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTINT(LuauTypeInferIterationLimit) LUAU_FASTINT(LuauTypeInferIterationLimit)
@ -48,7 +49,7 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
end end
)"; )";
const std::string expected = R"( const std::string expected = FFlag::LuauStoreCSTData2 ? R"(
function f(a:{fn:()->(a,b...)}): () function f(a:{fn:()->(a,b...)}): ()
if type(a) == 'boolean' then if type(a) == 'boolean' then
local a1:boolean=a local a1:boolean=a
@ -56,9 +57,18 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
local a2:{fn:()->(a,b...)}=a local a2:{fn:()->(a,b...)}=a
end end
end end
)"
: R"(
function f(a:{fn:()->(a,b...)}): ()
if type(a) == 'boolean'then
local a1:boolean=a
elseif a.fn()then
local a2:{fn:()->(a,b...)}=a
end
end
)"; )";
const std::string expectedWithNewSolver = R"( const std::string expectedWithNewSolver = FFlag::LuauStoreCSTData2 ? R"(
function f(a:{fn:()->(unknown,...unknown)}): () function f(a:{fn:()->(unknown,...unknown)}): ()
if type(a) == 'boolean' then if type(a) == 'boolean' then
local a1:{fn:()->(unknown,...unknown)}&boolean=a local a1:{fn:()->(unknown,...unknown)}&boolean=a
@ -66,9 +76,18 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a
end end
end end
)"
: R"(
function f(a:{fn:()->(unknown,...unknown)}): ()
if type(a) == 'boolean'then
local a1:{fn:()->(unknown,...unknown)}&boolean=a
elseif a.fn()then
local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a
end
end
)"; )";
const std::string expectedWithEqSat = R"( const std::string expectedWithEqSat = FFlag::LuauStoreCSTData2 ? R"(
function f(a:{fn:()->(unknown,...unknown)}): () function f(a:{fn:()->(unknown,...unknown)}): ()
if type(a) == 'boolean' then if type(a) == 'boolean' then
local a1:{fn:()->(unknown,...unknown)}&boolean=a local a1:{fn:()->(unknown,...unknown)}&boolean=a
@ -76,6 +95,15 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete")
local a2:{fn:()->(unknown,...unknown)}&negate<boolean>=a local a2:{fn:()->(unknown,...unknown)}&negate<boolean>=a
end end
end end
)"
: R"(
function f(a:{fn:()->(unknown,...unknown)}): ()
if type(a) == 'boolean'then
local a1:{fn:()->(unknown,...unknown)}&boolean=a
elseif a.fn()then
local a2:{fn:()->(unknown,...unknown)}&negate<boolean>=a
end
end
)"; )";
if (FFlag::LuauSolverV2 && !FFlag::DebugLuauEqSatSimplification) if (FFlag::LuauSolverV2 && !FFlag::DebugLuauEqSatSimplification)

View file

@ -10,11 +10,13 @@
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauEqSatSimplification) LUAU_FASTFLAG(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable) LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
LUAU_FASTFLAG(LuauWeakNilRefinementType) LUAU_FASTFLAG(LuauWeakNilRefinementType)
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
LUAU_FASTFLAG(LuauSimplificationTableExternType) LUAU_FASTFLAG(LuauSimplificationTableExternType)
LUAU_FASTFLAG(LuauBetterCannotCallFunctionPrimitive)
LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck)
LUAU_FASTFLAG(LuauAvoidDoubleNegation) LUAU_FASTFLAG(LuauAvoidDoubleNegation)
using namespace Luau; using namespace Luau;
@ -108,6 +110,13 @@ struct RefinementExternTypeFixture : BuiltinsFixture
{"IsA", Property{isA}}, {"IsA", Property{isA}},
}; };
TypeId scriptConnection = arena.addType(ExternType("ExternScriptConnection", {}, inst, std::nullopt, {}, nullptr, "Test", {}));
TypePackId disconnectArgs = arena.addTypePack({scriptConnection});
TypeId disconnect = arena.addType(FunctionType{disconnectArgs, builtinTypes->emptyTypePack});
getMutable<ExternType>(scriptConnection)->props = {
{"Disconnect", Property{disconnect}},
};
TypeId folder = frontend.globals.globalTypes.addType(ExternType{"Folder", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); TypeId folder = frontend.globals.globalTypes.addType(ExternType{"Folder", {}, inst, std::nullopt, {}, nullptr, "Test", {}});
TypeId part = frontend.globals.globalTypes.addType(ExternType{"Part", {}, inst, std::nullopt, {}, nullptr, "Test", {}}); TypeId part = frontend.globals.globalTypes.addType(ExternType{"Part", {}, inst, std::nullopt, {}, nullptr, "Test", {}});
getMutable<ExternType>(part)->props = { getMutable<ExternType>(part)->props = {
@ -123,6 +132,7 @@ struct RefinementExternTypeFixture : BuiltinsFixture
frontend.globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vec3}; frontend.globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vec3};
frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, inst}; frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, inst};
frontend.globals.globalScope->exportedTypeBindings["ExternScriptConnection"] = TypeFun{{}, scriptConnection};
frontend.globals.globalScope->exportedTypeBindings["Folder"] = TypeFun{{}, folder}; frontend.globals.globalScope->exportedTypeBindings["Folder"] = TypeFun{{}, folder};
frontend.globals.globalScope->exportedTypeBindings["Part"] = TypeFun{{}, part}; frontend.globals.globalScope->exportedTypeBindings["Part"] = TypeFun{{}, part};
frontend.globals.globalScope->exportedTypeBindings["WeldConstraint"] = TypeFun{{}, weldConstraint}; frontend.globals.globalScope->exportedTypeBindings["WeldConstraint"] = TypeFun{{}, weldConstraint};
@ -760,7 +770,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonoptional_type_can_narrow_to_nil_if_sense_
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
{ {
CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil" CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil"
CHECK("string & unknown & unknown & ~nil" == toString(requireTypeAtPosition({6, 24}))); // type(v) ~= "nil" CHECK("string & unknown & unknown & ~nil" == toString(requireTypeAtPosition({6, 24}))); // type(v) ~= "nil"
@ -2504,7 +2514,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi
end end
)")); )"));
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
CHECK_EQ("nil & string & unknown", toString(requireTypeAtPosition({4, 24}))); CHECK_EQ("nil & string & unknown", toString(requireTypeAtPosition({4, 24})));
else else
CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24}))); CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24})));
@ -2648,5 +2658,77 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1451")
)")); )"));
} }
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
CheckResult result = check(R"(
type Disconnectable = {
Disconnect: (self: Disconnectable) -> (...any);
} | {
disconnect: (self: Disconnectable) -> (...any)
} | ExternScriptConnection
local x: Disconnectable = workspace.ChildAdded:Connect(function()
print("child added")
end)
if type(x.Disconnect) == "function" then
x:Disconnect()
end
)");
if (FFlag::LuauTypeCheckerStricterIndexCheck)
{
LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK_EQ(toString(result.errors[0]), "Key 'Disconnect' is missing from 't2 where t1 = ExternScriptConnection | t2 | { Disconnect: (t1) -> (...any) } ; t2 = { disconnect: (t1) -> (...any) }' in the type 't1 where t1 = ExternScriptConnection | { Disconnect: (t1) -> (...any) } | { disconnect: (t1) -> (...any) }'");
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
CHECK_EQ(toString(result.errors[1]), "The type function is not precise enough for us to determine the appropriate result type of this call.");
else
CHECK_EQ(toString(result.errors[1]), "Cannot call a value of type function");
}
else
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
if (FFlag::LuauBetterCannotCallFunctionPrimitive)
CHECK_EQ(toString(result.errors[0]), "The type function is not precise enough for us to determine the appropriate result type of this call.");
else
CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type function");
}
}
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1835")
{
LUAU_REQUIRE_NO_ERRORS(check(R"(
--!strict
local t: {name: string}? = nil
function f()
local name = if t then t.name else "name"
end
)"));
LUAU_REQUIRE_NO_ERRORS(check(R"(
--!strict
local t: {name: string}? = nil
function f()
if t then end
local name = if t then t.name else "name"
end
)"));
CheckResult result = check(R"(
local t: {name: string}? = nil
if t then end
print(t.name)
local name = if t then t.name else "name"
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(get<OptionalValueAccess>(result.errors[0]));
}
TEST_SUITE_END(); TEST_SUITE_END();

View file

@ -21,8 +21,8 @@ LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering) LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint) LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert) LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert)
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
@ -30,6 +30,11 @@ LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck)
LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauSimplifyOutOfLine) LUAU_FASTFLAG(LuauSimplifyOutOfLine)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
LUAU_FASTFLAG(LuauEnableWriteOnlyProperties)
LUAU_FASTFLAG(LuauDisablePrimitiveInferenceInLargeTables)
LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit)
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
TEST_SUITE_BEGIN("TableTests"); TEST_SUITE_BEGIN("TableTests");
@ -594,9 +599,6 @@ TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_assignmen
TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_function_call") TEST_CASE_FIXTURE(Fixture, "okay_to_add_property_to_unsealed_tables_by_function_call")
{ {
// CLI-114873
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
function get(x) return x.opts["MYOPT"] end function get(x) return x.opts["MYOPT"] end
@ -700,7 +702,7 @@ TEST_CASE_FIXTURE(Fixture, "indexers_get_quantified_too")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::LuauSolverV2 && FFlag::LuauNonReentrantGeneralization3) if (FFlag::LuauSolverV2 && FFlag::LuauEagerGeneralization)
CHECK("<a>({a}) -> ()" == toString(requireType("swap"))); CHECK("<a>({a}) -> ()" == toString(requireType("swap")));
else if (FFlag::LuauSolverV2) else if (FFlag::LuauSolverV2)
CHECK("({unknown}) -> ()" == toString(requireType("swap"))); CHECK("({unknown}) -> ()" == toString(requireType("swap")));
@ -1153,8 +1155,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_inferred")
TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_both_ways") TEST_CASE_FIXTURE(BuiltinsFixture, "meta_add_both_ways")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
type VectorMt = { __add: (Vector, number) -> Vector } type VectorMt = { __add: (Vector, number) -> Vector }
local vectorMt: VectorMt local vectorMt: VectorMt
@ -2210,7 +2210,6 @@ local Test: {Table} = {
TEST_CASE_FIXTURE(Fixture, "common_table_element_general") TEST_CASE_FIXTURE(Fixture, "common_table_element_general")
{ {
// CLI-115275 - Bidirectional inference does not always propagate indexer types into the expression
DOES_NOT_PASS_NEW_SOLVER_GUARD(); DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
@ -2380,7 +2379,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table
local c : string = t.m("hi") local c : string = t.m("hi")
)"); )");
if (FFlag::DebugLuauGreedyGeneralization && FFlag::LuauSolverV2) if (FFlag::LuauEagerGeneralization && FFlag::LuauSolverV2)
{ {
// FIXME CLI-151985 // FIXME CLI-151985
LUAU_CHECK_ERROR_COUNT(2, result); LUAU_CHECK_ERROR_COUNT(2, result);
@ -2651,7 +2650,6 @@ Type 'number' could not be converted into 'string' in an invariant context)";
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table") TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table")
{ {
// Table properties like HasSuper.p must be invariant. The new solver rightly rejects this program.
DOES_NOT_PASS_NEW_SOLVER_GUARD(); DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
@ -2702,9 +2700,6 @@ Table type '{ x: number, y: number }' not compatible with type 'Super' because t
TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer") TEST_CASE_FIXTURE(Fixture, "explicitly_typed_table_with_indexer")
{ {
// CLI-114791 Bidirectional inference should be able to cause the inference engine to forget that a table literal has some property
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
type Super = { x : number } type Super = { x : number }
@ -3752,7 +3747,12 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shap
TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type") TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type")
{ {
ScopedFastFlag sffs[] = {{FFlag::LuauNonReentrantGeneralization3, true}, {FFlag::LuauReportSubtypingErrors, true}}; ScopedFastFlag sff[] = {
{FFlag::LuauReportSubtypingErrors, true},
{FFlag::LuauEagerGeneralization, true},
{FFlag::LuauSubtypeGenericsAndNegations, true},
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
};
CheckResult result = check(R"( CheckResult result = check(R"(
local function f(s) local function f(s)
@ -4412,7 +4412,7 @@ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported
CHECK(Location{{5, 18}, {5, 23}} == result.errors[3].location); CHECK(Location{{5, 18}, {5, 23}} == result.errors[3].location);
} }
TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported") TEST_CASE_FIXTURE(Fixture, "read_and_write_only_indexers_are_unsupported")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
type T = {read [string]: number} type T = {read [string]: number}
@ -4442,6 +4442,22 @@ TEST_CASE_FIXTURE(Fixture, "infer_write_property")
CHECK("({ y: number }) -> ()" == toString(requireType("f"))); CHECK("({ y: number }) -> ()" == toString(requireType("f")));
} }
TEST_CASE_FIXTURE(Fixture, "new_solver_supports_read_write_properties")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag sff2{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"(
type W = {read x: number}
type X = {write x: boolean}
type Y = {read ["prop"]: boolean}
type Z = {write ["prop"]: string}
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression") TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
@ -4495,6 +4511,59 @@ TEST_CASE_FIXTURE(Fixture, "write_to_read_only_property")
CHECK(PropertyAccessViolation::CannotWrite == pav->context); CHECK(PropertyAccessViolation::CannotWrite == pav->context);
} }
TEST_CASE_FIXTURE(Fixture, "write_to_write_only_property")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"(
function f(t: {write x: number})
t.x = 5
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "bidirectional_typechecking_with_write_only_property")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"(
function f(t: {write x: number})
t.x = 5
end
f({ x = 2 })
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "read_from_write_only_property")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
ScopedFastFlag writeOnly{FFlag::LuauEnableWriteOnlyProperties, true};
CheckResult result = check(R"(
function f(t: {write x: number})
local foo = t.x
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("Property x of table '{ write x: number }' is write-only" == toString(result.errors[0]));
PropertyAccessViolation* pav = get<PropertyAccessViolation>(result.errors[0]);
REQUIRE(pav);
CHECK("{ write x: number }" == toString(pav->table, {true}));
CHECK("x" == pav->key);
CHECK(PropertyAccessViolation::CannotRead == pav->context);
}
TEST_CASE_FIXTURE(Fixture, "write_to_unusually_named_read_only_property") TEST_CASE_FIXTURE(Fixture, "write_to_unusually_named_read_only_property")
{ {
ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopedFastFlag sff{FFlag::LuauSolverV2, true};
@ -4510,7 +4579,7 @@ TEST_CASE_FIXTURE(Fixture, "write_to_unusually_named_read_only_property")
CHECK("Property \"hello world\" of table '{ read [\"hello world\"]: number }' is read-only" == toString(result.errors[0])); CHECK("Property \"hello world\" of table '{ read [\"hello world\"]: number }' is read-only" == toString(result.errors[0]));
} }
TEST_CASE_FIXTURE(Fixture, "write_annotations_are_unsupported_even_with_the_new_solver") TEST_CASE_FIXTURE(Fixture, "write_annotations_are_supported_with_the_new_solver")
{ {
ScopedFastFlag sff{FFlag::LuauSolverV2, true}; ScopedFastFlag sff{FFlag::LuauSolverV2, true};
@ -4519,10 +4588,15 @@ TEST_CASE_FIXTURE(Fixture, "write_annotations_are_unsupported_even_with_the_new_
end end
)"); )");
if (FFlag::LuauEnableWriteOnlyProperties)
LUAU_REQUIRE_NO_ERRORS(result);
else
{
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK("write keyword is illegal here" == toString(result.errors[0])); CHECK("write keyword is illegal here" == toString(result.errors[0]));
CHECK(Location{{1, 23}, {1, 28}} == result.errors[0].location); CHECK(Location{{1, 23}, {1, 28}} == result.errors[0].location);
}
} }
TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported") TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported")
@ -4549,10 +4623,8 @@ TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported
CHECK(Location{{5, 18}, {5, 23}} == result.errors[3].location); CHECK(Location{{5, 18}, {5, 23}} == result.errors[3].location);
} }
TEST_CASE_FIXTURE(Fixture, "read_ond_write_only_indexers_are_unsupported") TEST_CASE_FIXTURE(Fixture, "read_and_write_only_indexers_are_unsupported")
{ {
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
type T = {read [string]: number} type T = {read [string]: number}
type U = {write [string]: boolean} type U = {write [string]: boolean}
@ -4571,7 +4643,11 @@ TEST_CASE_FIXTURE(Fixture, "table_writes_introduce_write_properties")
if (!FFlag::LuauSolverV2) if (!FFlag::LuauSolverV2)
return; return;
ScopedFastFlag sff[] = {{FFlag::LuauNonReentrantGeneralization3, true}}; ScopedFastFlag sff[] = {
{FFlag::LuauEagerGeneralization, true},
{FFlag::LuauSubtypeGenericsAndNegations, true},
{FFlag::LuauNoMoreInjectiveTypeFunctions, true}
};
CheckResult result = check(R"( CheckResult result = check(R"(
function oc(player, speaker) function oc(player, speaker)
@ -4622,7 +4698,7 @@ TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array")
end end
)"); )");
if (FFlag::LuauSolverV2 && !FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauSolverV2 && !FFlag::LuauEagerGeneralization)
{ {
LUAU_CHECK_ERROR_COUNT(1, result); LUAU_CHECK_ERROR_COUNT(1, result);
LUAU_CHECK_ERROR(result, NotATable); LUAU_CHECK_ERROR(result, NotATable);
@ -4670,7 +4746,7 @@ TEST_CASE_FIXTURE(Fixture, "parameter_was_set_an_indexer_and_bounded_by_another_
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
// FIXME CLI-114134. We need to simplify types more consistently. // FIXME CLI-114134. We need to simplify types more consistently.
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
CHECK("({number} & {number}, unknown) -> ()" == toString(requireType("f"))); CHECK("({number} & {number}, unknown) -> ()" == toString(requireType("f")));
else else
CHECK_EQ("(unknown & {number} & {number}, unknown) -> ()", toString(requireType("f"))); CHECK_EQ("(unknown & {number} & {number}, unknown) -> ()", toString(requireType("f")));
@ -5212,8 +5288,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "write_only_table_field_duplicate")
} }
)"); )");
LUAU_CHECK_ERROR_COUNT(1, result); if (FFlag::LuauEnableWriteOnlyProperties)
LUAU_REQUIRE_NO_ERRORS(result);
else
{
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("write keyword is illegal here", toString(result.errors[0])); CHECK_EQ("write keyword is illegal here", toString(result.errors[0]));
}
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_musnt_assert") TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_musnt_assert")
@ -5661,5 +5742,81 @@ TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_assignment")
CHECK_EQ("boolean", toString(tm->wantedType)); CHECK_EQ("boolean", toString(tm->wantedType));
} }
TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_tables")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"(
type Word = "foo" | "bar"
local words: { Word } = { "foo", "bar", "foo" }
)");
// We expect this to have errors now, as we've set the limit for inference
// in tables so low.
LUAU_REQUIRE_ERROR_COUNT(3, result);
}
TEST_CASE_FIXTURE(Fixture, "disable_singleton_inference_on_large_nested_tables")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"(
type Word = "foo" | "bar"
local words: {{ Word }} = {{ "foo", "bar", "foo" }}
)");
LUAU_REQUIRE_ERROR_COUNT(3, result);
}
TEST_CASE_FIXTURE(Fixture, "large_table_inference_does_not_bleed")
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauTableLiteralSubtypeSpecificCheck, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
ScopedFastInt _{FInt::LuauPrimitiveInferenceInTableLimit, 2};
CheckResult result = check(R"(
type Word = "foo" | "bar"
local words: { Word } = { "foo", "bar", "foo" }
local otherWords: { Word } = {"foo"}
)");
LUAU_REQUIRE_ERROR_COUNT(3, result);
for (const auto& err: result.errors)
// Check that all of the errors are localized to `words`, not `otherWords`
CHECK(err.location.begin.line == 2);
}
#if 0
TEST_CASE_FIXTURE(Fixture, "extremely_large_table" * doctest::timeout(2.0))
{
ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauDisablePrimitiveInferenceInLargeTables, true},
};
const std::string source =
"local res = {\n" +
rep("\"foo\",\n", 100'000) +
"}";
LUAU_REQUIRE_NO_ERRORS(check(source));
CHECK_EQ("{string}", toString(requireType("res"), {true}));
}
#endif
TEST_SUITE_END(); TEST_SUITE_END();

View file

@ -28,7 +28,7 @@ LUAU_FASTFLAG(LuauTypeCheckerAcceptNumberConcats)
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument) LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2) LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2)
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions) LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect) LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
LUAU_FASTFLAG(LuauHasPropProperBlock) LUAU_FASTFLAG(LuauHasPropProperBlock)
LUAU_FASTFLAG(LuauStringPartLengthLimit) LUAU_FASTFLAG(LuauStringPartLengthLimit)
@ -37,6 +37,8 @@ LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauAvoidDoubleNegation) LUAU_FASTFLAG(LuauAvoidDoubleNegation)
LUAU_FASTFLAG(LuauInsertErrorTypesIntoIndexerResult) LUAU_FASTFLAG(LuauInsertErrorTypesIntoIndexerResult)
LUAU_FASTFLAG(LuauSimplifyOutOfLine) LUAU_FASTFLAG(LuauSimplifyOutOfLine)
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
using namespace Luau; using namespace Luau;
@ -445,7 +447,7 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit")
#endif #endif
ScopedFastInt luauRecursionLimit{FInt::LuauRecursionLimit, limit + 100}; ScopedFastInt luauRecursionLimit{FInt::LuauRecursionLimit, limit + 100};
ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit - 100}; ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit - 100};
ScopedFastFlag _{FFlag::LuauNonReentrantGeneralization3, false}; ScopedFastFlag _{FFlag::LuauEagerGeneralization, false};
CheckResult result = check(R"(("foo"))" + rep(":lower()", limit)); CheckResult result = check(R"(("foo"))" + rep(":lower()", limit));
@ -1258,9 +1260,6 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_cache_limit_normalizer")
TEST_CASE_FIXTURE(Fixture, "follow_on_new_types_in_substitution") TEST_CASE_FIXTURE(Fixture, "follow_on_new_types_in_substitution")
{ {
// CLI-114134
DOES_NOT_PASS_NEW_SOLVER_GUARD();
CheckResult result = check(R"( CheckResult result = check(R"(
local obj = {} local obj = {}
@ -2042,8 +2041,10 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert")
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauHasPropProperBlock, true}, {FFlag::LuauHasPropProperBlock, true},
{FFlag::LuauNonReentrantGeneralization3, true}, {FFlag::LuauEagerGeneralization, true},
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true} {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
{FFlag::LuauSubtypeGenericsAndNegations, true},
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
}; };
auto result = check(R"( auto result = check(R"(
@ -2078,8 +2079,10 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauNonReentrantGeneralization3, true}, {FFlag::LuauEagerGeneralization, true},
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}, {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
{FFlag::LuauSubtypeGenericsAndNegations, true},
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
@ -2106,15 +2109,19 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_generalize_one_remove_type_assert_2")
LUAU_REQUIRE_NO_ERROR(result, ConstraintSolvingIncompleteError); LUAU_REQUIRE_NO_ERROR(result, ConstraintSolvingIncompleteError);
} }
#if 0
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion") TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_simplify_combinatorial_explosion")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true}, {FFlag::LuauSolverV2, true},
{FFlag::LuauHasPropProperBlock, true}, {FFlag::LuauHasPropProperBlock, true},
{FFlag::LuauNonReentrantGeneralization3, true}, {FFlag::LuauEagerGeneralization, true},
{FFlag::LuauOptimizeFalsyAndTruthyIntersect, true}, {FFlag::LuauOptimizeFalsyAndTruthyIntersect, true},
{FFlag::LuauStringPartLengthLimit, true}, {FFlag::LuauStringPartLengthLimit, true},
{FFlag::LuauSimplificationRecheckAssumption, true}, {FFlag::LuauSimplificationRecheckAssumption, true},
{FFlag::LuauSubtypeGenericsAndNegations, true},
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
}; };
LUAU_REQUIRE_ERRORS(check(R"( LUAU_REQUIRE_ERRORS(check(R"(
@ -2128,6 +2135,8 @@ local _
)")); )"));
} }
#endif
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_missing_follow_table_freeze") TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_missing_follow_table_freeze")
{ {
ScopedFastFlag _{FFlag::LuauMagicFreezeCheckBlocked2, true}; ScopedFastFlag _{FFlag::LuauMagicFreezeCheckBlocked2, true};

View file

@ -12,10 +12,11 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall) LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
LUAU_FASTFLAG(LuauFixEmptyTypePackStringification)
TEST_SUITE_BEGIN("TypePackTests"); TEST_SUITE_BEGIN("TypePackTests");
@ -99,7 +100,7 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
CHECK_EQ("<a, b..., c...>((c...) -> (b...), (a) -> (c...), a) -> (b...)", toString(requireType("apply"))); CHECK_EQ("<a, b..., c...>((c...) -> (b...), (a) -> (c...), a) -> (b...)", toString(requireType("apply")));
else else
CHECK_EQ("<a, b..., c...>((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply"))); CHECK_EQ("<a, b..., c...>((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply")));
@ -339,6 +340,9 @@ local c: Packed<string, number, boolean>
REQUIRE(ttvA->instantiatedTypeParams.size() == 1); REQUIRE(ttvA->instantiatedTypeParams.size() == 1);
REQUIRE(ttvA->instantiatedTypePackParams.size() == 1); REQUIRE(ttvA->instantiatedTypePackParams.size() == 1);
CHECK_EQ(toString(ttvA->instantiatedTypeParams[0], {true}), "number"); CHECK_EQ(toString(ttvA->instantiatedTypeParams[0], {true}), "number");
if (FFlag::LuauFixEmptyTypePackStringification)
CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), "()");
else
CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), ""); CHECK_EQ(toString(ttvA->instantiatedTypePackParams[0], {true}), "");
auto ttvB = get<TableType>(requireType("b")); auto ttvB = get<TableType>(requireType("b"));

View file

@ -8,11 +8,12 @@ LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow) LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow)
LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauReportSubtypingErrors)
LUAU_FASTFLAG(LuauNonReentrantGeneralization3) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauDfgMatchCGScopes)
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument) LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops)
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions)
using namespace Luau; using namespace Luau;
@ -414,7 +415,13 @@ TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions")
TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_future_assignments") TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_future_assignments")
{ {
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauReportSubtypingErrors, true}, {FFlag::LuauNonReentrantGeneralization3, true}}; ScopedFastFlag sffs[] = {
{FFlag::LuauSolverV2, true},
{FFlag::LuauReportSubtypingErrors, true},
{FFlag::LuauEagerGeneralization, true},
{FFlag::LuauSubtypeGenericsAndNegations, true},
{FFlag::LuauNoMoreInjectiveTypeFunctions, true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
local f local f
@ -851,24 +858,22 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assign_in_an_if_branch_without_else")
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_table_freeze_in_binary_expr") TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_table_freeze_in_binary_expr")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag _{FFlag::LuauSolverV2, true};
{FFlag::LuauPreprocessTypestatedArgument, true}, // CLI-154237: This currently throws an exception due to a mismatch between
{FFlag::LuauDfgMatchCGScopes, true}, // the scopes created in the data flow graph versus the constraint generator.
}; CHECK_THROWS_AS(
check(R"(
// Previously this would ICE due to mismatched scopes between the
// constraint generator and the data flow graph.
LUAU_REQUIRE_ERRORS(check(R"(
local _ local _
if _ or table.freeze(_,_) or table.freeze(_,_) then if _ or table.freeze(_,_) or table.freeze(_,_) then
end end
)")); )"),
Luau::InternalCompilerError
);
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_in_conditional") TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_in_conditional")
{ {
ScopedFastFlag _{FFlag::LuauDfgMatchCGScopes, true}; ScopedFastFlag _{FFlag::LuauSolverV2, true};
// NOTE: This _probably_ should be disallowed, but it is representing that // NOTE: This _probably_ should be disallowed, but it is representing that
// type stating functions in short circuiting binary expressions do not // type stating functions in short circuiting binary expressions do not
// reflect their type states. // reflect their type states.
@ -883,21 +888,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_in_conditional")
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_table_freeze_in_conditional_expr") TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_table_freeze_in_conditional_expr")
{ {
ScopedFastFlag sffs[] = { ScopedFastFlag _{FFlag::LuauSolverV2, true};
{FFlag::LuauPreprocessTypestatedArgument, true}, // CLI-154237: This currently throws an exception due to a mismatch between
{FFlag::LuauDfgMatchCGScopes, true}, // the scopes created in the data flow graph versus the constraint generator.
}; CHECK_THROWS_AS(
check(R"(
// Previously this would ICE due to mismatched scopes between the
// constraint generator and the data flow graph.
LUAU_REQUIRE_ERRORS(check(R"(
--!strict
local _ local _
if if
if table.freeze(_,_) then _ else _ if table.freeze(_,_) then _ else _
then then
end end
)")); )"),
Luau::InternalCompilerError
);
} }
TEST_SUITE_END(); TEST_SUITE_END();

View file

@ -10,7 +10,7 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(DebugLuauGreedyGeneralization) LUAU_FASTFLAG(LuauEagerGeneralization)
LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck)
TEST_SUITE_BEGIN("UnionTypes"); TEST_SUITE_BEGIN("UnionTypes");
@ -902,7 +902,7 @@ TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
if (FFlag::DebugLuauGreedyGeneralization) if (FFlag::LuauEagerGeneralization)
CHECK_EQ( CHECK_EQ(
"<a>(({ read x: a } & { x: number }) | ({ read x: a } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f")) "<a>(({ read x: a } & { x: number }) | ({ read x: a } & { x: string })) -> { x: number } | { x: string }", toString(requireType("f"))
); );

View file

@ -8,7 +8,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions); LUAU_FASTFLAG(LuauNoMoreInjectiveTypeFunctions);
LUAU_FASTFLAG(DebugLuauGreedyGeneralization);
TEST_SUITE_BEGIN("TypeInferUnknownNever"); TEST_SUITE_BEGIN("TypeInferUnknownNever");

View file

@ -17,7 +17,6 @@ using namespace Luau::TypePath;
LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauSolverV2);
LUAU_DYNAMIC_FASTINT(LuauTypePathMaximumTraverseSteps); LUAU_DYNAMIC_FASTINT(LuauTypePathMaximumTraverseSteps);
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds);
struct TypePathFixture : Fixture struct TypePathFixture : Fixture
{ {

View file

@ -103,6 +103,12 @@ def main():
parser.add_argument( parser.add_argument(
"path", action="store", help="Path to the Luau.UnitTest executable" "path", action="store", help="Path to the Luau.UnitTest executable"
) )
parser.add_argument(
"--fflags",
dest="flags",
action="store",
help="Set extra FFlags",
)
parser.add_argument( parser.add_argument(
"--dump", "--dump",
dest="dump", dest="dump",
@ -136,9 +142,11 @@ def main():
failList = loadFailList() failList = loadFailList()
flags = ["true", "LuauSolverV2"] flags = "true,LuauSolverV2"
if args.flags:
flags += "," + args.flags
commandLine = [args.path, "--reporters=xml", "--fflags=" + ",".join(flags)] commandLine = [args.path, "--reporters=xml", "--fflags=" + flags]
if args.random_seed: if args.random_seed:
commandLine.append("--random-seed=" + str(args.random_seed)) commandLine.append("--random-seed=" + str(args.random_seed))