mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Sync to upstream/release/672
This commit is contained in:
parent
2b7a89db49
commit
3adf25898a
58 changed files with 1389 additions and 445 deletions
|
@ -74,10 +74,6 @@ struct FreeType
|
||||||
// This one got promoted to explicit
|
// This one got promoted to explicit
|
||||||
explicit FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound, Polarity polarity = Polarity::Unknown);
|
explicit FreeType(Scope* scope, TypeId lowerBound, TypeId upperBound, Polarity polarity = Polarity::Unknown);
|
||||||
explicit FreeType(Scope* scope, TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
explicit FreeType(Scope* scope, TypeLevel level, TypeId lowerBound, TypeId upperBound);
|
||||||
// Old constructors
|
|
||||||
explicit FreeType(TypeLevel level);
|
|
||||||
explicit FreeType(Scope* scope);
|
|
||||||
FreeType(Scope* scope, TypeLevel level);
|
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
|
|
|
@ -37,10 +37,6 @@ struct TypeArena
|
||||||
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope);
|
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope);
|
||||||
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope, TypeLevel level);
|
TypeId freshType(NotNull<BuiltinTypes> builtins, Scope* scope, TypeLevel level);
|
||||||
|
|
||||||
TypeId freshType_DEPRECATED(TypeLevel level);
|
|
||||||
TypeId freshType_DEPRECATED(Scope* scope);
|
|
||||||
TypeId freshType_DEPRECATED(Scope* scope, TypeLevel level);
|
|
||||||
|
|
||||||
TypePackId freshTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
TypePackId freshTypePack(Scope* scope, Polarity polarity = Polarity::Unknown);
|
||||||
|
|
||||||
TypePackId addTypePack(std::initializer_list<TypeId> types);
|
TypePackId addTypePack(std::initializer_list<TypeId> types);
|
||||||
|
|
|
@ -185,6 +185,8 @@ TypePackIterator begin(TypePackId tp);
|
||||||
TypePackIterator begin(TypePackId tp, const TxnLog* log);
|
TypePackIterator begin(TypePackId tp, const TxnLog* log);
|
||||||
TypePackIterator end(TypePackId tp);
|
TypePackIterator end(TypePackId tp);
|
||||||
|
|
||||||
|
TypePackId getTail(TypePackId tp);
|
||||||
|
|
||||||
using SeenSet = std::set<std::pair<const void*, const void*>>;
|
using SeenSet = std::set<std::pair<const void*, const void*>>;
|
||||||
|
|
||||||
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs);
|
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs);
|
||||||
|
|
|
@ -475,26 +475,26 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
writeRaw("}");
|
writeRaw("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(const AstGenericType& genericType)
|
void write(class AstGenericType* genericType)
|
||||||
{
|
{
|
||||||
writeRaw("{");
|
writeRaw("{");
|
||||||
bool c = pushComma();
|
bool c = pushComma();
|
||||||
writeType("AstGenericType");
|
writeType("AstGenericType");
|
||||||
write("name", genericType.name);
|
write("name", genericType->name);
|
||||||
if (genericType.defaultValue)
|
if (genericType->defaultValue)
|
||||||
write("luauType", genericType.defaultValue);
|
write("luauType", genericType->defaultValue);
|
||||||
popComma(c);
|
popComma(c);
|
||||||
writeRaw("}");
|
writeRaw("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(const AstGenericTypePack& genericTypePack)
|
void write(class AstGenericTypePack* genericTypePack)
|
||||||
{
|
{
|
||||||
writeRaw("{");
|
writeRaw("{");
|
||||||
bool c = pushComma();
|
bool c = pushComma();
|
||||||
writeType("AstGenericTypePack");
|
writeType("AstGenericTypePack");
|
||||||
write("name", genericTypePack.name);
|
write("name", genericTypePack->name);
|
||||||
if (genericTypePack.defaultValue)
|
if (genericTypePack->defaultValue)
|
||||||
write("luauType", genericTypePack.defaultValue);
|
write("luauType", genericTypePack->defaultValue);
|
||||||
popComma(c);
|
popComma(c);
|
||||||
writeRaw("}");
|
writeRaw("}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFollowTableFreeze)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked)
|
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
||||||
|
@ -1550,8 +1549,7 @@ bool MagicClone::infer(const MagicFunctionCallContext& context)
|
||||||
static std::optional<TypeId> freezeTable(TypeId inputType, const MagicFunctionCallContext& context)
|
static std::optional<TypeId> freezeTable(TypeId inputType, const MagicFunctionCallContext& context)
|
||||||
{
|
{
|
||||||
TypeArena* arena = context.solver->arena;
|
TypeArena* arena = context.solver->arena;
|
||||||
if (FFlag::LuauFollowTableFreeze)
|
inputType = follow(inputType);
|
||||||
inputType = follow(inputType);
|
|
||||||
if (auto mt = get<MetatableType>(inputType))
|
if (auto mt = get<MetatableType>(inputType))
|
||||||
{
|
{
|
||||||
std::optional<TypeId> frozenTable = freezeTable(mt->table, context);
|
std::optional<TypeId> frozenTable = freezeTable(mt->table, context);
|
||||||
|
|
|
@ -51,16 +51,7 @@ struct ReferenceCountInitializer : TypeOnceVisitor
|
||||||
|
|
||||||
bool visit(TypeId, const TypeFunctionInstanceType&) override
|
bool visit(TypeId, const TypeFunctionInstanceType&) override
|
||||||
{
|
{
|
||||||
// We do not consider reference counted types that are inside a type
|
return FFlag::DebugLuauGreedyGeneralization;
|
||||||
// function to be part of the reachable reference counted types.
|
|
||||||
// Otherwise, code can be constructed in just the right way such
|
|
||||||
// that two type functions both claim to mutate a free type, which
|
|
||||||
// prevents either type function from trying to generalize it, so
|
|
||||||
// we potentially get stuck.
|
|
||||||
//
|
|
||||||
// The default behavior here is `true` for "visit the child types"
|
|
||||||
// of this type, hence:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,8 +121,10 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
||||||
}
|
}
|
||||||
else if (auto hic = get<HasIndexerConstraint>(*this))
|
else if (auto hic = get<HasIndexerConstraint>(*this))
|
||||||
{
|
{
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
rci.traverse(hic->subjectType);
|
||||||
rci.traverse(hic->resultType);
|
rci.traverse(hic->resultType);
|
||||||
// `HasIndexerConstraint` should not mutate `subjectType` or `indexType`.
|
// `HasIndexerConstraint` should not mutate `indexType`.
|
||||||
}
|
}
|
||||||
else if (auto apc = get<AssignPropConstraint>(*this))
|
else if (auto apc = get<AssignPropConstraint>(*this))
|
||||||
{
|
{
|
||||||
|
@ -150,6 +143,10 @@ DenseHashSet<TypeId> Constraint::getMaybeMutatedFreeTypes() const
|
||||||
rci.traverse(ty);
|
rci.traverse(ty);
|
||||||
// `UnpackConstraint` should not mutate `sourcePack`.
|
// `UnpackConstraint` should not mutate `sourcePack`.
|
||||||
}
|
}
|
||||||
|
else if (auto rpc = get<ReduceConstraint>(*this); FFlag::DebugLuauGreedyGeneralization && rpc)
|
||||||
|
{
|
||||||
|
rci.traverse(rpc->ty);
|
||||||
|
}
|
||||||
else if (auto rpc = get<ReducePackConstraint>(*this))
|
else if (auto rpc = get<ReducePackConstraint>(*this))
|
||||||
{
|
{
|
||||||
rci.traverse(rpc->tp);
|
rci.traverse(rpc->tp);
|
||||||
|
|
|
@ -39,7 +39,6 @@ LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
||||||
|
|
||||||
|
@ -52,6 +51,7 @@ LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNoTypeFunctionsNamedTypeOf)
|
LUAU_FASTFLAGVARIABLE(LuauNoTypeFunctionsNamedTypeOf)
|
||||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||||
|
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1409,7 +1409,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 = !hasFreeType(sig.signature);
|
bool sigFullyDefined = FFlag::DebugLuauGreedyGeneralization ? false : !hasFreeType(sig.signature);
|
||||||
if (sigFullyDefined)
|
if (sigFullyDefined)
|
||||||
emplaceType<BoundType>(asMutable(functionType), sig.signature);
|
emplaceType<BoundType>(asMutable(functionType), sig.signature);
|
||||||
|
|
||||||
|
@ -1471,7 +1471,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 = !hasFreeType(sig.signature);
|
bool sigFullyDefined = FFlag::DebugLuauGreedyGeneralization ? false : !hasFreeType(sig.signature);
|
||||||
|
|
||||||
DefId def = dfg->getDef(function->name);
|
DefId def = dfg->getDef(function->name);
|
||||||
|
|
||||||
|
@ -2389,9 +2389,12 @@ InferencePack ConstraintGenerator::checkPack(const ScopePtr& scope, AstExprCall*
|
||||||
this,
|
this,
|
||||||
[checkConstraint, callConstraint](const ConstraintPtr& constraint)
|
[checkConstraint, callConstraint](const ConstraintPtr& constraint)
|
||||||
{
|
{
|
||||||
constraint->dependencies.emplace_back(checkConstraint);
|
if (!(FFlag::DebugLuauGreedyGeneralization && get<PrimitiveTypeConstraint>(*constraint)))
|
||||||
|
{
|
||||||
|
constraint->dependencies.emplace_back(checkConstraint);
|
||||||
|
|
||||||
callConstraint->dependencies.emplace_back(constraint.get());
|
callConstraint->dependencies.emplace_back(constraint.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2496,8 +2499,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantStrin
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FreeType ft =
|
FreeType ft = FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType};
|
||||||
FFlag::LuauFreeTypesMustHaveBounds ? FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType} : FreeType{scope.get()};
|
|
||||||
ft.lowerBound = arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}});
|
ft.lowerBound = arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}});
|
||||||
ft.upperBound = builtinTypes->stringType;
|
ft.upperBound = builtinTypes->stringType;
|
||||||
freeTy = arena->addType(ft);
|
freeTy = arena->addType(ft);
|
||||||
|
@ -2524,8 +2526,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprConstantBool*
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FreeType ft =
|
FreeType ft = FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType};
|
||||||
FFlag::LuauFreeTypesMustHaveBounds ? FreeType{scope.get(), builtinTypes->neverType, builtinTypes->unknownType} : FreeType{scope.get()};
|
|
||||||
ft.lowerBound = singletonType;
|
ft.lowerBound = singletonType;
|
||||||
ft.upperBound = builtinTypes->booleanType;
|
ft.upperBound = builtinTypes->booleanType;
|
||||||
freeTy = arena->addType(ft);
|
freeTy = arena->addType(ft);
|
||||||
|
@ -3076,7 +3077,7 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprLocal* local
|
||||||
if (ty)
|
if (ty)
|
||||||
{
|
{
|
||||||
TypeIds* localDomain = localTypes.find(*ty);
|
TypeIds* localDomain = localTypes.find(*ty);
|
||||||
if (localDomain)
|
if (localDomain && !(FFlag::LuauDoNotAddUpvalueTypesToLocalType && local->upvalue))
|
||||||
localDomain->insert(rhsType);
|
localDomain->insert(rhsType);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3107,8 +3108,10 @@ void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprLocal* local
|
||||||
if (annotatedTy)
|
if (annotatedTy)
|
||||||
addConstraint(scope, local->location, SubtypeConstraint{rhsType, *annotatedTy});
|
addConstraint(scope, local->location, SubtypeConstraint{rhsType, *annotatedTy});
|
||||||
|
|
||||||
if (TypeIds* localDomain = localTypes.find(*ty))
|
// This is vestigial.
|
||||||
localDomain->insert(rhsType);
|
if (!FFlag::LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
|
if (TypeIds* localDomain = localTypes.find(*ty))
|
||||||
|
localDomain->insert(rhsType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprGlobal* global, TypeId rhsType)
|
void ConstraintGenerator::visitLValue(const ScopePtr& scope, AstExprGlobal* global, TypeId rhsType)
|
||||||
|
@ -3410,13 +3413,22 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu
|
||||||
bodyScope->varargPack = std::nullopt;
|
bodyScope->varargPack = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LUAU_ASSERT(nullptr != varargPack);
|
||||||
|
|
||||||
if (FFlag::DebugLuauGreedyGeneralization)
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
{
|
{
|
||||||
genericTypes = argTypes;
|
// Some of the types in argTypes will eventually be generics, and some
|
||||||
|
// will not. The ones that are not generic will be pruned when
|
||||||
|
// GeneralizationConstraint dispatches.
|
||||||
|
genericTypes.insert(genericTypes.begin(), argTypes.begin(), argTypes.end());
|
||||||
|
varargPack = follow(varargPack);
|
||||||
|
returnType = follow(returnType);
|
||||||
|
if (varargPack == returnType)
|
||||||
|
genericTypePacks = {varargPack};
|
||||||
|
else
|
||||||
|
genericTypePacks = {varargPack, returnType};
|
||||||
}
|
}
|
||||||
|
|
||||||
LUAU_ASSERT(nullptr != varargPack);
|
|
||||||
|
|
||||||
// If there is both an annotation and an expected type, the annotation wins.
|
// If there is both an annotation and an expected type, the annotation wins.
|
||||||
// Type checking will sort out any discrepancies later.
|
// Type checking will sort out any discrepancies later.
|
||||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst && fn->returnAnnotation)
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst && fn->returnAnnotation)
|
||||||
|
|
|
@ -681,7 +681,6 @@ void ConstraintSolver::initFreeTypeTracking()
|
||||||
}
|
}
|
||||||
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
maybeMutatedFreeTypes.emplace(c, maybeMutatedTypesPerConstraint);
|
||||||
|
|
||||||
|
|
||||||
for (NotNull<const Constraint> dep : c->dependencies)
|
for (NotNull<const Constraint> dep : c->dependencies)
|
||||||
{
|
{
|
||||||
block(dep, c);
|
block(dep, c);
|
||||||
|
@ -2082,24 +2081,61 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
|
||||||
if (auto lhsFree = getMutable<FreeType>(lhsType))
|
if (auto lhsFree = getMutable<FreeType>(lhsType))
|
||||||
{
|
{
|
||||||
auto lhsFreeUpperBound = follow(lhsFree->upperBound);
|
auto lhsFreeUpperBound = follow(lhsFree->upperBound);
|
||||||
if (get<TableType>(lhsFreeUpperBound) || get<MetatableType>(lhsFreeUpperBound))
|
|
||||||
lhsType = lhsFreeUpperBound;
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
|
||||||
|
if (!blocked.empty())
|
||||||
|
{
|
||||||
|
for (TypeId t : blocked)
|
||||||
|
block(t, constraint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (maybeTy)
|
||||||
|
{
|
||||||
|
bind(constraint, c.propType, isIndex ? arena->addType(UnionType{{*maybeTy, builtinTypes->nilType}}) : *maybeTy);
|
||||||
|
unify(constraint, rhsType, *maybeTy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
|
||||||
|
|
||||||
|
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||||
|
|
||||||
|
TableType* upperTable = getMutable<TableType>(newUpperBound);
|
||||||
|
LUAU_ASSERT(upperTable);
|
||||||
|
|
||||||
|
upperTable->props[c.propName] = rhsType;
|
||||||
|
|
||||||
|
// Food for thought: Could we block if simplification encounters a blocked type?
|
||||||
|
lhsFree->upperBound = simplifyIntersection(constraint->scope, constraint->location, lhsFreeUpperBound, newUpperBound);
|
||||||
|
|
||||||
|
bind(constraint, c.propType, rhsType);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
|
if (get<TableType>(lhsFreeUpperBound) || get<MetatableType>(lhsFreeUpperBound))
|
||||||
|
lhsType = lhsFreeUpperBound;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TypeId newUpperBound = arena->addType(TableType{TableState::Free, TypeLevel{}, constraint->scope});
|
||||||
|
|
||||||
trackInteriorFreeType(constraint->scope, newUpperBound);
|
trackInteriorFreeType(constraint->scope, newUpperBound);
|
||||||
|
|
||||||
TableType* upperTable = getMutable<TableType>(newUpperBound);
|
TableType* upperTable = getMutable<TableType>(newUpperBound);
|
||||||
LUAU_ASSERT(upperTable);
|
LUAU_ASSERT(upperTable);
|
||||||
|
|
||||||
upperTable->props[c.propName] = rhsType;
|
upperTable->props[c.propName] = rhsType;
|
||||||
|
|
||||||
// Food for thought: Could we block if simplification encounters a blocked type?
|
// Food for thought: Could we block if simplification encounters a blocked type?
|
||||||
lhsFree->upperBound = simplifyIntersection(constraint->scope, constraint->location, lhsFreeUpperBound, newUpperBound);
|
lhsFree->upperBound = simplifyIntersection(constraint->scope, constraint->location, lhsFreeUpperBound, newUpperBound);
|
||||||
|
|
||||||
bind(constraint, c.propType, rhsType);
|
bind(constraint, c.propType, rhsType);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2873,8 +2909,21 @@ TablePropLookupResult ConstraintSolver::lookupTableProp(
|
||||||
{
|
{
|
||||||
const TypeId upperBound = follow(ft->upperBound);
|
const TypeId upperBound = follow(ft->upperBound);
|
||||||
|
|
||||||
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
return lookupTableProp(constraint, upperBound, propName, context, inConditional, suppressSimplification, seen);
|
{
|
||||||
|
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
||||||
|
{
|
||||||
|
TablePropLookupResult res = lookupTableProp(constraint, upperBound, propName, context, inConditional, suppressSimplification, seen);
|
||||||
|
// If the upper bound is a table that already has the property, we don't need to extend its bounds.
|
||||||
|
if (res.propType || get<PrimitiveType>(upperBound))
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (get<TableType>(upperBound) || get<PrimitiveType>(upperBound))
|
||||||
|
return lookupTableProp(constraint, upperBound, propName, context, inConditional, suppressSimplification, seen);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: The upper bound could be an intersection that contains suitable tables or extern types.
|
// TODO: The upper bound could be an intersection that contains suitable tables or extern types.
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ LUAU_FASTFLAGVARIABLE(LuauPreprocessTypestatedArgument)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackTrueReset)
|
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackTrueReset)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -501,12 +502,26 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
|
||||||
}
|
}
|
||||||
|
|
||||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||||
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
|
if (FFlag::LuauDfgIfBlocksShouldRespectControlFlow)
|
||||||
join(scope, scope, elseScope);
|
{
|
||||||
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
|
// If the control flow from the `if` or `else` block is non-linear,
|
||||||
join(scope, thenScope, scope);
|
// then we should assume that the _other_ branch is the one taken.
|
||||||
else if ((thencf | elsecf) == ControlFlow::None)
|
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
|
||||||
join(scope, thenScope, elseScope);
|
scope->inherit(elseScope);
|
||||||
|
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
|
||||||
|
scope->inherit(thenScope);
|
||||||
|
else if ((thencf | elsecf) == ControlFlow::None)
|
||||||
|
join(scope, thenScope, elseScope);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (thencf != ControlFlow::None && elsecf == ControlFlow::None)
|
||||||
|
join(scope, scope, elseScope);
|
||||||
|
else if (thencf == ControlFlow::None && elsecf != ControlFlow::None)
|
||||||
|
join(scope, thenScope, scope);
|
||||||
|
else if ((thencf | elsecf) == ControlFlow::None)
|
||||||
|
join(scope, thenScope, elseScope);
|
||||||
|
}
|
||||||
|
|
||||||
if (thencf == elsecf)
|
if (thencf == elsecf)
|
||||||
return thencf;
|
return thencf;
|
||||||
|
@ -1164,7 +1179,7 @@ DefId DataFlowGraphBuilder::visitLValue(AstExprLocal* l, DefId incomingDef)
|
||||||
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
DfgScope* scope = FFlag::LuauDfgScopeStackNotNull ? currentScope() : currentScope_DEPRECATED();
|
||||||
|
|
||||||
// In order to avoid alias tracking, we need to clip the reference to the parent def.
|
// In order to avoid alias tracking, we need to clip the reference to the parent def.
|
||||||
if (scope->canUpdateDefinition(l->local))
|
if (scope->canUpdateDefinition(l->local) && !(FFlag::LuauDoNotAddUpvalueTypesToLocalType && l->upvalue))
|
||||||
{
|
{
|
||||||
DefId updated = defArena->freshCell(l->local, l->location, containsSubscriptedDefinition(incomingDef));
|
DefId updated = defArena->freshCell(l->local, l->location, containsSubscriptedDefinition(incomingDef));
|
||||||
scope->bindings[l->local] = updated;
|
scope->bindings[l->local] = updated;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauDeclareExternType)
|
LUAU_FASTFLAG(LuauDeclareExternType)
|
||||||
|
LUAU_FASTFLAG(LuauTypeFunOptional)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -339,11 +340,11 @@ std::string getBuiltinDefinitionSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: split into separate tagged unions when the new solver can appropriately handle that.
|
// TODO: split into separate tagged unions when the new solver can appropriately handle that.
|
||||||
static const std::string kBuiltinDefinitionTypesSrc = R"BUILTIN_SRC(
|
static const std::string kBuiltinDefinitionTypeMethodSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
export type type = {
|
export type type = {
|
||||||
tag: "nil" | "unknown" | "never" | "any" | "boolean" | "number" | "string" | "buffer" | "thread" |
|
tag: "nil" | "unknown" | "never" | "any" | "boolean" | "number" | "string" | "buffer" | "thread" |
|
||||||
"singleton" | "negation" | "union" | "intesection" | "table" | "function" | "class" | "generic",
|
"singleton" | "negation" | "union" | "intersection" | "table" | "function" | "class" | "generic",
|
||||||
|
|
||||||
is: (self: type, arg: string) -> boolean,
|
is: (self: type, arg: string) -> boolean,
|
||||||
|
|
||||||
|
@ -390,6 +391,10 @@ export type type = {
|
||||||
ispack: (self: type) -> boolean,
|
ispack: (self: type) -> boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionTypesLibSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
declare types: {
|
declare types: {
|
||||||
unknown: type,
|
unknown: type,
|
||||||
never: type,
|
never: type,
|
||||||
|
@ -409,12 +414,44 @@ declare types: {
|
||||||
newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
|
newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
|
||||||
copy: @checked (arg: type) -> type,
|
copy: @checked (arg: type) -> type,
|
||||||
}
|
}
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionTypesLibWithOptionalSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
declare types: {
|
||||||
|
unknown: type,
|
||||||
|
never: type,
|
||||||
|
any: type,
|
||||||
|
boolean: type,
|
||||||
|
number: type,
|
||||||
|
string: type,
|
||||||
|
thread: type,
|
||||||
|
buffer: type,
|
||||||
|
|
||||||
|
singleton: @checked (arg: string | boolean | nil) -> type,
|
||||||
|
optional: @checked (arg: type) -> type,
|
||||||
|
generic: @checked (name: string, ispack: boolean?) -> type,
|
||||||
|
negationof: @checked (arg: type) -> type,
|
||||||
|
unionof: @checked (...type) -> type,
|
||||||
|
intersectionof: @checked (...type) -> type,
|
||||||
|
newtable: @checked (props: {[type]: type} | {[type]: { read: type, write: type } } | nil, indexer: { index: type, readresult: type, writeresult: type }?, metatable: type?) -> type,
|
||||||
|
newfunction: @checked (parameters: { head: {type}?, tail: type? }?, returns: { head: {type}?, tail: type? }?, generics: {type}?) -> type,
|
||||||
|
copy: @checked (arg: type) -> type,
|
||||||
|
}
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
|
||||||
std::string getTypeFunctionDefinitionSource()
|
std::string getTypeFunctionDefinitionSource()
|
||||||
{
|
{
|
||||||
return kBuiltinDefinitionTypesSrc;
|
|
||||||
|
std::string result = kBuiltinDefinitionTypeMethodSrc;
|
||||||
|
|
||||||
|
if (FFlag::LuauTypeFunOptional)
|
||||||
|
result += kBuiltinDefinitionTypesLibWithOptionalSrc;
|
||||||
|
else
|
||||||
|
result += kBuiltinDefinitionTypesLibSrc;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -28,8 +28,6 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||||
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
LUAU_FASTINT(LuauTypeInferIterationLimit);
|
||||||
LUAU_FASTINT(LuauTarjanChildLimit)
|
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMixedModeDefFinderTraversesTypeOf)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCloneIncrementalModule)
|
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete)
|
LUAU_FASTFLAGVARIABLE(DebugLogFragmentsFromAutocomplete)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
|
LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
||||||
|
@ -737,7 +735,7 @@ struct MixedModeIncrementalTCDefFinder : public AstVisitor
|
||||||
// requires that we find the local/global `m` and place it in the environment.
|
// requires that we find the local/global `m` and place it in the environment.
|
||||||
// The default behaviour here is to return false, and have individual visitors override
|
// The default behaviour here is to return false, and have individual visitors override
|
||||||
// the specific behaviour they need.
|
// the specific behaviour they need.
|
||||||
return FFlag::LuauMixedModeDefFinderTraversesTypeOf;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(AstStatTypeAlias* alias) override
|
bool visit(AstStatTypeAlias* alias) override
|
||||||
|
@ -1227,31 +1225,6 @@ ModulePtr cloneModule(CloneState& cloneState, const ModulePtr& source, std::uniq
|
||||||
return incremental;
|
return incremental;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModulePtr copyModule(const ModulePtr& result, std::unique_ptr<Allocator> alloc)
|
|
||||||
{
|
|
||||||
ModulePtr incrementalModule = std::make_shared<Module>();
|
|
||||||
incrementalModule->name = result->name;
|
|
||||||
incrementalModule->humanReadableName = "Incremental$" + result->humanReadableName;
|
|
||||||
incrementalModule->internalTypes.owningModule = incrementalModule.get();
|
|
||||||
incrementalModule->interfaceTypes.owningModule = incrementalModule.get();
|
|
||||||
incrementalModule->allocator = std::move(alloc);
|
|
||||||
// Don't need to keep this alive (it's already on the source module)
|
|
||||||
copyModuleVec(incrementalModule->scopes, result->scopes);
|
|
||||||
copyModuleMap(incrementalModule->astTypes, result->astTypes);
|
|
||||||
copyModuleMap(incrementalModule->astTypePacks, result->astTypePacks);
|
|
||||||
copyModuleMap(incrementalModule->astExpectedTypes, result->astExpectedTypes);
|
|
||||||
// Don't need to clone astOriginalCallTypes
|
|
||||||
copyModuleMap(incrementalModule->astOverloadResolvedTypes, result->astOverloadResolvedTypes);
|
|
||||||
// Don't need to clone astForInNextTypes
|
|
||||||
copyModuleMap(incrementalModule->astForInNextTypes, result->astForInNextTypes);
|
|
||||||
// Don't need to clone astResolvedTypes
|
|
||||||
// Don't need to clone astResolvedTypePacks
|
|
||||||
// Don't need to clone upperBoundContributors
|
|
||||||
copyModuleMap(incrementalModule->astScopes, result->astScopes);
|
|
||||||
// Don't need to clone declared Globals;
|
|
||||||
return incrementalModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mixedModeCompatibility(
|
void mixedModeCompatibility(
|
||||||
const ScopePtr& bottomScopeStale,
|
const ScopePtr& bottomScopeStale,
|
||||||
const ScopePtr& myFakeScope,
|
const ScopePtr& myFakeScope,
|
||||||
|
@ -1315,10 +1288,8 @@ FragmentTypeCheckResult typecheckFragmentHelper_DEPRECATED(
|
||||||
ModulePtr incrementalModule = nullptr;
|
ModulePtr incrementalModule = nullptr;
|
||||||
if (FFlag::LuauAllFreeTypesHaveScopes)
|
if (FFlag::LuauAllFreeTypesHaveScopes)
|
||||||
incrementalModule = cloneModule(cloneState, stale, std::move(astAllocator), freshChildOfNearestScope.get());
|
incrementalModule = cloneModule(cloneState, stale, std::move(astAllocator), freshChildOfNearestScope.get());
|
||||||
else if (FFlag::LuauCloneIncrementalModule)
|
|
||||||
incrementalModule = cloneModule_DEPRECATED(cloneState, stale, std::move(astAllocator));
|
|
||||||
else
|
else
|
||||||
incrementalModule = copyModule(stale, std::move(astAllocator));
|
incrementalModule = cloneModule_DEPRECATED(cloneState, stale, std::move(astAllocator));
|
||||||
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneModuleEnd);
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneModuleEnd);
|
||||||
incrementalModule->checkedInNewSolver = true;
|
incrementalModule->checkedInNewSolver = true;
|
||||||
|
@ -1372,44 +1343,27 @@ FragmentTypeCheckResult typecheckFragmentHelper_DEPRECATED(
|
||||||
};
|
};
|
||||||
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeStart);
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeStart);
|
||||||
if (FFlag::LuauCloneIncrementalModule)
|
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
||||||
{
|
cg.rootScope = freshChildOfNearestScope.get();
|
||||||
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
|
||||||
cg.rootScope = freshChildOfNearestScope.get();
|
|
||||||
|
|
||||||
if (FFlag::LuauAllFreeTypesHaveScopes)
|
if (FFlag::LuauAllFreeTypesHaveScopes)
|
||||||
cloneAndSquashScopes(
|
cloneAndSquashScopes(
|
||||||
cloneState, closestScope.get(), stale, NotNull{&incrementalModule->internalTypes}, NotNull{&dfg}, root, freshChildOfNearestScope.get()
|
cloneState, closestScope.get(), stale, NotNull{&incrementalModule->internalTypes}, NotNull{&dfg}, root, freshChildOfNearestScope.get()
|
||||||
);
|
);
|
||||||
else
|
|
||||||
cloneAndSquashScopes_DEPRECATED(
|
|
||||||
cloneState, closestScope.get(), stale, NotNull{&incrementalModule->internalTypes}, NotNull{&dfg}, root, freshChildOfNearestScope.get()
|
|
||||||
);
|
|
||||||
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeEnd);
|
|
||||||
cg.visitFragmentRoot(freshChildOfNearestScope, root);
|
|
||||||
|
|
||||||
if (FFlag::LuauPersistConstraintGenerationScopes)
|
|
||||||
{
|
|
||||||
for (auto p : cg.scopes)
|
|
||||||
incrementalModule->scopes.emplace_back(std::move(p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
cloneAndSquashScopes_DEPRECATED(
|
||||||
|
cloneState, closestScope.get(), stale, NotNull{&incrementalModule->internalTypes}, NotNull{&dfg}, root, freshChildOfNearestScope.get()
|
||||||
|
);
|
||||||
|
|
||||||
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeEnd);
|
||||||
|
cg.visitFragmentRoot(freshChildOfNearestScope, root);
|
||||||
|
|
||||||
|
if (FFlag::LuauPersistConstraintGenerationScopes)
|
||||||
{
|
{
|
||||||
// Any additions to the scope must occur in a fresh scope
|
for (auto p : cg.scopes)
|
||||||
cg.rootScope = stale->getModuleScope().get();
|
incrementalModule->scopes.emplace_back(std::move(p));
|
||||||
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
|
||||||
mixedModeCompatibility(closestScope, freshChildOfNearestScope, stale, NotNull{&dfg}, root);
|
|
||||||
// closest Scope -> children = { ...., freshChildOfNearestScope}
|
|
||||||
// We need to trim nearestChild from the scope hierarchy
|
|
||||||
closestScope->children.emplace_back(freshChildOfNearestScope.get());
|
|
||||||
cg.visitFragmentRoot(freshChildOfNearestScope, root);
|
|
||||||
// Trim nearestChild from the closestScope
|
|
||||||
Scope* back = closestScope->children.back().get();
|
|
||||||
LUAU_ASSERT(back == freshChildOfNearestScope.get());
|
|
||||||
closestScope->children.pop_back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverStart);
|
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverStart);
|
||||||
|
|
||||||
if (FFlag::LuauAllFreeTypesHaveScopes)
|
if (FFlag::LuauAllFreeTypesHaveScopes)
|
||||||
|
|
|
@ -1403,7 +1403,10 @@ std::optional<TypeId> generalize(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TypeId unsealedTableTy : fts.unsealedTables)
|
for (TypeId unsealedTableTy : fts.unsealedTables)
|
||||||
sealTable(scope, unsealedTableTy);
|
{
|
||||||
|
if (!generalizationTarget || unsealedTableTy == *generalizationTarget)
|
||||||
|
sealTable(scope, unsealedTableTy);
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& [freePackId, params] : fts.typePacks)
|
for (const auto& [freePackId, params] : fts.typePacks)
|
||||||
{
|
{
|
||||||
|
@ -1463,29 +1466,93 @@ std::optional<TypeId> generalize(
|
||||||
|
|
||||||
struct GenericCounter : TypeVisitor
|
struct GenericCounter : TypeVisitor
|
||||||
{
|
{
|
||||||
|
struct CounterState
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
Polarity polarity = Polarity::None;
|
||||||
|
};
|
||||||
|
|
||||||
NotNull<DenseHashSet<TypeId>> cachedTypes;
|
NotNull<DenseHashSet<TypeId>> cachedTypes;
|
||||||
DenseHashMap<TypeId, size_t> generics{nullptr};
|
DenseHashMap<TypeId, CounterState> generics{nullptr};
|
||||||
DenseHashMap<TypePackId, size_t> genericPacks{nullptr};
|
DenseHashMap<TypePackId, CounterState> genericPacks{nullptr};
|
||||||
|
|
||||||
|
Polarity polarity = Polarity::Positive;
|
||||||
|
|
||||||
explicit GenericCounter(NotNull<DenseHashSet<TypeId>> cachedTypes)
|
explicit GenericCounter(NotNull<DenseHashSet<TypeId>> cachedTypes)
|
||||||
: cachedTypes(cachedTypes)
|
: cachedTypes(cachedTypes)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const FunctionType& ft) override
|
||||||
|
{
|
||||||
|
if (ty->persistent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
polarity = invert(polarity);
|
||||||
|
traverse(ft.argTypes);
|
||||||
|
polarity = invert(polarity);
|
||||||
|
traverse(ft.retTypes);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const TableType& tt) override
|
||||||
|
{
|
||||||
|
if (ty->persistent)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Polarity previous = polarity;
|
||||||
|
|
||||||
|
for (const auto& [_name, prop] : tt.props)
|
||||||
|
{
|
||||||
|
if (prop.isReadOnly())
|
||||||
|
traverse(*prop.readTy);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(prop.isShared());
|
||||||
|
|
||||||
|
polarity = Polarity::Mixed;
|
||||||
|
traverse(prop.type());
|
||||||
|
polarity = previous;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tt.indexer)
|
||||||
|
{
|
||||||
|
polarity = Polarity::Mixed;
|
||||||
|
traverse(tt.indexer->indexType);
|
||||||
|
traverse(tt.indexer->indexResultType);
|
||||||
|
polarity = previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const ExternType&) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const GenericType&) override
|
bool visit(TypeId ty, const GenericType&) override
|
||||||
{
|
{
|
||||||
size_t* count = generics.find(ty);
|
auto state = generics.find(ty);
|
||||||
if (count)
|
if (state)
|
||||||
++*count;
|
{
|
||||||
|
++state->count;
|
||||||
|
state->polarity |= polarity;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypePackId tp, const GenericTypePack&) override
|
bool visit(TypePackId tp, const GenericTypePack&) override
|
||||||
{
|
{
|
||||||
size_t* count = genericPacks.find(tp);
|
auto state = genericPacks.find(tp);
|
||||||
if (count)
|
if (state)
|
||||||
++*count;
|
{
|
||||||
|
++state->count;
|
||||||
|
state->polarity |= polarity;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1521,21 +1588,30 @@ void pruneUnnecessaryGenerics(
|
||||||
generic = follow(generic);
|
generic = follow(generic);
|
||||||
auto g = get<GenericType>(generic);
|
auto g = get<GenericType>(generic);
|
||||||
if (g && !g->explicitName)
|
if (g && !g->explicitName)
|
||||||
counter.generics[generic] = 0;
|
counter.generics[generic] = {};
|
||||||
}
|
}
|
||||||
for (TypePackId genericPack : functionTy->genericPacks)
|
|
||||||
|
// It is sometimes the case that a pack in the generic list will become a
|
||||||
|
// pack that (transitively) has a generic tail. If it does, we need to add
|
||||||
|
// that generic tail to the generic pack list.
|
||||||
|
for (size_t i = 0; i < functionTy->genericPacks.size(); ++i)
|
||||||
{
|
{
|
||||||
genericPack = follow(genericPack);
|
TypePackId genericPack = follow(functionTy->genericPacks[i]);
|
||||||
auto g = get<GenericTypePack>(genericPack);
|
|
||||||
if (g && !g->explicitName)
|
TypePackId tail = getTail(genericPack);
|
||||||
counter.genericPacks[genericPack] = 0;
|
|
||||||
|
if (tail != genericPack)
|
||||||
|
functionTy->genericPacks.push_back(tail);
|
||||||
|
|
||||||
|
if (auto g = get<GenericTypePack>(tail); g && !g->explicitName)
|
||||||
|
counter.genericPacks[genericPack] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
counter.traverse(ty);
|
counter.traverse(ty);
|
||||||
|
|
||||||
for (const auto& [generic, count] : counter.generics)
|
for (const auto& [generic, state] : counter.generics)
|
||||||
{
|
{
|
||||||
if (count == 1)
|
if (state.count == 1 && state.polarity != Polarity::Mixed)
|
||||||
emplaceType<BoundType>(asMutable(generic), builtinTypes->unknownType);
|
emplaceType<BoundType>(asMutable(generic), builtinTypes->unknownType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1557,9 +1633,9 @@ void pruneUnnecessaryGenerics(
|
||||||
|
|
||||||
functionTy->generics.erase(it, functionTy->generics.end());
|
functionTy->generics.erase(it, functionTy->generics.end());
|
||||||
|
|
||||||
for (const auto& [genericPack, count] : counter.genericPacks)
|
for (const auto& [genericPack, state] : counter.genericPacks)
|
||||||
{
|
{
|
||||||
if (count == 1)
|
if (state.count == 1)
|
||||||
emplaceTypePack<BoundTypePack>(asMutable(genericPack), builtinTypes->unknownTypePack);
|
emplaceTypePack<BoundTypePack>(asMutable(genericPack), builtinTypes->unknownTypePack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -164,7 +163,7 @@ TypeId ReplaceGenerics::clean(TypeId ty)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return FFlag::LuauFreeTypesMustHaveBounds ? arena->freshType(builtinTypes, scope, level) : addType(FreeType{scope, level});
|
return arena->freshType(builtinTypes, scope, level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,9 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
LUAU_FASTFLAGVARIABLE(LuauNonStrictVisitorImprovements)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes)
|
LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
|
@ -218,7 +217,7 @@ struct NonStrictTypeChecker
|
||||||
return *fst;
|
return *fst;
|
||||||
else if (auto ftp = get<FreeTypePack>(pack))
|
else if (auto ftp = get<FreeTypePack>(pack))
|
||||||
{
|
{
|
||||||
TypeId result = FFlag::LuauFreeTypesMustHaveBounds ? arena->freshType(builtinTypes, ftp->scope) : arena->addType(FreeType{ftp->scope});
|
TypeId result = arena->freshType(builtinTypes, ftp->scope);
|
||||||
TypePackId freeTail = arena->addTypePack(FreeTypePack{ftp->scope});
|
TypePackId freeTail = arena->addTypePack(FreeTypePack{ftp->scope});
|
||||||
|
|
||||||
TypePack* resultPack = emplaceTypePack<TypePack>(asMutable(pack));
|
TypePack* resultPack = emplaceTypePack<TypePack>(asMutable(pack));
|
||||||
|
@ -341,11 +340,8 @@ struct NonStrictTypeChecker
|
||||||
{
|
{
|
||||||
ctx.remove(dfg->getDef(local));
|
ctx.remove(dfg->getDef(local));
|
||||||
|
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
{
|
visit(local->annotation);
|
||||||
if (local->annotation)
|
|
||||||
visit(local->annotation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -431,9 +427,8 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatFor* forStatement)
|
NonStrictContext visit(AstStatFor* forStatement)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
if (forStatement->var->annotation)
|
visit(forStatement->var->annotation);
|
||||||
visit(forStatement->var->annotation);
|
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||||
{
|
{
|
||||||
|
@ -454,13 +449,10 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatForIn* forInStatement)
|
NonStrictContext visit(AstStatForIn* forInStatement)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
{
|
{
|
||||||
for (auto var : forInStatement->vars)
|
for (auto var : forInStatement->vars)
|
||||||
{
|
visit(var->annotation);
|
||||||
if (var->annotation)
|
|
||||||
visit(var->annotation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||||
|
@ -511,7 +503,7 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatTypeAlias* typeAlias)
|
NonStrictContext visit(AstStatTypeAlias* typeAlias)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
{
|
{
|
||||||
visitGenerics(typeAlias->generics, typeAlias->genericPacks);
|
visitGenerics(typeAlias->generics, typeAlias->genericPacks);
|
||||||
visit(typeAlias->type);
|
visit(typeAlias->type);
|
||||||
|
@ -527,7 +519,7 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatDeclareFunction* declFn)
|
NonStrictContext visit(AstStatDeclareFunction* declFn)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
{
|
{
|
||||||
visitGenerics(declFn->generics, declFn->genericPacks);
|
visitGenerics(declFn->generics, declFn->genericPacks);
|
||||||
visit(declFn->params);
|
visit(declFn->params);
|
||||||
|
@ -539,7 +531,7 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatDeclareGlobal* declGlobal)
|
NonStrictContext visit(AstStatDeclareGlobal* declGlobal)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
visit(declGlobal->type);
|
visit(declGlobal->type);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -547,7 +539,7 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstStatDeclareExternType* declClass)
|
NonStrictContext visit(AstStatDeclareExternType* declClass)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
{
|
{
|
||||||
if (declClass->indexer)
|
if (declClass->indexer)
|
||||||
{
|
{
|
||||||
|
@ -823,22 +815,16 @@ struct NonStrictTypeChecker
|
||||||
}
|
}
|
||||||
remainder.remove(dfg->getDef(local));
|
remainder.remove(dfg->getDef(local));
|
||||||
|
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
{
|
visit(local->annotation);
|
||||||
if (local->annotation)
|
|
||||||
visit(local->annotation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
{
|
{
|
||||||
visitGenerics(exprFn->generics, exprFn->genericPacks);
|
visitGenerics(exprFn->generics, exprFn->genericPacks);
|
||||||
|
|
||||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
{
|
visit(exprFn->returnAnnotation);
|
||||||
if (exprFn->returnAnnotation)
|
|
||||||
visit(exprFn->returnAnnotation);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (exprFn->returnAnnotation_DEPRECATED)
|
if (exprFn->returnAnnotation_DEPRECATED)
|
||||||
|
@ -889,7 +875,7 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
|
NonStrictContext visit(AstExprTypeAssertion* typeAssertion)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
visit(typeAssertion->annotation);
|
visit(typeAssertion->annotation);
|
||||||
|
|
||||||
if (FFlag::LuauNonStrictVisitorImprovements)
|
if (FFlag::LuauNonStrictVisitorImprovements)
|
||||||
|
@ -930,7 +916,11 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
void visit(AstType* ty)
|
void visit(AstType* ty)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes);
|
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes2);
|
||||||
|
|
||||||
|
// If this node is `nullptr`, early exit.
|
||||||
|
if (!ty)
|
||||||
|
return;
|
||||||
|
|
||||||
if (auto t = ty->as<AstTypeReference>())
|
if (auto t = ty->as<AstTypeReference>())
|
||||||
return visit(t);
|
return visit(t);
|
||||||
|
@ -1139,7 +1129,11 @@ struct NonStrictTypeChecker
|
||||||
|
|
||||||
void visit(AstTypePack* pack)
|
void visit(AstTypePack* pack)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes);
|
LUAU_ASSERT(FFlag::LuauNewNonStrictVisitTypes2);
|
||||||
|
|
||||||
|
// If there is no pack node, early exit.
|
||||||
|
if (!pack)
|
||||||
|
return;
|
||||||
|
|
||||||
if (auto p = pack->as<AstTypePackExplicit>())
|
if (auto p = pack->as<AstTypePackExplicit>())
|
||||||
return visit(p);
|
return visit(p);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/RecursionCounter.h"
|
#include "Luau/RecursionCounter.h"
|
||||||
#include "Luau/Set.h"
|
#include "Luau/Set.h"
|
||||||
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
#include "Luau/TypePairHash.h"
|
#include "Luau/TypePairHash.h"
|
||||||
#include "Luau/TypeUtils.h"
|
#include "Luau/TypeUtils.h"
|
||||||
|
@ -17,6 +18,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8)
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSimplificationRecheckAssumption)
|
LUAU_FASTFLAGVARIABLE(LuauSimplificationRecheckAssumption)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAGVARIABLE(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauSimplificationTableExternType)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -316,12 +318,14 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
{
|
{
|
||||||
if (get<AnyType>(right))
|
if (get<AnyType>(right))
|
||||||
return Relation::Subset;
|
return Relation::Subset;
|
||||||
else if (get<UnknownType>(right))
|
|
||||||
|
if (get<UnknownType>(right))
|
||||||
return Relation::Coincident;
|
return Relation::Coincident;
|
||||||
else if (get<ErrorType>(right))
|
|
||||||
|
if (get<ErrorType>(right))
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
else
|
|
||||||
return Relation::Superset;
|
return Relation::Superset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get<UnknownType>(right))
|
if (get<UnknownType>(right))
|
||||||
|
@ -331,8 +335,8 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
{
|
{
|
||||||
if (get<AnyType>(right))
|
if (get<AnyType>(right))
|
||||||
return Relation::Coincident;
|
return Relation::Coincident;
|
||||||
else
|
|
||||||
return Relation::Superset;
|
return Relation::Superset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get<AnyType>(right))
|
if (get<AnyType>(right))
|
||||||
|
@ -364,26 +368,33 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
if (isTypeVariable(left) || isTypeVariable(right))
|
if (isTypeVariable(left) || isTypeVariable(right))
|
||||||
return Relation::Intersects;
|
return Relation::Intersects;
|
||||||
|
|
||||||
|
if (FFlag::LuauSimplificationTableExternType)
|
||||||
|
{
|
||||||
|
// if either type is a type function, we cannot know if they'll be related.
|
||||||
|
if (get<TypeFunctionInstanceType>(left) || get<TypeFunctionInstanceType>(right))
|
||||||
|
return Relation::Intersects;
|
||||||
|
}
|
||||||
|
|
||||||
if (get<ErrorType>(left))
|
if (get<ErrorType>(left))
|
||||||
{
|
{
|
||||||
if (get<ErrorType>(right))
|
if (get<ErrorType>(right))
|
||||||
return Relation::Coincident;
|
return Relation::Coincident;
|
||||||
else if (get<AnyType>(right))
|
else if (get<AnyType>(right))
|
||||||
return Relation::Subset;
|
return Relation::Subset;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
if (get<ErrorType>(right))
|
else if (get<ErrorType>(right))
|
||||||
return flip(relate(right, left, seen));
|
return flip(relate(right, left, seen));
|
||||||
|
|
||||||
if (get<NeverType>(left))
|
if (get<NeverType>(left))
|
||||||
{
|
{
|
||||||
if (get<NeverType>(right))
|
if (get<NeverType>(right))
|
||||||
return Relation::Coincident;
|
return Relation::Coincident;
|
||||||
else
|
|
||||||
return Relation::Subset;
|
return Relation::Subset;
|
||||||
}
|
}
|
||||||
if (get<NeverType>(right))
|
else if (get<NeverType>(right))
|
||||||
return flip(relate(right, left, seen));
|
return flip(relate(right, left, seen));
|
||||||
|
|
||||||
if (auto ut = get<IntersectionType>(left))
|
if (auto ut = get<IntersectionType>(left))
|
||||||
|
@ -447,33 +458,34 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
{
|
{
|
||||||
if (lp->type == rp->type)
|
if (lp->type == rp->type)
|
||||||
return Relation::Coincident;
|
return Relation::Coincident;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto rs = get<SingletonType>(right))
|
if (auto rs = get<SingletonType>(right))
|
||||||
{
|
{
|
||||||
if (lp->type == PrimitiveType::String && rs->variant.get_if<StringSingleton>())
|
if (lp->type == PrimitiveType::String && rs->variant.get_if<StringSingleton>())
|
||||||
return Relation::Superset;
|
return Relation::Superset;
|
||||||
else if (lp->type == PrimitiveType::Boolean && rs->variant.get_if<BooleanSingleton>())
|
|
||||||
|
if (lp->type == PrimitiveType::Boolean && rs->variant.get_if<BooleanSingleton>())
|
||||||
return Relation::Superset;
|
return Relation::Superset;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lp->type == PrimitiveType::Function)
|
if (lp->type == PrimitiveType::Function)
|
||||||
{
|
{
|
||||||
if (get<FunctionType>(right))
|
if (get<FunctionType>(right))
|
||||||
return Relation::Superset;
|
return Relation::Superset;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
if (lp->type == PrimitiveType::Table)
|
if (lp->type == PrimitiveType::Table)
|
||||||
{
|
{
|
||||||
if (get<TableType>(right))
|
if (get<TableType>(right))
|
||||||
return Relation::Superset;
|
return Relation::Superset;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ExternType>(right))
|
if (get<FunctionType>(right) || get<TableType>(right) || get<MetatableType>(right) || get<ExternType>(right))
|
||||||
|
@ -487,12 +499,13 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
|
|
||||||
if (get<PrimitiveType>(right))
|
if (get<PrimitiveType>(right))
|
||||||
return flip(relate(right, left, seen));
|
return flip(relate(right, left, seen));
|
||||||
|
|
||||||
if (auto rs = get<SingletonType>(right))
|
if (auto rs = get<SingletonType>(right))
|
||||||
{
|
{
|
||||||
if (ls->variant == rs->variant)
|
if (ls->variant == rs->variant)
|
||||||
return Relation::Coincident;
|
return Relation::Coincident;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,11 +515,11 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
{
|
{
|
||||||
if (rp->type == PrimitiveType::Function)
|
if (rp->type == PrimitiveType::Function)
|
||||||
return Relation::Subset;
|
return Relation::Subset;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return Relation::Intersects;
|
return Relation::Intersects;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto lt = get<TableType>(left))
|
if (auto lt = get<TableType>(left))
|
||||||
|
@ -515,10 +528,11 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
{
|
{
|
||||||
if (rp->type == PrimitiveType::Table)
|
if (rp->type == PrimitiveType::Table)
|
||||||
return Relation::Subset;
|
return Relation::Subset;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
else if (auto rt = get<TableType>(right))
|
|
||||||
|
if (auto rt = get<TableType>(right))
|
||||||
{
|
{
|
||||||
// TODO PROBABLY indexers and metatables.
|
// TODO PROBABLY indexers and metatables.
|
||||||
if (1 == rt->props.size())
|
if (1 == rt->props.size())
|
||||||
|
@ -538,14 +552,42 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
*/
|
*/
|
||||||
if (lt->props.size() > 1 && r == Relation::Superset)
|
if (lt->props.size() > 1 && r == Relation::Superset)
|
||||||
return Relation::Intersects;
|
return Relation::Intersects;
|
||||||
else
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
else if (1 == lt->props.size())
|
|
||||||
|
if (1 == lt->props.size())
|
||||||
return flip(relate(right, left, seen));
|
return flip(relate(right, left, seen));
|
||||||
else
|
|
||||||
return Relation::Intersects;
|
return Relation::Intersects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauSimplificationTableExternType)
|
||||||
|
{
|
||||||
|
if (auto re = get<ExternType>(right))
|
||||||
|
{
|
||||||
|
Relation overall = Relation::Coincident;
|
||||||
|
|
||||||
|
for (auto& [name, prop] : lt->props)
|
||||||
|
{
|
||||||
|
if (auto propInExternType = re->props.find(name); propInExternType != re->props.end())
|
||||||
|
{
|
||||||
|
Relation propRel = relate(prop.type(), propInExternType->second.type());
|
||||||
|
|
||||||
|
if (propRel == Relation::Disjoint)
|
||||||
|
return Relation::Disjoint;
|
||||||
|
|
||||||
|
if (propRel == Relation::Coincident)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
overall = Relation::Intersects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return overall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO metatables
|
// TODO metatables
|
||||||
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
|
@ -557,10 +599,11 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen)
|
||||||
{
|
{
|
||||||
if (isSubclass(ct, rct))
|
if (isSubclass(ct, rct))
|
||||||
return Relation::Subset;
|
return Relation::Subset;
|
||||||
else if (isSubclass(rct, ct))
|
|
||||||
|
if (isSubclass(rct, ct))
|
||||||
return Relation::Superset;
|
return Relation::Superset;
|
||||||
else
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Relation::Disjoint;
|
return Relation::Disjoint;
|
||||||
|
|
|
@ -7,13 +7,11 @@
|
||||||
#include "Luau/Normalize.h"
|
#include "Luau/Normalize.h"
|
||||||
#include "Luau/RecursionCounter.h"
|
#include "Luau/RecursionCounter.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/StringUtils.h"
|
|
||||||
#include "Luau/Substitution.h"
|
#include "Luau/Substitution.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/TxnLog.h"
|
#include "Luau/TxnLog.h"
|
||||||
#include "Luau/Type.h"
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
#include "Luau/TypeCheckLimits.h"
|
|
||||||
#include "Luau/TypeFunction.h"
|
#include "Luau/TypeFunction.h"
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
#include "Luau/TypePath.h"
|
#include "Luau/TypePath.h"
|
||||||
|
@ -33,7 +31,7 @@ struct VarianceFlipper
|
||||||
Subtyping::Variance* variance;
|
Subtyping::Variance* variance;
|
||||||
Subtyping::Variance oldValue;
|
Subtyping::Variance oldValue;
|
||||||
|
|
||||||
VarianceFlipper(Subtyping::Variance* v)
|
explicit VarianceFlipper(Subtyping::Variance* v)
|
||||||
: variance(v)
|
: variance(v)
|
||||||
, oldValue(*v)
|
, oldValue(*v)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
|
||||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
@ -706,8 +705,6 @@ struct Printer_DEPRECATED
|
||||||
writer.keyword("do");
|
writer.keyword("do");
|
||||||
for (const auto& s : block->body)
|
for (const auto& s : block->body)
|
||||||
visualize(*s);
|
visualize(*s);
|
||||||
if (!FFlag::LuauFixDoBlockEndLocation)
|
|
||||||
writer.advance(block->location.end);
|
|
||||||
writeEnd(program.location);
|
writeEnd(program.location);
|
||||||
}
|
}
|
||||||
else if (const auto& a = program.as<AstStatIf>())
|
else if (const auto& a = program.as<AstStatIf>())
|
||||||
|
@ -2323,8 +2320,6 @@ struct Printer
|
||||||
{
|
{
|
||||||
const auto cstNode = lookupCstNode<CstExprFunction>(&func);
|
const auto cstNode = lookupCstNode<CstExprFunction>(&func);
|
||||||
|
|
||||||
// TODO(CLI-139347): need to handle return type (incl. parentheses of return type)
|
|
||||||
|
|
||||||
if (func.generics.size > 0 || func.genericPacks.size > 0)
|
if (func.generics.size > 0 || func.genericPacks.size > 0)
|
||||||
{
|
{
|
||||||
CommaSeparatorInserter comma(writer, cstNode ? cstNode->genericsCommaPositions.begin() : nullptr);
|
CommaSeparatorInserter comma(writer, cstNode ? cstNode->genericsCommaPositions.begin() : nullptr);
|
||||||
|
@ -2395,12 +2390,18 @@ struct Printer
|
||||||
if (cstNode)
|
if (cstNode)
|
||||||
advance(cstNode->returnSpecifierPosition);
|
advance(cstNode->returnSpecifierPosition);
|
||||||
writer.symbol(":");
|
writer.symbol(":");
|
||||||
writer.space();
|
|
||||||
|
|
||||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
{
|
||||||
|
if (!cstNode)
|
||||||
|
writer.space();
|
||||||
visualizeTypePackAnnotation(*func.returnAnnotation, false, false);
|
visualizeTypePackAnnotation(*func.returnAnnotation, false, false);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
writer.space();
|
||||||
visualizeTypeList(*func.returnAnnotation_DEPRECATED, false);
|
visualizeTypeList(*func.returnAnnotation_DEPRECATED, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visualizeBlock(*func.body);
|
visualizeBlock(*func.body);
|
||||||
|
|
|
@ -506,31 +506,6 @@ FreeType::FreeType(Scope* scope, TypeLevel level, TypeId lowerBound, TypeId uppe
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Old constructors
|
|
||||||
FreeType::FreeType(TypeLevel level)
|
|
||||||
: index(Unifiable::freshIndex())
|
|
||||||
, level(level)
|
|
||||||
, scope(nullptr)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauFreeTypesMustHaveBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeType::FreeType(Scope* scope)
|
|
||||||
: index(Unifiable::freshIndex())
|
|
||||||
, level{}
|
|
||||||
, scope(scope)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauFreeTypesMustHaveBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeType::FreeType(Scope* scope, TypeLevel level)
|
|
||||||
: index(Unifiable::freshIndex())
|
|
||||||
, level(level)
|
|
||||||
, scope(scope)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauFreeTypesMustHaveBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
GenericType::GenericType()
|
GenericType::GenericType()
|
||||||
: index(Unifiable::freshIndex())
|
: index(Unifiable::freshIndex())
|
||||||
, name("g" + std::to_string(index))
|
, name("g" + std::to_string(index))
|
||||||
|
|
|
@ -50,33 +50,6 @@ TypeId TypeArena::freshType(NotNull<BuiltinTypes> builtins, Scope* scope, TypeLe
|
||||||
return allocated;
|
return allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId TypeArena::freshType_DEPRECATED(TypeLevel level)
|
|
||||||
{
|
|
||||||
TypeId allocated = types.allocate(FreeType{level});
|
|
||||||
|
|
||||||
asMutable(allocated)->owningArena = this;
|
|
||||||
|
|
||||||
return allocated;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeId TypeArena::freshType_DEPRECATED(Scope* scope)
|
|
||||||
{
|
|
||||||
TypeId allocated = types.allocate(FreeType{scope});
|
|
||||||
|
|
||||||
asMutable(allocated)->owningArena = this;
|
|
||||||
|
|
||||||
return allocated;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeId TypeArena::freshType_DEPRECATED(Scope* scope, TypeLevel level)
|
|
||||||
{
|
|
||||||
TypeId allocated = types.allocate(FreeType{scope, level});
|
|
||||||
|
|
||||||
asMutable(allocated)->owningArena = this;
|
|
||||||
|
|
||||||
return allocated;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePackId TypeArena::freshTypePack(Scope* scope, Polarity polarity)
|
TypePackId TypeArena::freshTypePack(Scope* scope, Polarity polarity)
|
||||||
{
|
{
|
||||||
TypePackId allocated = typePacks.allocate(FreeTypePack{scope, polarity});
|
TypePackId allocated = typePacks.allocate(FreeTypePack{scope, polarity});
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
||||||
|
@ -2114,10 +2113,7 @@ TypeId TypeChecker2::visit(AstExprBinary* expr, AstNode* overrideKey)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
expectedRets = module->internalTypes.addTypePack(
|
expectedRets = module->internalTypes.addTypePack({module->internalTypes.freshType(builtinTypes, scope, TypeLevel{})});
|
||||||
{FFlag::LuauFreeTypesMustHaveBounds ? module->internalTypes.freshType(builtinTypes, scope, TypeLevel{})
|
|
||||||
: module->internalTypes.freshType_DEPRECATED(scope, TypeLevel{})}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId expectedTy = module->internalTypes.addType(FunctionType(expectedArgs, expectedRets));
|
TypeId expectedTy = module->internalTypes.addType(FunctionType(expectedArgs, expectedRets));
|
||||||
|
@ -2380,8 +2376,7 @@ TypeId TypeChecker2::flattenPack(TypePackId pack)
|
||||||
return *fst;
|
return *fst;
|
||||||
else if (auto ftp = get<FreeTypePack>(pack))
|
else if (auto ftp = get<FreeTypePack>(pack))
|
||||||
{
|
{
|
||||||
TypeId result = FFlag::LuauFreeTypesMustHaveBounds ? module->internalTypes.freshType(builtinTypes, ftp->scope)
|
TypeId result = module->internalTypes.freshType(builtinTypes, ftp->scope);
|
||||||
: module->internalTypes.addType(FreeType{ftp->scope});
|
|
||||||
TypePackId freeTail = module->internalTypes.addTypePack(FreeTypePack{ftp->scope});
|
TypePackId freeTail = module->internalTypes.addTypePack(FreeTypePack{ftp->scope});
|
||||||
|
|
||||||
TypePack* resultPack = emplaceTypePack<TypePack>(asMutable(pack));
|
TypePack* resultPack = emplaceTypePack<TypePack>(asMutable(pack));
|
||||||
|
|
|
@ -1839,10 +1839,20 @@ 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 (isPending(lhsTy, ctx->solver))
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
{
|
||||||
else if (isPending(rhsTy, ctx->solver))
|
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||||
|
else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy))
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isPending(lhsTy, ctx->solver))
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||||
|
else if (isPending(rhsTy, ctx->solver))
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
||||||
|
}
|
||||||
|
|
||||||
// Or evalutes to the LHS type if the LHS is truthy, and the RHS type if LHS is falsy.
|
// Or evalutes to the LHS type if the LHS is truthy, and the RHS type if LHS is falsy.
|
||||||
SimplifyResult filteredLhs = simplifyIntersection(ctx->builtins, ctx->arena, lhsTy, ctx->builtins->truthyType);
|
SimplifyResult filteredLhs = simplifyIntersection(ctx->builtins, ctx->arena, lhsTy, ctx->builtins->truthyType);
|
||||||
|
@ -1876,10 +1886,20 @@ 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 (isPending(lhsTy, ctx->solver))
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
{
|
||||||
else if (isPending(rhsTy, ctx->solver))
|
if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(lhsTy))
|
||||||
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||||
|
else if (is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(rhsTy))
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isPending(lhsTy, ctx->solver))
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {lhsTy}, {}};
|
||||||
|
else if (isPending(rhsTy, ctx->solver))
|
||||||
|
return {std::nullopt, Reduction::MaybeOk, {rhsTy}, {}};
|
||||||
|
}
|
||||||
|
|
||||||
// Algebra Reduction Rules for comparison type functions
|
// Algebra Reduction Rules for comparison type functions
|
||||||
// Note that comparing to never tells you nothing about the other operand
|
// Note that comparing to never tells you nothing about the other operand
|
||||||
|
@ -2240,8 +2260,12 @@ 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
|
||||||
|
? is<BlockedType, PendingExpansionType, TypeFunctionInstanceType>(targetTy)
|
||||||
|
: isPending(targetTy, ctx->solver);
|
||||||
|
|
||||||
// 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 (isPending(targetTy, ctx->solver))
|
if (targetIsPending)
|
||||||
return {std::nullopt, Reduction::MaybeOk, {targetTy}, {}};
|
return {std::nullopt, Reduction::MaybeOk, {targetTy}, {}};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2326,8 +2350,32 @@ 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 (!result.blockedTypes.empty())
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
{
|
||||||
|
// Simplification considers free and generic types to be
|
||||||
|
// 'blocking', but that's not suitable for refine<>.
|
||||||
|
//
|
||||||
|
// If we are only blocked on those types, we consider
|
||||||
|
// the simplification a success and reduce.
|
||||||
|
if (std::all_of(
|
||||||
|
begin(result.blockedTypes),
|
||||||
|
end(result.blockedTypes),
|
||||||
|
[](auto&& v)
|
||||||
|
{
|
||||||
|
return is<FreeType, GenericType>(follow(v));
|
||||||
|
}
|
||||||
|
))
|
||||||
|
{
|
||||||
|
return {result.result, {}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!result.blockedTypes.empty())
|
||||||
|
return {nullptr, {result.blockedTypes.begin(), result.blockedTypes.end()}};
|
||||||
|
}
|
||||||
return {result.result, {}};
|
return {result.result, {}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3527,7 +3575,7 @@ BuiltinTypeFunctions::BuiltinTypeFunctions()
|
||||||
, ltFunc{"lt", ltTypeFunction}
|
, ltFunc{"lt", ltTypeFunction}
|
||||||
, leFunc{"le", leTypeFunction}
|
, leFunc{"le", leTypeFunction}
|
||||||
, eqFunc{"eq", eqTypeFunction}
|
, eqFunc{"eq", eqTypeFunction}
|
||||||
, refineFunc{"refine", refineTypeFunction}
|
, refineFunc{"refine", refineTypeFunction, /*canReduceGenerics*/ FFlag::DebugLuauGreedyGeneralization}
|
||||||
, singletonFunc{"singleton", singletonTypeFunction}
|
, singletonFunc{"singleton", singletonTypeFunction}
|
||||||
, unionFunc{"union", unionTypeFunction}
|
, unionFunc{"union", unionTypeFunction}
|
||||||
, intersectFunc{"intersect", intersectTypeFunction}
|
, intersectFunc{"intersect", intersectTypeFunction}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
LUAU_DYNAMIC_FASTINT(LuauTypeFunctionSerdeIterationLimit)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents)
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunReadWriteParents)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTypeFunOptional)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -315,6 +316,38 @@ static int getSingletonValue(lua_State* L)
|
||||||
luaL_error(L, "type.value: can't call `value` method on `%s` type", getTag(L, self).c_str());
|
luaL_error(L, "type.value: can't call `value` method on `%s` type", getTag(L, self).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Luau: `types.optional(ty: type) -> type`
|
||||||
|
// Returns the type instance representing an optional version of `ty`.
|
||||||
|
// If `ty` is a union, this adds `nil` to the components of the union.
|
||||||
|
// Otherwise, makes a union of the two things.
|
||||||
|
static int createOptional(lua_State* L)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(FFlag::LuauTypeFunOptional);
|
||||||
|
|
||||||
|
int argumentCount = lua_gettop(L);
|
||||||
|
if (argumentCount != 1)
|
||||||
|
luaL_error(L, "types.optional: expected 1 argument, but got %d", argumentCount);
|
||||||
|
|
||||||
|
TypeFunctionTypeId argument = getTypeUserData(L, 1);
|
||||||
|
|
||||||
|
std::vector<TypeFunctionTypeId> components;
|
||||||
|
|
||||||
|
if (auto unionTy = get<TypeFunctionUnionType>(argument))
|
||||||
|
{
|
||||||
|
components.reserve(unionTy->components.size() + 1);
|
||||||
|
|
||||||
|
components.insert(components.begin(), unionTy->components.begin(), unionTy->components.end());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
components.emplace_back(argument);
|
||||||
|
|
||||||
|
components.emplace_back(allocateTypeFunctionType(L, TypeFunctionPrimitiveType(TypeFunctionPrimitiveType::NilType)));
|
||||||
|
|
||||||
|
allocTypeUserData(L, TypeFunctionUnionType{components});
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Luau: `types.unionof(...: type) -> type`
|
// Luau: `types.unionof(...: type) -> type`
|
||||||
// Returns the type instance representing union
|
// Returns the type instance representing union
|
||||||
static int createUnion(lua_State* L)
|
static int createUnion(lua_State* L)
|
||||||
|
@ -1524,6 +1557,7 @@ void registerTypesLibrary(lua_State* L)
|
||||||
{"copy", deepCopy},
|
{"copy", deepCopy},
|
||||||
{"generic", createGeneric},
|
{"generic", createGeneric},
|
||||||
|
|
||||||
|
{(FFlag::LuauTypeFunOptional) ? "optional" : nullptr, (FFlag::LuauTypeFunOptional) ? createOptional : nullptr},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
|
||||||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification)
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||||
|
@ -799,8 +798,7 @@ struct Demoter : Substitution
|
||||||
{
|
{
|
||||||
auto ftv = get<FreeType>(ty);
|
auto ftv = get<FreeType>(ty);
|
||||||
LUAU_ASSERT(ftv);
|
LUAU_ASSERT(ftv);
|
||||||
return FFlag::LuauFreeTypesMustHaveBounds ? arena->freshType(builtins, demotedLevel(ftv->level))
|
return arena->freshType(builtins, demotedLevel(ftv->level));
|
||||||
: addType(FreeType{demotedLevel(ftv->level)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId clean(TypePackId tp) override
|
TypePackId clean(TypePackId tp) override
|
||||||
|
@ -5410,8 +5408,7 @@ TypeId TypeChecker::freshType(const ScopePtr& scope)
|
||||||
|
|
||||||
TypeId TypeChecker::freshType(TypeLevel level)
|
TypeId TypeChecker::freshType(TypeLevel level)
|
||||||
{
|
{
|
||||||
return FFlag::LuauFreeTypesMustHaveBounds ? currentModule->internalTypes.freshType(builtinTypes, level)
|
return currentModule->internalTypes.freshType(builtinTypes, level);
|
||||||
: currentModule->internalTypes.addType(Type(FreeType(level)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId TypeChecker::singletonType(bool value)
|
TypeId TypeChecker::singletonType(bool value)
|
||||||
|
|
|
@ -208,6 +208,26 @@ TypePackIterator end(TypePackId tp)
|
||||||
return TypePackIterator{};
|
return TypePackIterator{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePackId getTail(TypePackId tp)
|
||||||
|
{
|
||||||
|
DenseHashSet<TypePackId> seen{nullptr};
|
||||||
|
while (tp)
|
||||||
|
{
|
||||||
|
tp = follow(tp);
|
||||||
|
|
||||||
|
if (seen.contains(tp))
|
||||||
|
break;
|
||||||
|
seen.insert(tp);
|
||||||
|
|
||||||
|
if (auto pack = get<TypePack>(tp); pack && pack->tail)
|
||||||
|
tp = *pack->tail;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return follow(tp);
|
||||||
|
}
|
||||||
|
|
||||||
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs)
|
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs)
|
||||||
{
|
{
|
||||||
TypePackId lhsId = const_cast<TypePackId>(&lhs);
|
TypePackId lhsId = const_cast<TypePackId>(&lhs);
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||||
|
|
||||||
|
@ -328,7 +327,7 @@ TypePack extendTypePack(
|
||||||
trackInteriorFreeType(ftp->scope, t);
|
trackInteriorFreeType(ftp->scope, t);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
t = FFlag::LuauFreeTypesMustHaveBounds ? arena.freshType(builtinTypes, ftp->scope) : arena.freshType_DEPRECATED(ftp->scope);
|
t = arena.freshType(builtinTypes, ftp->scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
newPack.head.push_back(t);
|
newPack.head.push_back(t);
|
||||||
|
|
|
@ -22,7 +22,6 @@ LUAU_FASTFLAGVARIABLE(LuauTransitiveSubtyping)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering)
|
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart)
|
LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart)
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1559,7 +1558,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
return freshType(NotNull{types}, builtinTypes, scope);
|
return freshType(NotNull{types}, builtinTypes, scope);
|
||||||
else
|
else
|
||||||
return FFlag::LuauFreeTypesMustHaveBounds ? types->freshType(builtinTypes, scope, level) : types->freshType_DEPRECATED(scope, level);
|
return types->freshType(builtinTypes, scope, level);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TypePackId emptyTp = types->addTypePack(TypePack{{}, std::nullopt});
|
const TypePackId emptyTp = types->addTypePack(TypePack{{}, std::nullopt});
|
||||||
|
|
|
@ -20,7 +20,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData2)
|
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup3)
|
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFixDoBlockEndLocation)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseOptionalAsNode2)
|
LUAU_FASTFLAGVARIABLE(LuauParseOptionalAsNode2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDeclareExternType)
|
LUAU_FASTFLAGVARIABLE(LuauDeclareExternType)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
|
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
|
||||||
|
@ -555,7 +554,7 @@ AstStat* Parser::parseDo()
|
||||||
|
|
||||||
Location endLocation = lexer.current().location;
|
Location endLocation = lexer.current().location;
|
||||||
body->hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
|
body->hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
|
||||||
if (FFlag::LuauFixDoBlockEndLocation && body->hasEnd)
|
if (body->hasEnd)
|
||||||
body->location.end = endLocation.end;
|
body->location.end = endLocation.end;
|
||||||
|
|
||||||
if (FFlag::LuauStoreCSTData2 && options.storeCstData)
|
if (FFlag::LuauStoreCSTData2 && options.storeCstData)
|
||||||
|
|
|
@ -87,6 +87,11 @@ private:
|
||||||
[[nodiscard]] Error navigateToAlias(const std::string& alias, const std::string& value);
|
[[nodiscard]] Error navigateToAlias(const std::string& alias, const std::string& value);
|
||||||
[[nodiscard]] Error navigateToAndPopulateConfig(const std::string& desiredAlias);
|
[[nodiscard]] Error navigateToAndPopulateConfig(const std::string& desiredAlias);
|
||||||
|
|
||||||
|
[[nodiscard]] Error resetToRequirer();
|
||||||
|
[[nodiscard]] Error jumpToAlias(const std::string& aliasPath);
|
||||||
|
[[nodiscard]] Error navigateToParent(std::optional<std::string> previousComponent);
|
||||||
|
[[nodiscard]] Error navigateToChild(const std::string& component);
|
||||||
|
|
||||||
NavigationContext& navigationContext;
|
NavigationContext& navigationContext;
|
||||||
ErrorHandler& errorHandler;
|
ErrorHandler& errorHandler;
|
||||||
Luau::Config config;
|
Luau::Config config;
|
||||||
|
|
|
@ -10,24 +10,11 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
static constexpr char kRequireErrorAmbiguous[] = "require path could not be resolved to a unique file";
|
|
||||||
static constexpr char kRequireErrorGeneric[] = "error requiring module";
|
|
||||||
|
|
||||||
namespace Luau::Require
|
namespace Luau::Require
|
||||||
{
|
{
|
||||||
|
|
||||||
using Error = std::optional<std::string>;
|
using Error = std::optional<std::string>;
|
||||||
|
|
||||||
static Error toError(NavigationContext::NavigateResult result)
|
|
||||||
{
|
|
||||||
if (result == NavigationContext::NavigateResult::Success)
|
|
||||||
return std::nullopt;
|
|
||||||
if (result == NavigationContext::NavigateResult::Ambiguous)
|
|
||||||
return kRequireErrorAmbiguous;
|
|
||||||
else
|
|
||||||
return kRequireErrorGeneric;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string extractAlias(std::string_view path)
|
static std::string extractAlias(std::string_view path)
|
||||||
{
|
{
|
||||||
// To ignore the '@' alias prefix when processing the alias
|
// To ignore the '@' alias prefix when processing the alias
|
||||||
|
@ -53,7 +40,7 @@ Navigator::Status Navigator::navigate(std::string path)
|
||||||
{
|
{
|
||||||
std::replace(path.begin(), path.end(), '\\', '/');
|
std::replace(path.begin(), path.end(), '\\', '/');
|
||||||
|
|
||||||
if (Error error = toError(navigationContext.reset(navigationContext.getRequirerIdentifier())))
|
if (Error error = resetToRequirer())
|
||||||
{
|
{
|
||||||
errorHandler.reportError(*error);
|
errorHandler.reportError(*error);
|
||||||
return Status::ErrorReported;
|
return Status::ErrorReported;
|
||||||
|
@ -98,7 +85,7 @@ Error Navigator::navigateImpl(std::string_view path)
|
||||||
|
|
||||||
// If the alias is "@self", we reset to the requirer's context and
|
// If the alias is "@self", we reset to the requirer's context and
|
||||||
// navigate directly from there.
|
// navigate directly from there.
|
||||||
if (Error error = toError(navigationContext.reset(navigationContext.getRequirerIdentifier())))
|
if (Error error = resetToRequirer())
|
||||||
return error;
|
return error;
|
||||||
if (Error error = navigateThroughPath(path))
|
if (Error error = navigateThroughPath(path))
|
||||||
return error;
|
return error;
|
||||||
|
@ -114,7 +101,7 @@ Error Navigator::navigateImpl(std::string_view path)
|
||||||
|
|
||||||
if (pathType == PathType::RelativeToCurrent || pathType == PathType::RelativeToParent)
|
if (pathType == PathType::RelativeToCurrent || pathType == PathType::RelativeToParent)
|
||||||
{
|
{
|
||||||
if (Error error = toError(navigationContext.toParent()))
|
if (Error error = navigateToParent(std::nullopt))
|
||||||
return error;
|
return error;
|
||||||
if (Error error = navigateThroughPath(path))
|
if (Error error = navigateThroughPath(path))
|
||||||
return error;
|
return error;
|
||||||
|
@ -133,6 +120,7 @@ Error Navigator::navigateThroughPath(std::string_view path)
|
||||||
components = splitPath(components.second);
|
components = splitPath(components.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> previousComponent;
|
||||||
while (!(components.first.empty() && components.second.empty()))
|
while (!(components.first.empty() && components.second.empty()))
|
||||||
{
|
{
|
||||||
if (components.first == "." || components.first.empty())
|
if (components.first == "." || components.first.empty())
|
||||||
|
@ -142,14 +130,15 @@ Error Navigator::navigateThroughPath(std::string_view path)
|
||||||
}
|
}
|
||||||
else if (components.first == "..")
|
else if (components.first == "..")
|
||||||
{
|
{
|
||||||
if (Error error = toError(navigationContext.toParent()))
|
if (Error error = navigateToParent(previousComponent))
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Error error = toError(navigationContext.toChild(std::string{components.first})))
|
if (Error error = navigateToChild(std::string{components.first}))
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
previousComponent = components.first;
|
||||||
components = splitPath(components.second);
|
components = splitPath(components.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,11 +156,11 @@ Error Navigator::navigateToAlias(const std::string& alias, const std::string& va
|
||||||
}
|
}
|
||||||
else if (pathType == PathType::Aliased)
|
else if (pathType == PathType::Aliased)
|
||||||
{
|
{
|
||||||
return "@" + alias + " cannot point to other aliases";
|
return "alias \"@" + alias + "\" cannot point to an aliased path (\"" + value + "\")";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Error error = toError(navigationContext.jumpToAlias(value)))
|
if (Error error = jumpToAlias(value))
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +178,7 @@ Error Navigator::navigateToAndPopulateConfig(const std::string& desiredAlias)
|
||||||
{
|
{
|
||||||
std::optional<std::string> configContents = navigationContext.getConfig();
|
std::optional<std::string> configContents = navigationContext.getConfig();
|
||||||
if (!configContents)
|
if (!configContents)
|
||||||
return "could not get configuration file contents";
|
return "could not get configuration file contents to resolve alias \"" + desiredAlias + "\"";
|
||||||
|
|
||||||
Luau::ConfigOptions opts;
|
Luau::ConfigOptions opts;
|
||||||
Luau::ConfigOptions::AliasOptions aliasOpts;
|
Luau::ConfigOptions::AliasOptions aliasOpts;
|
||||||
|
@ -205,4 +194,56 @@ Error Navigator::navigateToAndPopulateConfig(const std::string& desiredAlias)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error Navigator::resetToRequirer()
|
||||||
|
{
|
||||||
|
NavigationContext::NavigateResult result = navigationContext.reset(navigationContext.getRequirerIdentifier());
|
||||||
|
if (result == NavigationContext::NavigateResult::Success)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::string errorMessage = "could not reset to requiring context";
|
||||||
|
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||||
|
errorMessage += " (ambiguous)";
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error Navigator::jumpToAlias(const std::string& aliasPath)
|
||||||
|
{
|
||||||
|
NavigationContext::NavigateResult result = navigationContext.jumpToAlias(aliasPath);
|
||||||
|
if (result == NavigationContext::NavigateResult::Success)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::string errorMessage = "could not jump to alias \"" + aliasPath + "\"";
|
||||||
|
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||||
|
errorMessage += " (ambiguous)";
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error Navigator::navigateToParent(std::optional<std::string> previousComponent)
|
||||||
|
{
|
||||||
|
NavigationContext::NavigateResult result = navigationContext.toParent();
|
||||||
|
if (result == NavigationContext::NavigateResult::Success)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::string errorMessage;
|
||||||
|
if (previousComponent)
|
||||||
|
errorMessage = "could not get parent of component \"" + *previousComponent + "\"";
|
||||||
|
else
|
||||||
|
errorMessage = "could not get parent of requiring context";
|
||||||
|
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||||
|
errorMessage += " (ambiguous)";
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error Navigator::navigateToChild(const std::string& component)
|
||||||
|
{
|
||||||
|
NavigationContext::NavigateResult result = navigationContext.toChild(component);
|
||||||
|
if (result == NavigationContext::NavigateResult::Success)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::string errorMessage = "could not resolve child component \"" + component + "\"";
|
||||||
|
if (result == NavigationContext::NavigateResult::Ambiguous)
|
||||||
|
errorMessage += " (ambiguous)";
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -106,7 +106,9 @@ struct luarequire_Configuration
|
||||||
luarequire_WriteResult (*get_config)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
luarequire_WriteResult (*get_config)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
||||||
|
|
||||||
// Executes the module and places the result on the stack. Returns the
|
// Executes the module and places the result on the stack. Returns the
|
||||||
// number of results placed on the stack.
|
// number of results placed on the stack. Returning -1 directs the requiring
|
||||||
|
// thread to yield. In this case, this thread should be resumed with the
|
||||||
|
// module result pushed onto its stack.
|
||||||
int (*load)(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* contents);
|
int (*load)(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* contents);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,3 +132,10 @@ LUALIB_API int luarequire_pushproxyrequire(lua_State* L, luarequire_Configuratio
|
||||||
// result will always be immediately returned when the given path is required.
|
// result will always be immediately returned when the given path is required.
|
||||||
// Expects the path and table to be passed as arguments on the stack.
|
// Expects the path and table to be passed as arguments on the stack.
|
||||||
LUALIB_API int luarequire_registermodule(lua_State* L);
|
LUALIB_API int luarequire_registermodule(lua_State* L);
|
||||||
|
|
||||||
|
// Clears the entry associated with the given cache key from the require cache.
|
||||||
|
// Expects the cache key to be passed as an argument on the stack.
|
||||||
|
LUALIB_API int luarequire_clearcacheentry(lua_State* L);
|
||||||
|
|
||||||
|
// Clears all entries from the require cache.
|
||||||
|
LUALIB_API int luarequire_clearcache(lua_State* L);
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
static constexpr size_t initalFileBufferSize = 1024;
|
static constexpr size_t initalFileBufferSize = 1024;
|
||||||
static constexpr size_t initalIdentifierBufferSize = 64;
|
static constexpr size_t initalIdentifierBufferSize = 64;
|
||||||
|
|
||||||
|
@ -111,14 +113,16 @@ std::optional<std::string> RuntimeNavigationContext::getStringFromCWriter(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RuntimeErrorHandler::RuntimeErrorHandler(lua_State* L)
|
RuntimeErrorHandler::RuntimeErrorHandler(lua_State* L, std::string requiredPath)
|
||||||
: L(L)
|
: L(L)
|
||||||
|
, errorPrefix("error requiring module \"" + std::move(requiredPath) + "\": ")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeErrorHandler::reportError(std::string message)
|
void RuntimeErrorHandler::reportError(std::string message)
|
||||||
{
|
{
|
||||||
luaL_errorL(L, "%s", message.c_str());
|
std::string fullError = errorPrefix + std::move(message);
|
||||||
|
luaL_errorL(L, "%s", fullError.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include "Luau/RequireNavigator.h"
|
#include "Luau/RequireNavigator.h"
|
||||||
#include "Luau/Require.h"
|
#include "Luau/Require.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct lua_State;
|
struct lua_State;
|
||||||
struct luarequire_Configuration;
|
struct luarequire_Configuration;
|
||||||
|
|
||||||
|
@ -48,11 +50,12 @@ private:
|
||||||
class RuntimeErrorHandler : public ErrorHandler
|
class RuntimeErrorHandler : public ErrorHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RuntimeErrorHandler(lua_State* L);
|
RuntimeErrorHandler(lua_State* L, std::string requiredPath);
|
||||||
void reportError(std::string message) override;
|
void reportError(std::string message) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
lua_State* L;
|
lua_State* L;
|
||||||
|
std::string errorPrefix;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -53,7 +53,7 @@ static int pushrequireclosureinternal(
|
||||||
lua_pushlightuserdata(L, ctx);
|
lua_pushlightuserdata(L, ctx);
|
||||||
|
|
||||||
// require-like closure captures config and ctx as upvalues
|
// require-like closure captures config and ctx as upvalues
|
||||||
lua_pushcclosure(L, requirelikefunc, debugname, 2);
|
lua_pushcclosurek(L, requirelikefunc, debugname, 2, Luau::Require::lua_requirecont);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,3 +77,13 @@ int luarequire_registermodule(lua_State* L)
|
||||||
{
|
{
|
||||||
return Luau::Require::registerModuleImpl(L);
|
return Luau::Require::registerModuleImpl(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int luarequire_clearcacheentry(lua_State* L)
|
||||||
|
{
|
||||||
|
return Luau::Require::clearCacheEntry(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
int luarequire_clearcache(lua_State* L)
|
||||||
|
{
|
||||||
|
return Luau::Require::clearCache(L);
|
||||||
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
||||||
luaL_error(L, "require is not supported in this context");
|
luaL_error(L, "require is not supported in this context");
|
||||||
|
|
||||||
RuntimeNavigationContext navigationContext{lrc, L, ctx, requirerChunkname};
|
RuntimeNavigationContext navigationContext{lrc, L, ctx, requirerChunkname};
|
||||||
RuntimeErrorHandler errorHandler{L}; // Errors reported directly to lua_State.
|
RuntimeErrorHandler errorHandler{L, path}; // Errors reported directly to lua_State.
|
||||||
|
|
||||||
Navigator navigator(navigationContext, errorHandler);
|
Navigator navigator(navigationContext, errorHandler);
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
||||||
|
|
||||||
if (!navigationContext.isModulePresent())
|
if (!navigationContext.isModulePresent())
|
||||||
{
|
{
|
||||||
luaL_errorL(L, "no module present at resolved path");
|
errorHandler.reportError("no module present at resolved path");
|
||||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,24 +118,13 @@ static int checkRegisteredModules(lua_State* L, const char* path)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
int lua_requirecont(lua_State* L, int status)
|
||||||
{
|
{
|
||||||
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
// Number of stack arguments present before this continuation is called.
|
||||||
if (!lrc)
|
const int numStackArgs = 2;
|
||||||
luaL_error(L, "unable to find require configuration");
|
const int numResults = lua_gettop(L) - numStackArgs;
|
||||||
|
const char* cacheKey = luaL_checkstring(L, 2);
|
||||||
|
|
||||||
void* ctx = lua_tolightuserdata(L, lua_upvalueindex(2));
|
|
||||||
|
|
||||||
const char* path = luaL_checkstring(L, 1);
|
|
||||||
|
|
||||||
if (checkRegisteredModules(L, path) == 1)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, requirerChunkname, path);
|
|
||||||
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
int numResults = lrc->load(L, ctx, path, resolvedRequire.chunkname.c_str(), resolvedRequire.contents.c_str());
|
|
||||||
if (numResults > 1)
|
if (numResults > 1)
|
||||||
luaL_error(L, "module must return a single value");
|
luaL_error(L, "module must return a single value");
|
||||||
|
|
||||||
|
@ -151,7 +140,7 @@ int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
||||||
lua_pushvalue(L, -2);
|
lua_pushvalue(L, -2);
|
||||||
// (-3) result, (-2) cache table, (-1) result
|
// (-3) result, (-2) cache table, (-1) result
|
||||||
|
|
||||||
lua_setfield(L, -2, resolvedRequire.cacheKey.c_str());
|
lua_setfield(L, -2, cacheKey);
|
||||||
// (-2) result, (-1) cache table
|
// (-2) result, (-1) cache table
|
||||||
|
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
@ -161,6 +150,43 @@ int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
||||||
return numResults;
|
return numResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
||||||
|
{
|
||||||
|
// If modifying the state of the stack, please update numStackArgs in the
|
||||||
|
// lua_requirecont continuation function.
|
||||||
|
|
||||||
|
luarequire_Configuration* lrc = static_cast<luarequire_Configuration*>(lua_touserdata(L, lua_upvalueindex(1)));
|
||||||
|
if (!lrc)
|
||||||
|
luaL_error(L, "unable to find require configuration");
|
||||||
|
|
||||||
|
void* ctx = lua_tolightuserdata(L, lua_upvalueindex(2));
|
||||||
|
|
||||||
|
// (1) path
|
||||||
|
const char* path = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
|
if (checkRegisteredModules(L, path) == 1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ResolvedRequire resolvedRequire = resolveRequire(lrc, L, ctx, requirerChunkname, path);
|
||||||
|
if (resolvedRequire.status == ResolvedRequire::Status::Cached)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// (1) path, (2) cacheKey
|
||||||
|
lua_pushstring(L, resolvedRequire.cacheKey.c_str());
|
||||||
|
|
||||||
|
int numArgsBeforeLoad = lua_gettop(L);
|
||||||
|
int numResults = lrc->load(L, ctx, path, resolvedRequire.chunkname.c_str(), resolvedRequire.contents.c_str());
|
||||||
|
if (numResults == -1)
|
||||||
|
{
|
||||||
|
if (lua_gettop(L) != numArgsBeforeLoad)
|
||||||
|
luaL_error(L, "stack cannot be modified when require yields");
|
||||||
|
|
||||||
|
return lua_yield(L, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lua_requirecont(L, LUA_OK);
|
||||||
|
}
|
||||||
|
|
||||||
int lua_proxyrequire(lua_State* L)
|
int lua_proxyrequire(lua_State* L)
|
||||||
{
|
{
|
||||||
const char* requirerChunkname = luaL_checkstring(L, 2);
|
const char* requirerChunkname = luaL_checkstring(L, 2);
|
||||||
|
@ -170,7 +196,14 @@ int lua_proxyrequire(lua_State* L)
|
||||||
int lua_require(lua_State* L)
|
int lua_require(lua_State* L)
|
||||||
{
|
{
|
||||||
lua_Debug ar;
|
lua_Debug ar;
|
||||||
lua_getinfo(L, 1, "s", &ar);
|
int level = 1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!lua_getinfo(L, level++, "s", &ar))
|
||||||
|
luaL_error(L, "require is not supported in this context");
|
||||||
|
} while (ar.what[0] == 'C');
|
||||||
|
|
||||||
return lua_requireinternal(L, ar.source);
|
return lua_requireinternal(L, ar.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,4 +232,21 @@ int registerModuleImpl(lua_State* L)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int clearCacheEntry(lua_State* L)
|
||||||
|
{
|
||||||
|
const char* cacheKey = luaL_checkstring(L, 1);
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, requiredCacheTableKey, 1);
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_setfield(L, -2, cacheKey);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clearCache(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_setfield(L, LUA_REGISTRYINDEX, requiredCacheTableKey);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -8,7 +8,11 @@ namespace Luau::Require
|
||||||
|
|
||||||
int lua_require(lua_State* L);
|
int lua_require(lua_State* L);
|
||||||
int lua_proxyrequire(lua_State* L);
|
int lua_proxyrequire(lua_State* L);
|
||||||
|
int lua_requirecont(lua_State* L, int status);
|
||||||
|
|
||||||
int registerModuleImpl(lua_State* L);
|
int registerModuleImpl(lua_State* L);
|
||||||
|
|
||||||
|
int clearCacheEntry(lua_State* L);
|
||||||
|
int clearCache(lua_State* L);
|
||||||
|
|
||||||
} // namespace Luau::Require
|
} // namespace Luau::Require
|
||||||
|
|
|
@ -599,4 +599,63 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypePackExplicit")
|
||||||
CHECK(toJson(root->body.data[1]) == expected);
|
CHECK(toJson(root->body.data[1]) == expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericType")
|
||||||
|
{
|
||||||
|
AstStatBlock* root = expectParse(R"(
|
||||||
|
a = function<b, c>()
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
CHECK(1 == root->body.size);
|
||||||
|
|
||||||
|
std::string_view expected =
|
||||||
|
R"({"type":"AstStatAssign","location":"1,8 - 2,11","vars":[{"type":"AstExprGlobal","location":"1,8 - 1,9","global":"a"}],"values":[{"type":"AstExprFunction","location":"1,12 - 2,11","attributes":[],"generics":[{"type":"AstGenericType","name":"b"},{"type":"AstGenericType","name":"c"}],"genericPacks":[],"args":[],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"1,28 - 2,8","hasEnd":true,"body":[]},"functionDepth":1,"debugname":""}]})";
|
||||||
|
|
||||||
|
CHECK(toJson(root->body.data[0]) == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypeWithDefault")
|
||||||
|
{
|
||||||
|
AstStatBlock* root = expectParse(R"(
|
||||||
|
type Foo<X = string> = X
|
||||||
|
)");
|
||||||
|
|
||||||
|
CHECK(1 == root->body.size);
|
||||||
|
|
||||||
|
std::string_view expected =
|
||||||
|
R"({"type":"AstStatTypeAlias","location":"1,8 - 1,32","name":"Foo","generics":[{"type":"AstGenericType","name":"X","luauType":{"type":"AstTypeReference","location":"1,21 - 1,27","name":"string","nameLocation":"1,21 - 1,27","parameters":[]}}],"genericPacks":[],"value":{"type":"AstTypeReference","location":"1,31 - 1,32","name":"X","nameLocation":"1,31 - 1,32","parameters":[]},"exported":false})";
|
||||||
|
|
||||||
|
CHECK(toJson(root->body.data[0]) == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypePack")
|
||||||
|
{
|
||||||
|
AstStatBlock* root = expectParse(R"(
|
||||||
|
a = function<b..., c...>()
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
CHECK(1 == root->body.size);
|
||||||
|
|
||||||
|
std::string_view expected =
|
||||||
|
R"({"type":"AstStatAssign","location":"1,8 - 2,11","vars":[{"type":"AstExprGlobal","location":"1,8 - 1,9","global":"a"}],"values":[{"type":"AstExprFunction","location":"1,12 - 2,11","attributes":[],"generics":[],"genericPacks":[{"type":"AstGenericTypePack","name":"b"},{"type":"AstGenericTypePack","name":"c"}],"args":[],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"1,34 - 2,8","hasEnd":true,"body":[]},"functionDepth":1,"debugname":""}]})";
|
||||||
|
|
||||||
|
CHECK(toJson(root->body.data[0]) == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstGenericTypePackWithDefault")
|
||||||
|
{
|
||||||
|
AstStatBlock* root = expectParse(R"(
|
||||||
|
type Foo<X... = ...string> = any
|
||||||
|
)");
|
||||||
|
|
||||||
|
CHECK(1 == root->body.size);
|
||||||
|
|
||||||
|
std::string_view expected =
|
||||||
|
R"({"type":"AstStatTypeAlias","location":"1,8 - 1,40","name":"Foo","generics":[],"genericPacks":[{"type":"AstGenericTypePack","name":"X","luauType":{"type":"AstTypePackVariadic","location":"1,24 - 1,33","variadicType":{"type":"AstTypeReference","location":"1,27 - 1,33","name":"string","nameLocation":"1,27 - 1,33","parameters":[]}}}],"value":{"type":"AstTypeReference","location":"1,37 - 1,40","name":"any","nameLocation":"1,37 - 1,40","parameters":[]},"exported":false})";
|
||||||
|
|
||||||
|
CHECK(toJson(root->body.data[0]) == expected);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2);
|
LUAU_FASTFLAG(LuauSolverV2);
|
||||||
|
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
|
|
||||||
struct DataFlowGraphFixture
|
struct DataFlowGraphFixture
|
||||||
{
|
{
|
||||||
|
@ -421,6 +422,8 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "property_lookup_on_a_phi_node_3")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||||
|
|
||||||
dfg(R"(
|
dfg(R"(
|
||||||
local x = 5
|
local x = 5
|
||||||
|
|
||||||
|
@ -439,15 +442,14 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_
|
||||||
DefId x4 = getDef<AstExprLocal, 3>(); // x = "five"
|
DefId x4 = getDef<AstExprLocal, 3>(); // x = "five"
|
||||||
|
|
||||||
CHECK(x1 != x2);
|
CHECK(x1 != x2);
|
||||||
CHECK(x2 != x3);
|
CHECK(x2 == x3);
|
||||||
CHECK(x3 != x4);
|
CHECK(x3 != x4);
|
||||||
|
|
||||||
const Phi* phi = get<Phi>(x2);
|
const Phi* phi = get<Phi>(x2);
|
||||||
REQUIRE(phi);
|
REQUIRE(phi);
|
||||||
REQUIRE(phi->operands.size() == 3);
|
REQUIRE(phi->operands.size() == 2);
|
||||||
CHECK(phi->operands.at(0) == x1);
|
CHECK(phi->operands.at(0) == x1);
|
||||||
CHECK(phi->operands.at(1) == x3);
|
CHECK(phi->operands.at(1) == x4);
|
||||||
CHECK(phi->operands.at(2) == x4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions_properties")
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "function_captures_are_phi_nodes_of_all_versions_properties")
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
LUAU_FASTFLAG(DebugLuauForceAllNewSolverTests)
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauTypeFunOptional)
|
||||||
|
|
||||||
#define DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(line) ScopedFastFlag sff_##line{FFlag::LuauSolverV2, FFlag::DebugLuauForceAllNewSolverTests};
|
#define DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(line) ScopedFastFlag sff_##line{FFlag::LuauSolverV2, FFlag::DebugLuauForceAllNewSolverTests};
|
||||||
|
|
||||||
#define DOES_NOT_PASS_NEW_SOLVER_GUARD() DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(__LINE__)
|
#define DOES_NOT_PASS_NEW_SOLVER_GUARD() DOES_NOT_PASS_NEW_SOLVER_GUARD_IMPL(__LINE__)
|
||||||
|
@ -182,6 +184,9 @@ private:
|
||||||
struct BuiltinsFixture : Fixture
|
struct BuiltinsFixture : Fixture
|
||||||
{
|
{
|
||||||
explicit BuiltinsFixture(bool prepareAutocomplete = false);
|
explicit BuiltinsFixture(bool prepareAutocomplete = false);
|
||||||
|
|
||||||
|
// For the purpose of our tests, we're always the latest version of type functions.
|
||||||
|
ScopedFastFlag sff_optionalInTypeFunctionLib{FFlag::LuauTypeFunOptional, true};
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments);
|
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& segments);
|
||||||
|
|
|
@ -24,10 +24,6 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTINT(LuauParseErrorLimit)
|
LUAU_FASTINT(LuauParseErrorLimit)
|
||||||
LUAU_FASTFLAG(LuauCloneIncrementalModule)
|
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauMixedModeDefFinderTraversesTypeOf)
|
|
||||||
LUAU_FASTFLAG(LuauFreeTypesMustHaveBounds)
|
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
|
LUAU_FASTFLAG(LuauBetterReverseDependencyTracking)
|
||||||
LUAU_FASTFLAG(LuauAutocompleteUsesModuleForTypeCompatibility)
|
LUAU_FASTFLAG(LuauAutocompleteUsesModuleForTypeCompatibility)
|
||||||
|
@ -72,8 +68,6 @@ 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 luauFreeTypesMustHaveBounds{FFlag::LuauFreeTypesMustHaveBounds, true};
|
|
||||||
ScopedFastFlag luauCloneIncrementalModule{FFlag::LuauCloneIncrementalModule, true};
|
|
||||||
ScopedFastFlag luauAllFreeTypesHaveScopes{FFlag::LuauAllFreeTypesHaveScopes, true};
|
ScopedFastFlag luauAllFreeTypesHaveScopes{FFlag::LuauAllFreeTypesHaveScopes, true};
|
||||||
ScopedFastFlag luauClonedTableAndFunctionTypesMustHaveScopes{FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes, true};
|
ScopedFastFlag luauClonedTableAndFunctionTypesMustHaveScopes{FFlag::LuauClonedTableAndFunctionTypesMustHaveScopes, true};
|
||||||
ScopedFastFlag luauDisableNewSolverAssertsInMixedMode{FFlag::LuauDisableNewSolverAssertsInMixedMode, true};
|
ScopedFastFlag luauDisableNewSolverAssertsInMixedMode{FFlag::LuauDisableNewSolverAssertsInMixedMode, true};
|
||||||
|
@ -1562,8 +1556,6 @@ return module)";
|
||||||
|
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
|
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
|
||||||
ScopedFastFlag sff2{FFlag::LuauCloneIncrementalModule, true};
|
|
||||||
ScopedFastFlag sff3{FFlag::LuauFreeTypesMustHaveBounds, true};
|
|
||||||
checkAndExamine(source, "module", "{ }");
|
checkAndExamine(source, "module", "{ }");
|
||||||
fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }");
|
fragmentACAndCheck(updated1, Position{1, 17}, "module", "{ }", "{ a: (%error-id%: unknown) -> () }");
|
||||||
fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }");
|
fragmentACAndCheck(updated2, Position{1, 18}, "module", "{ }", "{ ab: (%error-id%: unknown) -> () }");
|
||||||
|
@ -2782,7 +2774,6 @@ TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "fragment_ac_must_travers
|
||||||
// This test ensures that we traverse typeof expressions for defs that are being referred to in the fragment
|
// This test ensures that we traverse typeof expressions for defs that are being referred to in the fragment
|
||||||
// In this case, we want to ensure we populate the incremental environment with the reference to `m`
|
// In this case, we want to ensure we populate the incremental environment with the reference to `m`
|
||||||
// Without this, we would ice as we will refer to the local `m` before it's declaration
|
// Without this, we would ice as we will refer to the local `m` before it's declaration
|
||||||
ScopedFastFlag sff{FFlag::LuauMixedModeDefFinderTraversesTypeOf, true};
|
|
||||||
const std::string source = R"(
|
const std::string source = R"(
|
||||||
--!strict
|
--!strict
|
||||||
local m = {}
|
local m = {}
|
||||||
|
@ -2860,7 +2851,6 @@ type V = {h : number, i : U?}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "generalization_crash_when_old_solver_freetypes_have_no_bounds_set")
|
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "generalization_crash_when_old_solver_freetypes_have_no_bounds_set")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauFreeTypesMustHaveBounds, true};
|
|
||||||
const std::string source = R"(
|
const std::string source = R"(
|
||||||
local UserInputService = game:GetService("UserInputService");
|
local UserInputService = game:GetService("UserInputService");
|
||||||
|
|
||||||
|
@ -2892,7 +2882,6 @@ end)
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "fragment_autocomplete_ensures_memory_isolation")
|
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "fragment_autocomplete_ensures_memory_isolation")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauCloneIncrementalModule, true};
|
|
||||||
ToStringOptions opt;
|
ToStringOptions opt;
|
||||||
opt.exhaustive = true;
|
opt.exhaustive = true;
|
||||||
opt.exhaustive = true;
|
opt.exhaustive = true;
|
||||||
|
@ -2962,7 +2951,6 @@ return module)";
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "fragment_autocomplete_shouldnt_crash_on_cross_module_mutation")
|
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "fragment_autocomplete_shouldnt_crash_on_cross_module_mutation")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauCloneIncrementalModule, true};
|
|
||||||
const std::string source = R"(local module = {}
|
const std::string source = R"(local module = {}
|
||||||
function module.
|
function module.
|
||||||
return module
|
return module
|
||||||
|
|
|
@ -17,7 +17,7 @@ LUAU_FASTFLAG(LuauSolverV2);
|
||||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes)
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -1015,7 +1015,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "environments")
|
||||||
LUAU_REQUIRE_NO_ERRORS(resultA);
|
LUAU_REQUIRE_NO_ERRORS(resultA);
|
||||||
|
|
||||||
CheckResult resultB = frontend.check("B");
|
CheckResult resultB = frontend.check("B");
|
||||||
if (FFlag::LuauSolverV2 && !FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauSolverV2 && !FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
LUAU_REQUIRE_NO_ERRORS(resultB);
|
LUAU_REQUIRE_NO_ERRORS(resultB);
|
||||||
else
|
else
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, resultB);
|
LUAU_REQUIRE_ERROR_COUNT(1, resultB);
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||||
LUAU_FASTFLAG(LuauNonStrictVisitorImprovements)
|
LUAU_FASTFLAG(LuauNonStrictVisitorImprovements)
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes)
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -668,7 +668,7 @@ TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_non_strict")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNewNonStrictVisitTypes, true};
|
ScopedFastFlag sff{FFlag::LuauNewNonStrictVisitTypes2, true};
|
||||||
|
|
||||||
CheckResult result = check(Mode::Nonstrict, R"(
|
CheckResult result = check(Mode::Nonstrict, R"(
|
||||||
--!nonstrict
|
--!nonstrict
|
||||||
|
@ -683,7 +683,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict_2")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauNewNonStrictVisitTypes, true};
|
ScopedFastFlag sff{FFlag::LuauNewNonStrictVisitTypes2, true};
|
||||||
|
|
||||||
CheckResult result = check(Mode::Nonstrict, R"(
|
CheckResult result = check(Mode::Nonstrict, R"(
|
||||||
--!nonstrict
|
--!nonstrict
|
||||||
|
@ -707,4 +707,13 @@ end
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "incomplete_function_annotation")
|
||||||
|
{
|
||||||
|
CheckResult result = check(Mode::Nonstrict, R"(
|
||||||
|
local x: () ->
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -18,7 +18,6 @@ LUAU_FASTINT(LuauTypeLengthLimit)
|
||||||
LUAU_FASTINT(LuauParseErrorLimit)
|
LUAU_FASTINT(LuauParseErrorLimit)
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||||
LUAU_FASTFLAG(LuauFixDoBlockEndLocation)
|
|
||||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||||
LUAU_FASTFLAG(LuauParseStringIndexer)
|
LUAU_FASTFLAG(LuauParseStringIndexer)
|
||||||
|
@ -2869,8 +2868,6 @@ TEST_CASE_FIXTURE(Fixture, "inner_and_outer_scope_of_functions_have_correct_end_
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "do_block_end_location_is_after_end_token")
|
TEST_CASE_FIXTURE(Fixture, "do_block_end_location_is_after_end_token")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauFixDoBlockEndLocation, true};
|
|
||||||
|
|
||||||
AstStatBlock* stat = parse(R"(
|
AstStatBlock* stat = parse(R"(
|
||||||
do
|
do
|
||||||
local x = 1
|
local x = 1
|
||||||
|
|
|
@ -332,6 +332,13 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireSimpleRelativePath")
|
||||||
assertOutputContainsAll({"true", "result from dependency"});
|
assertOutputContainsAll({"true", "result from dependency"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireSimpleRelativePathWithinPcall")
|
||||||
|
{
|
||||||
|
std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/dependency";
|
||||||
|
runCode(L, "return pcall(require, \"" + path + "\")");
|
||||||
|
assertOutputContainsAll({"true", "result from dependency"});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireRelativeToRequiringFile")
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireRelativeToRequiringFile")
|
||||||
{
|
{
|
||||||
std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/module";
|
std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/module";
|
||||||
|
@ -372,7 +379,9 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireWithFileAmbiguity")
|
||||||
std::string ambiguousPath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/ambiguous_file_requirer";
|
std::string ambiguousPath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/ambiguous_file_requirer";
|
||||||
|
|
||||||
runProtectedRequire(ambiguousPath);
|
runProtectedRequire(ambiguousPath);
|
||||||
assertOutputContainsAll({"false", "require path could not be resolved to a unique file"});
|
assertOutputContainsAll(
|
||||||
|
{"false", "error requiring module \"./ambiguous/file/dependency\": could not resolve child component \"dependency\" (ambiguous)"}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireWithDirectoryAmbiguity")
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireWithDirectoryAmbiguity")
|
||||||
|
@ -380,7 +389,9 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireWithDirectoryAmbiguity")
|
||||||
std::string ambiguousPath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/ambiguous_directory_requirer";
|
std::string ambiguousPath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/ambiguous_directory_requirer";
|
||||||
|
|
||||||
runProtectedRequire(ambiguousPath);
|
runProtectedRequire(ambiguousPath);
|
||||||
assertOutputContainsAll({"false", "require path could not be resolved to a unique file"});
|
assertOutputContainsAll(
|
||||||
|
{"false", "error requiring module \"./ambiguous/directory/dependency\": could not resolve child component \"dependency\" (ambiguous)"}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "CheckCacheAfterRequireLuau")
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "CheckCacheAfterRequireLuau")
|
||||||
|
@ -466,6 +477,61 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "CheckCachedResult")
|
||||||
assertOutputContainsAll({"true"});
|
assertOutputContainsAll({"true"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "CheckClearCacheEntry")
|
||||||
|
{
|
||||||
|
std::string relativePath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/module";
|
||||||
|
std::string absolutePath = getLuauDirectory(PathType::Absolute) + "/tests/require/without_config/module";
|
||||||
|
std::string cacheKey = absolutePath + ".luau";
|
||||||
|
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
||||||
|
lua_getfield(L, -1, cacheKey.c_str());
|
||||||
|
REQUIRE_MESSAGE(lua_isnil(L, -1), "Cache already contained module result");
|
||||||
|
|
||||||
|
runProtectedRequire(relativePath);
|
||||||
|
|
||||||
|
assertOutputContainsAll({"true", "result from dependency", "required into module"});
|
||||||
|
|
||||||
|
// Check cache for the absolute path as a cache key
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
||||||
|
lua_getfield(L, -1, cacheKey.c_str());
|
||||||
|
REQUIRE_FALSE_MESSAGE(lua_isnil(L, -1), "Cache did not contain module result");
|
||||||
|
|
||||||
|
lua_pushcfunction(L, luarequire_clearcacheentry, nullptr);
|
||||||
|
lua_pushstring(L, cacheKey.c_str());
|
||||||
|
lua_call(L, 1, 0);
|
||||||
|
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
||||||
|
lua_getfield(L, -1, cacheKey.c_str());
|
||||||
|
REQUIRE_MESSAGE(lua_isnil(L, -1), "Cache was not cleared");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "CheckClearCache")
|
||||||
|
{
|
||||||
|
std::string relativePath = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/module";
|
||||||
|
std::string absolutePath = getLuauDirectory(PathType::Absolute) + "/tests/require/without_config/module";
|
||||||
|
std::string cacheKey = absolutePath + ".luau";
|
||||||
|
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
||||||
|
lua_getfield(L, -1, cacheKey.c_str());
|
||||||
|
REQUIRE_MESSAGE(lua_isnil(L, -1), "Cache already contained module result");
|
||||||
|
|
||||||
|
runProtectedRequire(relativePath);
|
||||||
|
|
||||||
|
assertOutputContainsAll({"true", "result from dependency", "required into module"});
|
||||||
|
|
||||||
|
// Check cache for the absolute path as a cache key
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
||||||
|
lua_getfield(L, -1, cacheKey.c_str());
|
||||||
|
REQUIRE_FALSE_MESSAGE(lua_isnil(L, -1), "Cache did not contain module result");
|
||||||
|
|
||||||
|
lua_pushcfunction(L, luarequire_clearcache, nullptr);
|
||||||
|
lua_call(L, 0, 0);
|
||||||
|
|
||||||
|
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
|
||||||
|
lua_getfield(L, -1, cacheKey.c_str());
|
||||||
|
REQUIRE_MESSAGE(lua_isnil(L, -1), "Cache was not cleared");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "RegisterRuntimeModule")
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RegisterRuntimeModule")
|
||||||
{
|
{
|
||||||
lua_pushcfunction(L, luarequire_registermodule, nullptr);
|
lua_pushcfunction(L, luarequire_registermodule, nullptr);
|
||||||
|
|
|
@ -1249,8 +1249,7 @@ TEST_CASE_FIXTURE(SubtypeFixture, "bill")
|
||||||
CHECK(isSubtype(b, a).isSubtype);
|
CHECK(isSubtype(b, a).isSubtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST_CASE_FIXTURE(SubtypeFixture, "({[string]: number, a: string}) -> () <: ({[string]: number, a: string}) -> ()")
|
TEST_CASE_FIXTURE(SubtypeFixture, "({[string]: number, a: string}) -> () <: ({[string]: number, a: string}) -> ()")
|
||||||
TEST_CASE_FIXTURE(SubtypeFixture, "fred")
|
|
||||||
{
|
{
|
||||||
auto makeTheType = [&]()
|
auto makeTheType = [&]()
|
||||||
{
|
{
|
||||||
|
|
|
@ -341,7 +341,10 @@ TEST_CASE("function_spaces_around_tokens")
|
||||||
|
|
||||||
TEST_CASE("function_with_types_spaces_around_tokens")
|
TEST_CASE("function_with_types_spaces_around_tokens")
|
||||||
{
|
{
|
||||||
ScopedFastFlag _{FFlag::LuauStoreCSTData2, true};
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauStoreCSTData2, true},
|
||||||
|
{FFlag::LuauStoreReturnTypesAsPackOnAst, true}
|
||||||
|
};
|
||||||
std::string code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
std::string code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
||||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
|
@ -392,9 +395,8 @@ TEST_CASE("function_with_types_spaces_around_tokens")
|
||||||
code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any ): string end )";
|
code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any ): string end )";
|
||||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
||||||
// TODO(CLI-139347): re-enable test once return type positions are supported
|
code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any) :string end )";
|
||||||
// code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any) :string end )";
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
// CHECK_EQ(code, transpile(code, {}, true).code);
|
|
||||||
|
|
||||||
code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
||||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||||
|
|
|
@ -18,6 +18,7 @@ LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
|
||||||
LUAU_FASTFLAG(LuauIndexTypeFunctionFunctionMetamethods)
|
LUAU_FASTFLAG(LuauIndexTypeFunctionFunctionMetamethods)
|
||||||
LUAU_FASTFLAG(LuauMetatableTypeFunctions)
|
LUAU_FASTFLAG(LuauMetatableTypeFunctions)
|
||||||
LUAU_FASTFLAG(LuauMetatablesHaveLength)
|
LUAU_FASTFLAG(LuauMetatablesHaveLength)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauIndexAnyIsAny)
|
LUAU_FASTFLAG(LuauIndexAnyIsAny)
|
||||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
||||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||||
|
@ -1674,4 +1675,70 @@ print(test.a)
|
||||||
CHECK("Type 'add<string, string>' does not have key 'a'" == toString(result.errors[1]));
|
CHECK("Type 'add<string, string>' does not have key 'a'" == toString(result.errors[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TFFixture
|
||||||
|
{
|
||||||
|
TypeArena arena_;
|
||||||
|
NotNull<TypeArena> arena{&arena_};
|
||||||
|
|
||||||
|
BuiltinTypes builtinTypes_;
|
||||||
|
NotNull<BuiltinTypes> builtinTypes{&builtinTypes_};
|
||||||
|
|
||||||
|
ScopePtr globalScope = std::make_shared<Scope>(builtinTypes->anyTypePack);
|
||||||
|
|
||||||
|
InternalErrorReporter ice;
|
||||||
|
UnifierSharedState unifierState{&ice};
|
||||||
|
SimplifierPtr simplifier = EqSatSimplification::newSimplifier(arena, builtinTypes);
|
||||||
|
Normalizer normalizer{arena, builtinTypes, NotNull{&unifierState}};
|
||||||
|
TypeCheckLimits limits;
|
||||||
|
TypeFunctionRuntime runtime{NotNull{&ice}, NotNull{&limits}};
|
||||||
|
|
||||||
|
const ScopedFastFlag sff[1] = {
|
||||||
|
{FFlag::DebugLuauGreedyGeneralization, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
BuiltinTypeFunctions builtinTypeFunctions;
|
||||||
|
|
||||||
|
TypeFunctionContext tfc{
|
||||||
|
arena,
|
||||||
|
builtinTypes,
|
||||||
|
NotNull{globalScope.get()},
|
||||||
|
NotNull{simplifier.get()},
|
||||||
|
NotNull{&normalizer},
|
||||||
|
NotNull{&runtime},
|
||||||
|
NotNull{&ice},
|
||||||
|
NotNull{&limits}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(TFFixture, "refine<G, ~(false?)>")
|
||||||
|
{
|
||||||
|
TypeId g = arena->addType(GenericType{globalScope.get(), Polarity::Negative});
|
||||||
|
|
||||||
|
TypeId refineTy = arena->addType(TypeFunctionInstanceType{
|
||||||
|
builtinTypeFunctions.refineFunc, {g, builtinTypes->truthyType}
|
||||||
|
});
|
||||||
|
|
||||||
|
FunctionGraphReductionResult res = reduceTypeFunctions(refineTy, Location{}, tfc);
|
||||||
|
|
||||||
|
CHECK(res.reducedTypes.size() == 1);
|
||||||
|
|
||||||
|
CHECK(res.errors.size() == 0);
|
||||||
|
CHECK(res.irreducibleTypes.size() == 0);
|
||||||
|
CHECK(res.blockedTypes.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(TFFixture, "or<'a, 'b>")
|
||||||
|
{
|
||||||
|
TypeId aType = arena->freshType(builtinTypes, globalScope.get());
|
||||||
|
TypeId bType = arena->freshType(builtinTypes, globalScope.get());
|
||||||
|
|
||||||
|
TypeId orType = arena->addType(TypeFunctionInstanceType{
|
||||||
|
builtinTypeFunctions.orFunc, {aType, bType}
|
||||||
|
});
|
||||||
|
|
||||||
|
FunctionGraphReductionResult res = reduceTypeFunctions(orType, Location{}, tfc);
|
||||||
|
|
||||||
|
CHECK(res.reducedTypes.size() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -15,7 +15,6 @@ LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
||||||
LUAU_FASTFLAG(LuauNoTypeFunctionsNamedTypeOf)
|
LUAU_FASTFLAG(LuauNoTypeFunctionsNamedTypeOf)
|
||||||
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_serialization_works")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_serialization_works")
|
||||||
|
@ -370,6 +369,43 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
|
||||||
CHECK(toString(tpm->givenTp) == "boolean | number | string");
|
CHECK(toString(tpm->givenTp) == "boolean | number | string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function numberhuh()
|
||||||
|
return types.optional(types.number)
|
||||||
|
end
|
||||||
|
-- forcing an error here to check the exact type of the union
|
||||||
|
local function ok(idx: numberhuh<>): never return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
TypePackMismatch* tpm = get<TypePackMismatch>(result.errors[0]);
|
||||||
|
REQUIRE(tpm);
|
||||||
|
CHECK(toString(tpm->givenTp) == "number?");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optional_works_on_unions")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function foobar()
|
||||||
|
local ty = types.unionof(types.string, types.number, types.boolean)
|
||||||
|
return types.optional(ty)
|
||||||
|
end
|
||||||
|
-- forcing an error here to check the exact type of the union
|
||||||
|
local function ok(idx: foobar<>): never return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
TypePackMismatch* tpm = get<TypePackMismatch>(result.errors[0]);
|
||||||
|
REQUIRE(tpm);
|
||||||
|
CHECK(toString(tpm->givenTp) == "(boolean | number | string)?");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
|
||||||
{
|
{
|
||||||
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
|
|
@ -14,7 +14,7 @@ LUAU_FASTFLAG(LuauFixInfiniteRecursionInNormalization)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes)
|
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeAliases");
|
TEST_SUITE_BEGIN("TypeAliases");
|
||||||
|
|
||||||
|
@ -1197,7 +1197,7 @@ TEST_CASE_FIXTURE(Fixture, "bound_type_in_alias_segfault")
|
||||||
export type FieldConfigMap<TSource, TContext> = Map<string, FieldConfig<TSource, TContext>>
|
export type FieldConfigMap<TSource, TContext> = Map<string, FieldConfig<TSource, TContext>>
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauNewNonStrictVisitTypes)
|
if (FFlag::LuauNewNonStrictVisitTypes2)
|
||||||
LUAU_CHECK_ERROR_COUNT(2, result);
|
LUAU_CHECK_ERROR_COUNT(2, result);
|
||||||
else
|
else
|
||||||
LUAU_CHECK_NO_ERRORS(result);
|
LUAU_CHECK_NO_ERRORS(result);
|
||||||
|
|
|
@ -12,6 +12,7 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("BuiltinTests");
|
TEST_SUITE_BEGIN("BuiltinTests");
|
||||||
|
@ -472,7 +473,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_pack_reduce")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2 && FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
CHECK("{ [number]: string | string | string, n: number }" == toString(requireType("t")));
|
||||||
|
else if (FFlag::LuauSolverV2)
|
||||||
CHECK_EQ("{ [number]: string, n: number }", toString(requireType("t")));
|
CHECK_EQ("{ [number]: string, n: number }", toString(requireType("t")));
|
||||||
else
|
else
|
||||||
CHECK_EQ("{| [number]: string, n: number |}", toString(requireType("t")));
|
CHECK_EQ("{| [number]: string, n: number |}", toString(requireType("t")));
|
||||||
|
|
|
@ -24,11 +24,13 @@ LUAU_FASTINT(LuauTarjanChildLimit)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauUngeneralizedTypesForRecursiveFunctions)
|
LUAU_FASTFLAG(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauReduceUnionFollowUnionType)
|
LUAU_FASTFLAG(LuauReduceUnionFollowUnionType)
|
||||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||||
LUAU_FASTFLAG(LuauFormatUseLastPosition)
|
LUAU_FASTFLAG(LuauFormatUseLastPosition)
|
||||||
|
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferFunctions");
|
TEST_SUITE_BEGIN("TypeInferFunctions");
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ TEST_CASE_FIXTURE(Fixture, "check_function_bodies")
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
{
|
{
|
||||||
const TypePackMismatch* tm = get<TypePackMismatch>(result.errors[0]);
|
const TypePackMismatch* tm = get<TypePackMismatch>(result.errors[0]);
|
||||||
REQUIRE(tm);
|
REQUIRE_MESSAGE(tm, "Expected TypeMismatch but got " << result.errors[0]);
|
||||||
CHECK(toString(tm->wantedTp) == "number");
|
CHECK(toString(tm->wantedTp) == "number");
|
||||||
CHECK(toString(tm->givenTp) == "boolean");
|
CHECK(toString(tm->givenTp) == "boolean");
|
||||||
}
|
}
|
||||||
|
@ -3032,7 +3034,11 @@ TEST_CASE_FIXTURE(Fixture, "unifier_should_not_bind_free_types")
|
||||||
auto tm2 = get<TypePackMismatch>(result.errors[1]);
|
auto tm2 = get<TypePackMismatch>(result.errors[1]);
|
||||||
REQUIRE(tm2);
|
REQUIRE(tm2);
|
||||||
CHECK(toString(tm2->wantedTp) == "string");
|
CHECK(toString(tm2->wantedTp) == "string");
|
||||||
CHECK(toString(tm2->givenTp) == "~(false?)");
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
CHECK(toString(tm2->givenTp) == "unknown & ~(false?)");
|
||||||
|
else
|
||||||
|
CHECK(toString(tm2->givenTp) == "~(false?)");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3213,12 +3219,13 @@ TEST_CASE_FIXTURE(Fixture, "recursive_function_calls_should_not_use_the_generali
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "fuzz_unwind_mutually_recursive_union_type_func")
|
TEST_CASE_FIXTURE(Fixture, "fuzz_unwind_mutually_recursive_union_type_func")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauReduceUnionFollowUnionType, true}};
|
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauDoNotAddUpvalueTypesToLocalType, true}};
|
||||||
|
|
||||||
// This block ends up minting a type like:
|
// Previously, this block minted a type like:
|
||||||
//
|
//
|
||||||
// t2 where t1 = union<t2, t1> | union<t2, t1> | union<t2, t1> ; t2 = union<t2, t1>
|
// t2 where t1 = union<t2, t1> | union<t2, t1> | union<t2, t1> ; t2 = union<t2, t1>
|
||||||
//
|
//
|
||||||
|
// ... due to how upvalues contributed to the locally inferred types.
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local _ = ...
|
local _ = ...
|
||||||
function _()
|
function _()
|
||||||
|
@ -3226,12 +3233,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_unwind_mutually_recursive_union_type_func")
|
||||||
end
|
end
|
||||||
_[function(...) repeat until _(_[l100]) _ = _ end] += _
|
_[function(...) repeat until _(_[l100]) _ = _ end] += _
|
||||||
)");
|
)");
|
||||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
auto err0 = get<UnknownSymbol>(result.errors[0]);
|
|
||||||
CHECK(err0);
|
|
||||||
CHECK_EQ(err0->name, "l100");
|
|
||||||
auto err1 = get<NotATable>(result.errors[1]);
|
|
||||||
CHECK(err1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_pack")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_pack")
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypeInferOperators");
|
TEST_SUITE_BEGIN("TypeInferOperators");
|
||||||
|
|
||||||
|
@ -27,8 +28,18 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types")
|
||||||
local x:string|number = s
|
local x:string|number = s
|
||||||
)");
|
)");
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ(toString(*requireType("s")), "number | string");
|
|
||||||
CHECK_EQ(toString(*requireType("x")), "number | string");
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
// FIXME: Regression
|
||||||
|
CHECK("(string & ~(false?)) | number" == toString(*requireType("s")));
|
||||||
|
CHECK("number | string" == toString(*requireType("x")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ(toString(*requireType("s")), "number | string");
|
||||||
|
CHECK_EQ(toString(*requireType("x")), "number | string");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras")
|
TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras")
|
||||||
|
@ -39,8 +50,18 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras")
|
||||||
local y = x or "s"
|
local y = x or "s"
|
||||||
)");
|
)");
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ(toString(*requireType("s")), "number | string");
|
|
||||||
CHECK_EQ(toString(*requireType("y")), "number | string");
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
// FIXME: Regression.
|
||||||
|
CHECK("(string & ~(false?)) | number" == toString(*requireType("s")));
|
||||||
|
CHECK("number | string | string" == toString(*requireType("y")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ(toString(*requireType("s")), "number | string");
|
||||||
|
CHECK_EQ(toString(*requireType("y")), "number | string");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union")
|
TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union")
|
||||||
|
@ -50,7 +71,14 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union")
|
||||||
local x:string = s
|
local x:string = s
|
||||||
)");
|
)");
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ(*requireType("s"), *builtinTypes->stringType);
|
|
||||||
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
{
|
||||||
|
// FIXME: Regression
|
||||||
|
CHECK("(string & ~(false?)) | string" == toString(requireType("s")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CHECK_EQ(*requireType("s"), *builtinTypes->stringType);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "and_does_not_always_add_boolean")
|
TEST_CASE_FIXTURE(Fixture, "and_does_not_always_add_boolean")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Normalize.h"
|
#include "Luau/Normalize.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
|
#include "Luau/Type.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
|
|
||||||
#include "Fixture.h"
|
#include "Fixture.h"
|
||||||
|
@ -11,10 +12,12 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||||
LUAU_FASTFLAG(LuauIntersectNotNil)
|
LUAU_FASTFLAG(LuauIntersectNotNil)
|
||||||
LUAU_FASTFLAG(LuauSkipNoRefineDuringRefinement)
|
LUAU_FASTFLAG(LuauSkipNoRefineDuringRefinement)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
|
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
|
||||||
LUAU_FASTFLAG(LuauSimplyRefineNotNil)
|
LUAU_FASTFLAG(LuauSimplyRefineNotNil)
|
||||||
LUAU_FASTFLAG(LuauWeakNilRefinementType)
|
LUAU_FASTFLAG(LuauWeakNilRefinementType)
|
||||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||||
|
LUAU_FASTFLAG(LuauSimplificationTableExternType)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -113,10 +116,18 @@ struct RefinementExternTypeFixture : BuiltinsFixture
|
||||||
{"Position", Property{vec3}},
|
{"Position", Property{vec3}},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TypeId optionalPart = arena.addType(UnionType{{part, builtinTypes->nilType}});
|
||||||
|
TypeId weldConstraint = frontend.globals.globalTypes.addType(ExternType{"WeldConstraint", {}, inst, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
getMutable<ExternType>(weldConstraint)->props = {
|
||||||
|
{"Part0", Property{optionalPart}},
|
||||||
|
{"Part1", Property{optionalPart}},
|
||||||
|
};
|
||||||
|
|
||||||
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["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};
|
||||||
|
|
||||||
for (const auto& [name, ty] : frontend.globals.globalScope->exportedTypeBindings)
|
for (const auto& [name, ty] : frontend.globals.globalScope->exportedTypeBindings)
|
||||||
persist(ty.type);
|
persist(ty.type);
|
||||||
|
@ -752,11 +763,22 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonoptional_type_can_narrow_to_nil_if_sense_
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil"
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
CHECK_EQ("string", toString(requireTypeAtPosition({6, 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_EQ("nil", toString(requireTypeAtPosition({10, 24}))); // equivalent to type(v) == "nil"
|
CHECK("nil & string & unknown & unknown" == toString(requireTypeAtPosition({10, 24}))); // equivalent to type(v) == "nil"
|
||||||
CHECK_EQ("string", toString(requireTypeAtPosition({12, 24}))); // equivalent to type(v) ~= "nil"
|
CHECK("string & unknown & unknown & ~nil" == toString(requireTypeAtPosition({12, 24}))); // equivalent to type(v) ~= "nil"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24}))); // type(v) == "nil"
|
||||||
|
CHECK_EQ("string", toString(requireTypeAtPosition({6, 24}))); // type(v) ~= "nil"
|
||||||
|
|
||||||
|
CHECK_EQ("nil", toString(requireTypeAtPosition({10, 24}))); // equivalent to type(v) == "nil"
|
||||||
|
CHECK_EQ("string", toString(requireTypeAtPosition({12, 24}))); // equivalent to type(v) ~= "nil"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "typeguard_not_to_be_string")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "typeguard_not_to_be_string")
|
||||||
|
@ -1578,9 +1600,7 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "refine_param_of_type_folder_or_p
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "isa_type_refinement_must_be_known_ahead_of_time")
|
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "isa_type_refinement_must_be_known_ahead_of_time")
|
||||||
{
|
{
|
||||||
// CLI-115087 - The new solver does not consistently combine tables with
|
ScopedFastFlag sff{FFlag::LuauSimplificationTableExternType, true};
|
||||||
// class types when they appear in the upper bounds of a free type.
|
|
||||||
DOES_NOT_PASS_NEW_SOLVER_GUARD();
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(x): Instance
|
local function f(x): Instance
|
||||||
|
@ -1596,8 +1616,41 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "isa_type_refinement_must_be_know
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
CHECK_EQ("Instance", toString(requireTypeAtPosition({3, 28})));
|
if (FFlag::LuauSolverV2)
|
||||||
CHECK_EQ("Instance", toString(requireTypeAtPosition({5, 28})));
|
{
|
||||||
|
CHECK_EQ("t1 where t1 = Instance & { read IsA: (t1, string) -> (unknown, ...unknown) }", toString(requireTypeAtPosition({3, 28})));
|
||||||
|
CHECK_EQ("t1 where t1 = Instance & { read IsA: (t1, string) -> (unknown, ...unknown) }", toString(requireTypeAtPosition({5, 28})));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ("Instance", toString(requireTypeAtPosition({3, 28})));
|
||||||
|
CHECK_EQ("Instance", toString(requireTypeAtPosition({5, 28})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "asserting_optional_properties_should_not_refine_extern_types_to_never")
|
||||||
|
{
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local weld: WeldConstraint = nil :: any
|
||||||
|
assert(weld.Part1)
|
||||||
|
print(weld) -- hover type incorrectly becomes `never`
|
||||||
|
assert(weld.Part1.Name == "RootPart")
|
||||||
|
local part1 = assert(weld.Part1)
|
||||||
|
local pos = part1.Position
|
||||||
|
)");
|
||||||
|
|
||||||
|
if (FFlag::LuauSolverV2)
|
||||||
|
{
|
||||||
|
// CLI-142467: this is a major regression that we need to address.
|
||||||
|
CHECK_EQ("never", toString(requireTypeAtPosition({3, 15})));
|
||||||
|
CHECK_EQ("any", toString(requireTypeAtPosition({6, 29})));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_EQ("WeldConstraint", toString(requireTypeAtPosition({3, 15})));
|
||||||
|
CHECK_EQ("Vector3", toString(requireTypeAtPosition({6, 29})));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "x_is_not_instance_or_else_not_part")
|
TEST_CASE_FIXTURE(RefinementExternTypeFixture, "x_is_not_instance_or_else_not_part")
|
||||||
|
@ -1605,6 +1658,7 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "x_is_not_instance_or_else_not_pa
|
||||||
// CLI-117135 - RefinementTests.x_is_not_instance_or_else_not_part not correctly applying refinements to a function parameter
|
// CLI-117135 - RefinementTests.x_is_not_instance_or_else_not_part not correctly applying refinements to a function parameter
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(x: Part | Folder | string)
|
local function f(x: Part | Folder | string)
|
||||||
if typeof(x) ~= "Instance" or not x:IsA("Part") then
|
if typeof(x) ~= "Instance" or not x:IsA("Part") then
|
||||||
|
@ -1827,6 +1881,7 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "refine_a_param_that_got_resolved
|
||||||
// CLI-117134 - Applying a refinement causes an optional value access error.
|
// CLI-117134 - Applying a refinement causes an optional value access error.
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type Id<T> = T
|
type Id<T> = T
|
||||||
|
|
||||||
|
@ -2447,7 +2502,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "remove_recursive_upper_bound_when_generalizi
|
||||||
end
|
end
|
||||||
)"));
|
)"));
|
||||||
|
|
||||||
CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24})));
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
CHECK_EQ("nil & string & unknown", toString(requireTypeAtPosition({4, 24})));
|
||||||
|
else
|
||||||
|
CHECK_EQ("nil", toString(requireTypeAtPosition({4, 24})));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "nonnil_refinement_on_generic")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "nonnil_refinement_on_generic")
|
||||||
|
|
|
@ -22,7 +22,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
||||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
LUAU_FASTFLAG(LuauFollowTableFreeze)
|
|
||||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||||
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
|
@ -4723,10 +4722,10 @@ TEST_CASE_FIXTURE(Fixture, "refined_thing_can_be_an_array")
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
if (FFlag::LuauSolverV2)
|
if (FFlag::LuauSolverV2 && !FFlag::DebugLuauGreedyGeneralization)
|
||||||
{
|
{
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_CHECK_ERROR_COUNT(1, result);
|
||||||
CHECK(get<NotATable>(result.errors[0]));
|
LUAU_CHECK_ERROR(result, NotATable);
|
||||||
CHECK_EQ("(unknown, *error-type*) -> *error-type*", toString(requireType("foo")));
|
CHECK_EQ("(unknown, *error-type*) -> *error-type*", toString(requireType("foo")));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -5335,7 +5334,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_freeze_musnt_assert")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::LuauFollowTableFreeze, true},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto result = check(R"(
|
auto result = check(R"(
|
||||||
|
|
|
@ -2023,7 +2023,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_assert_table_freeze_constraint_solving"
|
||||||
LUAU_REQUIRE_NO_ERROR(results, ConstraintSolvingIncompleteError);
|
LUAU_REQUIRE_NO_ERROR(results, ConstraintSolvingIncompleteError);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "konnichiwa" * doctest::timeout(0.25))
|
TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_unification_aborts_eventually" * doctest::timeout(0.25))
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs[] = {
|
ScopedFastFlag sffs[] = {
|
||||||
{FFlag::LuauSolverV2, false},
|
{FFlag::LuauSolverV2, false},
|
||||||
|
|
|
@ -13,6 +13,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||||
|
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("TypePackTests");
|
TEST_SUITE_BEGIN("TypePackTests");
|
||||||
|
|
||||||
|
@ -96,7 +97,10 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function")
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
CHECK_EQ("<a, b..., c...>((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply")));
|
if (FFlag::DebugLuauGreedyGeneralization)
|
||||||
|
CHECK_EQ("<a, b..., c...>((c...) -> (b...), (a) -> (c...), a) -> (b...)", toString(requireType("apply")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("<a, b..., c...>((b...) -> (c...), (a) -> (b...), a) -> (c...)", toString(requireType("apply")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "return_type_should_be_empty_if_nothing_is_returned")
|
TEST_CASE_FIXTURE(Fixture, "return_type_should_be_empty_if_nothing_is_returned")
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
||||||
|
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||||
|
LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
|
@ -263,6 +265,8 @@ TEST_CASE_FIXTURE(TypeStateFixture, "local_assigned_in_only_one_branch_that_fall
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_and_else_branch_also_assigns_but_is_met_with_return")
|
TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_and_else_branch_also_assigns_but_is_met_with_return")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x = nil
|
local x = nil
|
||||||
if math.random() > 0.5 then
|
if math.random() > 0.5 then
|
||||||
|
@ -275,11 +279,13 @@ TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_and_else_branch_also_as
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK("number?" == toString(requireType("y")));
|
CHECK("number" == toString(requireType("y")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_but_is_met_with_return_and_else_branch_assigns")
|
TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_but_is_met_with_return_and_else_branch_assigns")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x = nil
|
local x = nil
|
||||||
if math.random() > 0.5 then
|
if math.random() > 0.5 then
|
||||||
|
@ -292,7 +298,7 @@ TEST_CASE_FIXTURE(TypeStateFixture, "then_branch_assigns_but_is_met_with_return_
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK("string?" == toString(requireType("y")));
|
CHECK("string" == toString(requireType("y")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TypeStateFixture, "invalidate_type_refinements_upon_assignments")
|
TEST_CASE_FIXTURE(TypeStateFixture, "invalidate_type_refinements_upon_assignments")
|
||||||
|
@ -338,8 +344,9 @@ TEST_CASE_FIXTURE(TypeStateFixture, "local_t_is_assigned_a_fresh_table_with_x_as
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_are_unions_of_all_assignments")
|
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_do_not_mutate_upvalue_type")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x = nil
|
local x = nil
|
||||||
|
|
||||||
|
@ -352,12 +359,16 @@ TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_are_unions_of_all_assignmen
|
||||||
f()
|
f()
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK("(number | string)?" == toString(requireTypeAtPosition({4, 18})));
|
auto err = get<TypeMismatch>(result.errors[0]);
|
||||||
|
CHECK_EQ("number?", toString(err->wantedType));
|
||||||
|
CHECK_EQ("string", toString(err->givenType));
|
||||||
|
CHECK("number?" == toString(requireTypeAtPosition({4, 18})));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_are_unions_of_all_assignments_2")
|
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_do_not_mutate_upvalue_type_2")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t = {x = nil}
|
local t = {x = nil}
|
||||||
|
|
||||||
|
@ -370,9 +381,13 @@ TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_are_unions_of_all_assignmen
|
||||||
f()
|
f()
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK("{ x: nil } | { x: number } | { x: string }" == toString(requireTypeAtPosition({4, 18}), {true}));
|
auto err = get<TypeMismatch>(result.errors[0]);
|
||||||
CHECK("(number | string)?" == toString(requireTypeAtPosition({4, 20})));
|
CHECK_EQ("t | { x: number }", toString(err->wantedType));
|
||||||
|
CHECK_EQ("{ x: string }", toString(err->givenType));
|
||||||
|
|
||||||
|
CHECK("{ x: nil } | { x: number }" == toString(requireTypeAtPosition({4, 18}), {true}));
|
||||||
|
CHECK("number?" == toString(requireTypeAtPosition({4, 20})));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions")
|
TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions")
|
||||||
|
@ -555,4 +570,280 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_normalized_type_variables_are_bad" *
|
||||||
)"));
|
)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1547_simple")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local rand = 0
|
||||||
|
|
||||||
|
function a()
|
||||||
|
rand = (rand % 4) + 1;
|
||||||
|
end
|
||||||
|
)"));
|
||||||
|
|
||||||
|
auto randTy = getType("rand");
|
||||||
|
REQUIRE(randTy);
|
||||||
|
CHECK_EQ("number", toString(*randTy));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1547")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local rand = 0
|
||||||
|
|
||||||
|
function a()
|
||||||
|
rand = (rand % 4) + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
function b()
|
||||||
|
rand = math.max(rand - 1, 0);
|
||||||
|
end
|
||||||
|
)"));
|
||||||
|
|
||||||
|
auto randTy = getType("rand");
|
||||||
|
REQUIRE(randTy);
|
||||||
|
CHECK_EQ("number", toString(*randTy));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "modify_captured_table_field")
|
||||||
|
{
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local state = { x = 0 }
|
||||||
|
function incr()
|
||||||
|
state.x = state.x + 1
|
||||||
|
end
|
||||||
|
)"));
|
||||||
|
|
||||||
|
auto randTy = getType("state");
|
||||||
|
REQUIRE(randTy);
|
||||||
|
CHECK_EQ("{ x: number }", toString(*randTy, {true}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "oss_1561")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||||
|
|
||||||
|
loadDefinition(R"(
|
||||||
|
declare class Vector3
|
||||||
|
X: number
|
||||||
|
Y: number
|
||||||
|
Z: number
|
||||||
|
end
|
||||||
|
|
||||||
|
declare Vector3: {
|
||||||
|
new: (number?, number?, number?) -> Vector3
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local targetVelocity: Vector3 = Vector3.new()
|
||||||
|
function set2D(X: number, Y: number)
|
||||||
|
targetVelocity = Vector3.new(X, Y, targetVelocity.Z)
|
||||||
|
end
|
||||||
|
)"));
|
||||||
|
|
||||||
|
CHECK_EQ("(number, number) -> ()", toString(requireType("set2D")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "oss_1575")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
local flag = true
|
||||||
|
local function Flip()
|
||||||
|
flag = not flag
|
||||||
|
end
|
||||||
|
)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "capture_upvalue_in_returned_function")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDoNotAddUpvalueTypesToLocalType, true};
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||||
|
function def()
|
||||||
|
local i : number = 0
|
||||||
|
local function Counter()
|
||||||
|
i = i + 1
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
return Counter
|
||||||
|
end
|
||||||
|
)"));
|
||||||
|
CHECK_EQ("() -> () -> number", toString(requireType("def")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_else_branch")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
local x
|
||||||
|
local coinflip : () -> boolean = (nil :: any)
|
||||||
|
|
||||||
|
if coinflip () then
|
||||||
|
x = "I win."
|
||||||
|
else
|
||||||
|
error("You lose.")
|
||||||
|
end
|
||||||
|
|
||||||
|
print(x)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("string", toString(requireTypeAtPosition({11, 14})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_if_branch")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
local x
|
||||||
|
local coinflip : () -> boolean = (nil :: any)
|
||||||
|
|
||||||
|
if coinflip () then
|
||||||
|
error("You lose.")
|
||||||
|
else
|
||||||
|
x = "I win."
|
||||||
|
end
|
||||||
|
|
||||||
|
print(x)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("string", toString(requireTypeAtPosition({11, 14})));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
type Payload = { payload: number }
|
||||||
|
|
||||||
|
local function decode(s: string): Payload?
|
||||||
|
return (nil :: any)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function decodeEx(s: string): Payload
|
||||||
|
local p = decode(s)
|
||||||
|
if not p then
|
||||||
|
error("failed to decode payload!!!")
|
||||||
|
end
|
||||||
|
return p
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "refinement_through_erroring_in_loop")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, true},
|
||||||
|
{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
|
||||||
|
local x = nil
|
||||||
|
|
||||||
|
while math.random() > 0.5 do
|
||||||
|
x = 42
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print(x)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
// CLI-142447: This should probably be `nil` given that the `while` loop
|
||||||
|
// unconditionally returns, but `number?` is sound, if incomplete.
|
||||||
|
CHECK_EQ("number?", toString(requireTypeAtPosition({10, 14})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "type_refinement_in_loop")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
local function onEachString(t: { string | number })
|
||||||
|
for _, v in t do
|
||||||
|
if type(v) ~= "string" then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
print(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("number | string", toString(requireTypeAtPosition({4, 24})));
|
||||||
|
CHECK_EQ("string", toString(requireTypeAtPosition({7, 22})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "throw_in_if_branch_and_do_nothing_in_else")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, true},
|
||||||
|
{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
local x
|
||||||
|
local coinflip : () -> boolean = (nil :: any)
|
||||||
|
|
||||||
|
if coinflip () then
|
||||||
|
error("You lose.")
|
||||||
|
else
|
||||||
|
end
|
||||||
|
|
||||||
|
print(x)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("nil", toString(requireTypeAtPosition({10, 14})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "assign_in_an_if_branch_without_else")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sffs[] = {
|
||||||
|
{FFlag::LuauSolverV2, true},
|
||||||
|
{FFlag::LuauDfgIfBlocksShouldRespectControlFlow, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
--!strict
|
||||||
|
local x
|
||||||
|
local coinflip : () -> boolean = (nil :: any)
|
||||||
|
|
||||||
|
if coinflip () then
|
||||||
|
x = "I win."
|
||||||
|
end
|
||||||
|
|
||||||
|
print(x)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("string?", toString(requireTypeAtPosition({9, 14})));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
|
@ -1 +0,0 @@
|
||||||
return {"result from global_library"}
|
|
|
@ -1 +0,0 @@
|
||||||
return {"result from library"}
|
|
Loading…
Add table
Reference in a new issue