Merge branch 'upstream' into merge

This commit is contained in:
Alexander Youngblood 2024-11-22 12:39:19 -08:00
commit 4fa6e97caa
67 changed files with 783 additions and 407 deletions

View file

@ -295,11 +295,25 @@ private:
Inference check(const ScopePtr& scope, AstExprFunction* func, std::optional<TypeId> expectedType, bool generalize);
Inference check(const ScopePtr& scope, AstExprUnary* unary);
Inference check(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType);
Inference checkAstExprBinary(
const ScopePtr& scope,
const Location& location,
AstExprBinary::Op op,
AstExpr* left,
AstExpr* right,
std::optional<TypeId> expectedType
);
Inference check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType);
Inference check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert);
Inference check(const ScopePtr& scope, AstExprInterpString* interpString);
Inference check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType);
std::tuple<TypeId, TypeId, RefinementId> checkBinary(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType);
std::tuple<TypeId, TypeId, RefinementId> checkBinary(
const ScopePtr& scope,
AstExprBinary::Op op,
AstExpr* left,
AstExpr* right,
std::optional<TypeId> expectedType
);
void visitLValue(const ScopePtr& scope, AstExpr* expr, TypeId rhsType);
void visitLValue(const ScopePtr& scope, AstExprLocal* local, TypeId rhsType);

View file

@ -59,6 +59,25 @@ struct HashInstantiationSignature
size_t operator()(const InstantiationSignature& signature) const;
};
struct TablePropLookupResult
{
// What types are we blocked on for determining this type?
std::vector<TypeId> blockedTypes;
// The type of the property (if we were able to determine it).
std::optional<TypeId> propType;
// Whether or not this is _definitely_ derived as the result of an indexer.
// We use this to determine whether or not code like:
//
// t.lol = nil;
//
// ... is legal. If `t: { [string]: ~nil }` then this is legal as
// there's no guarantee on whether "lol" specifically exists.
// However, if `t: { lol: ~nil }`, then we cannot allow assignment as
// that would remove "lol" from the table entirely.
bool isIndex = false;
};
struct ConstraintSolver
{
NotNull<TypeArena> arena;
@ -211,7 +230,7 @@ public:
// for a, ... in next_function, t, ... do
bool tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy, const IterableConstraint& c, NotNull<const Constraint> constraint);
std::pair<std::vector<TypeId>, std::optional<TypeId>> lookupTableProp(
TablePropLookupResult lookupTableProp(
NotNull<const Constraint> constraint,
TypeId subjectType,
const std::string& propName,
@ -219,7 +238,8 @@ public:
bool inConditional = false,
bool suppressSimplification = false
);
std::pair<std::vector<TypeId>, std::optional<TypeId>> lookupTableProp(
TablePropLookupResult lookupTableProp(
NotNull<const Constraint> constraint,
TypeId subjectType,
const std::string& propName,

View file

@ -84,7 +84,6 @@ struct DfgScope
DfgScope* parent;
ScopeType scopeType;
Location location;
using Bindings = DenseHashMap<Symbol, const Def*>;
using Props = DenseHashMap<const Def*, std::unordered_map<std::string, const Def*>>;
@ -156,7 +155,7 @@ private:
DenseHashMap<Symbol, FunctionCapture> captures{Symbol{}};
void resolveCaptures();
DfgScope* makeChildScope(Location loc, DfgScope::ScopeType scopeType = DfgScope::Linear);
DfgScope* makeChildScope(DfgScope::ScopeType scopeType = DfgScope::Linear);
void join(DfgScope* p, DfgScope* a, DfgScope* b);
void joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b);

View file

@ -53,7 +53,7 @@ struct Replacer : Substitution
};
// A substitution which replaces generic functions by monomorphic functions
struct Instantiation2 : Substitution
struct Instantiation2 final : Substitution
{
// Mapping from generic types to free types to be used in instantiation.
DenseHashMap<TypeId, TypeId> genericSubstitutions{nullptr};

View file

@ -135,9 +135,6 @@ struct Module
TypePackId returnType = nullptr;
std::unordered_map<Name, TypeFun> exportedTypeBindings;
// We also need to keep DFG data alive between runs
std::shared_ptr<DataFlowGraph> dataFlowGraph = nullptr;
std::vector<std::unique_ptr<DfgScope>> dfgScopes;
bool hasModuleScope() const;
ScopePtr getModuleScope() const;

View file

@ -762,7 +762,7 @@ struct NegationType
TypeId ty;
};
using ErrorType = Unifiable::Error;
using ErrorType = Unifiable::Error<TypeId>;
using TypeVariant = Unifiable::Variant<
TypeId,

View file

@ -52,7 +52,7 @@ struct GenericTypePack
};
using BoundTypePack = Unifiable::Bound<TypePackId>;
using ErrorTypePack = Unifiable::Error;
using ErrorTypePack = Unifiable::Error<TypePackId>;
using TypePackVariant =
Unifiable::Variant<TypePackId, FreeTypePack, GenericTypePack, TypePack, VariadicTypePack, BlockedTypePack, TypeFunctionInstanceTypePack>;

View file

@ -3,6 +3,7 @@
#include "Luau/Variant.h"
#include <optional>
#include <string>
namespace Luau
@ -94,19 +95,29 @@ struct Bound
Id boundTo;
};
template<typename Id>
struct Error
{
// This constructor has to be public, since it's used in Type and TypePack,
// but shouldn't be called directly. Please use errorRecoveryType() instead.
Error();
explicit Error();
explicit Error(Id synthetic)
: synthetic{synthetic}
{
}
int index;
// This is used to create an error that can be rendered out using this field
// as appropriate metadata for communicating it to the user.
std::optional<Id> synthetic;
private:
static int nextIndex;
};
template<typename Id, typename... Value>
using Variant = Luau::Variant<Bound<Id>, Error, Value...>;
using Variant = Luau::Variant<Bound<Id>, Error<Id>, Value...>;
} // namespace Luau::Unifiable

View file

@ -10,7 +10,6 @@
#include "Type.h"
LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauBoundLazyTypes2)
LUAU_FASTFLAG(LuauSolverV2)
namespace Luau
@ -190,7 +189,7 @@ struct GenericTypeVisitor
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const Unifiable::Error& etp)
virtual bool visit(TypePackId tp, const ErrorTypePack& etp)
{
return visit(tp);
}
@ -461,7 +460,7 @@ struct GenericTypeVisitor
else if (auto gtv = get<GenericTypePack>(tp))
visit(tp, *gtv);
else if (auto etv = get<Unifiable::Error>(tp))
else if (auto etv = get<ErrorTypePack>(tp))
visit(tp, *etv);
else if (auto pack = get<TypePack>(tp))

View file

@ -257,8 +257,7 @@ private:
LUAU_ASSERT(!"Item holds neither TypeId nor TypePackId when enqueuing its children?");
}
// ErrorType and ErrorTypePack is an alias to this type.
void cloneChildren(Unifiable::Error* t)
void cloneChildren(ErrorType* t)
{
// noop.
}
@ -428,6 +427,11 @@ private:
t->boundTo = shallowClone(t->boundTo);
}
void cloneChildren(ErrorTypePack* t)
{
// noop.
}
void cloneChildren(VariadicTypePack* t)
{
t->ty = shallowClone(t->ty);

View file

@ -69,13 +69,11 @@ struct TypeGuard
std::string type;
};
static std::optional<TypeGuard> matchTypeGuard(const AstExprBinary* binary)
static std::optional<TypeGuard> matchTypeGuard(const AstExprBinary::Op op, AstExpr* left, AstExpr* right)
{
if (binary->op != AstExprBinary::CompareEq && binary->op != AstExprBinary::CompareNe)
if (op != AstExprBinary::CompareEq && op != AstExprBinary::CompareNe)
return std::nullopt;
AstExpr* left = binary->left;
AstExpr* right = binary->right;
if (right->is<AstExprCall>())
std::swap(left, right);
@ -1459,8 +1457,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatAssign* ass
ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatCompoundAssign* assign)
{
AstExprBinary binop = AstExprBinary{assign->location, assign->op, assign->var, assign->value};
TypeId resultTy = check(scope, &binop).ty;
TypeId resultTy = checkAstExprBinary(scope, assign->location, assign->op, assign->var, assign->value, std::nullopt).ty;
module->astCompoundAssignResultTypes[assign] = resultTy;
TypeId lhsType = check(scope, assign->var).ty;
@ -2437,63 +2434,75 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprUnary* unary)
Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType)
{
auto [leftType, rightType, refinement] = checkBinary(scope, binary, expectedType);
return checkAstExprBinary(scope, binary->location, binary->op, binary->left, binary->right, expectedType);
}
switch (binary->op)
Inference ConstraintGenerator::checkAstExprBinary(
const ScopePtr& scope,
const Location& location,
AstExprBinary::Op op,
AstExpr* left,
AstExpr* right,
std::optional<TypeId> expectedType
)
{
auto [leftType, rightType, refinement] = checkBinary(scope, op, left, right, expectedType);
switch (op)
{
case AstExprBinary::Op::Add:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().addFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().addFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::Sub:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().subFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().subFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::Mul:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().mulFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().mulFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::Div:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().divFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().divFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::FloorDiv:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().idivFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().idivFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::Pow:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().powFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().powFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::Mod:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().modFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().modFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::Concat:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().concatFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().concatFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::And:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().andFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().andFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::Or:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().orFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().orFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::CompareLt:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().ltFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().ltFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::CompareGe:
@ -2503,13 +2512,13 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar
{rightType, leftType}, // lua decided that `__ge(a, b)` is instead just `__lt(b, a)`
{},
scope,
binary->location
location
);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::CompareLe:
{
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().leFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().leFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::CompareGt:
@ -2519,15 +2528,15 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar
{rightType, leftType}, // lua decided that `__gt(a, b)` is instead just `__le(b, a)`
{},
scope,
binary->location
location
);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::CompareEq:
case AstExprBinary::Op::CompareNe:
{
DefId leftDef = dfg->getDef(binary->left);
DefId rightDef = dfg->getDef(binary->right);
DefId leftDef = dfg->getDef(left);
DefId rightDef = dfg->getDef(right);
bool leftSubscripted = containsSubscriptedDefinition(leftDef);
bool rightSubscripted = containsSubscriptedDefinition(rightDef);
@ -2536,11 +2545,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar
// we cannot add nil in this case because then we will blindly accept comparisons that we should not.
}
else if (leftSubscripted)
leftType = makeUnion(scope, binary->location, leftType, builtinTypes->nilType);
leftType = makeUnion(scope, location, leftType, builtinTypes->nilType);
else if (rightSubscripted)
rightType = makeUnion(scope, binary->location, rightType, builtinTypes->nilType);
rightType = makeUnion(scope, location, rightType, builtinTypes->nilType);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().eqFunc, {leftType, rightType}, {}, scope, binary->location);
TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().eqFunc, {leftType, rightType}, {}, scope, location);
return Inference{resultType, std::move(refinement)};
}
case AstExprBinary::Op::Op__Count:
@ -2586,44 +2595,46 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprInterpString*
std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
const ScopePtr& scope,
AstExprBinary* binary,
AstExprBinary::Op op,
AstExpr* left,
AstExpr* right,
std::optional<TypeId> expectedType
)
{
if (binary->op == AstExprBinary::And)
if (op == AstExprBinary::And)
{
std::optional<TypeId> relaxedExpectedLhs;
if (expectedType)
relaxedExpectedLhs = arena->addType(UnionType{{builtinTypes->falsyType, *expectedType}});
auto [leftType, leftRefinement] = check(scope, binary->left, relaxedExpectedLhs);
auto [leftType, leftRefinement] = check(scope, left, relaxedExpectedLhs);
ScopePtr rightScope = childScope(binary->right, scope);
applyRefinements(rightScope, binary->right->location, leftRefinement);
auto [rightType, rightRefinement] = check(rightScope, binary->right, expectedType);
ScopePtr rightScope = childScope(right, scope);
applyRefinements(rightScope, right->location, leftRefinement);
auto [rightType, rightRefinement] = check(rightScope, right, expectedType);
return {leftType, rightType, refinementArena.conjunction(leftRefinement, rightRefinement)};
}
else if (binary->op == AstExprBinary::Or)
else if (op == AstExprBinary::Or)
{
std::optional<TypeId> relaxedExpectedLhs;
if (expectedType)
relaxedExpectedLhs = arena->addType(UnionType{{builtinTypes->falsyType, *expectedType}});
auto [leftType, leftRefinement] = check(scope, binary->left, relaxedExpectedLhs);
auto [leftType, leftRefinement] = check(scope, left, relaxedExpectedLhs);
ScopePtr rightScope = childScope(binary->right, scope);
applyRefinements(rightScope, binary->right->location, refinementArena.negation(leftRefinement));
auto [rightType, rightRefinement] = check(rightScope, binary->right, expectedType);
ScopePtr rightScope = childScope(right, scope);
applyRefinements(rightScope, right->location, refinementArena.negation(leftRefinement));
auto [rightType, rightRefinement] = check(rightScope, right, expectedType);
return {leftType, rightType, refinementArena.disjunction(leftRefinement, rightRefinement)};
}
else if (auto typeguard = matchTypeGuard(binary))
else if (auto typeguard = matchTypeGuard(op, left, right))
{
TypeId leftType = check(scope, binary->left).ty;
TypeId rightType = check(scope, binary->right).ty;
TypeId leftType = check(scope, left).ty;
TypeId rightType = check(scope, right).ty;
const RefinementKey* key = dfg->getRefinementKey(typeguard->target);
if (!key)
@ -2665,24 +2676,24 @@ std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
}
RefinementId proposition = refinementArena.proposition(key, discriminantTy);
if (binary->op == AstExprBinary::CompareEq)
if (op == AstExprBinary::CompareEq)
return {leftType, rightType, proposition};
else if (binary->op == AstExprBinary::CompareNe)
else if (op == AstExprBinary::CompareNe)
return {leftType, rightType, refinementArena.negation(proposition)};
else
ice->ice("matchTypeGuard should only return a Some under `==` or `~=`!");
}
else if (binary->op == AstExprBinary::CompareEq || binary->op == AstExprBinary::CompareNe)
else if (op == AstExprBinary::CompareEq || op == AstExprBinary::CompareNe)
{
// We are checking a binary expression of the form a op b
// Just because a op b is epxected to return a bool, doesn't mean a, b are expected to be bools too
TypeId leftType = check(scope, binary->left, {}, true).ty;
TypeId rightType = check(scope, binary->right, {}, true).ty;
TypeId leftType = check(scope, left, {}, true).ty;
TypeId rightType = check(scope, right, {}, true).ty;
RefinementId leftRefinement = refinementArena.proposition(dfg->getRefinementKey(binary->left), rightType);
RefinementId rightRefinement = refinementArena.proposition(dfg->getRefinementKey(binary->right), leftType);
RefinementId leftRefinement = refinementArena.proposition(dfg->getRefinementKey(left), rightType);
RefinementId rightRefinement = refinementArena.proposition(dfg->getRefinementKey(right), leftType);
if (binary->op == AstExprBinary::CompareNe)
if (op == AstExprBinary::CompareNe)
{
leftRefinement = refinementArena.negation(leftRefinement);
rightRefinement = refinementArena.negation(rightRefinement);
@ -2692,8 +2703,8 @@ std::tuple<TypeId, TypeId, RefinementId> ConstraintGenerator::checkBinary(
}
else
{
TypeId leftType = check(scope, binary->left).ty;
TypeId rightType = check(scope, binary->right).ty;
TypeId leftType = check(scope, left).ty;
TypeId rightType = check(scope, right).ty;
return {leftType, rightType, nullptr};
}
}

View file

@ -34,6 +34,7 @@ LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500)
LUAU_FASTFLAGVARIABLE(LuauRemoveNotAnyHack)
LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification)
LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
namespace Luau
{
@ -1501,7 +1502,8 @@ bool ConstraintSolver::tryDispatch(const HasPropConstraint& c, NotNull<const Con
}
}
auto [blocked, result] = lookupTableProp(constraint, subjectType, c.prop, c.context, c.inConditional, c.suppressSimplification);
// It doesn't matter whether this type came from an indexer or not.
auto [blocked, result, _isIndex] = lookupTableProp(constraint, subjectType, c.prop, c.context, c.inConditional, c.suppressSimplification);
if (!blocked.empty())
{
for (TypeId blocked : blocked)
@ -1783,7 +1785,7 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
// Handle the case that lhsType is a table that already has the property or
// a matching indexer. This also handles unions and intersections.
const auto [blocked, maybeTy] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
const auto [blocked, maybeTy, isIndex] = lookupTableProp(constraint, lhsType, propName, ValueContext::LValue);
if (!blocked.empty())
{
for (TypeId t : blocked)
@ -1793,8 +1795,12 @@ bool ConstraintSolver::tryDispatch(const AssignPropConstraint& c, NotNull<const
if (maybeTy)
{
const TypeId propTy = *maybeTy;
bind(constraint, c.propType, propTy);
TypeId propTy = *maybeTy;
bind(
constraint,
c.propType,
isIndex && FFlag::LuauAllowNilAssignmentToIndexer ? arena->addType(UnionType{{propTy, builtinTypes->nilType}}) : propTy
);
unify(constraint, rhsType, propTy);
return true;
}
@ -1888,7 +1894,12 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
{
unify(constraint, indexType, lhsTable->indexer->indexType);
unify(constraint, rhsType, lhsTable->indexer->indexResultType);
bind(constraint, c.propType, lhsTable->indexer->indexResultType);
bind(
constraint,
c.propType,
FFlag::LuauAllowNilAssignmentToIndexer ? arena->addType(UnionType{{lhsTable->indexer->indexResultType, builtinTypes->nilType}})
: lhsTable->indexer->indexResultType
);
return true;
}
@ -1937,7 +1948,12 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNull<const
{
unify(constraint, indexType, lhsClass->indexer->indexType);
unify(constraint, rhsType, lhsClass->indexer->indexResultType);
bind(constraint, c.propType, lhsClass->indexer->indexResultType);
bind(
constraint,
c.propType,
FFlag::LuauAllowNilAssignmentToIndexer ? arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}})
: lhsClass->indexer->indexResultType
);
return true;
}
@ -2360,7 +2376,7 @@ NotNull<const Constraint> ConstraintSolver::unpackAndAssign(
return c;
}
std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTableProp(
TablePropLookupResult ConstraintSolver::lookupTableProp(
NotNull<const Constraint> constraint,
TypeId subjectType,
const std::string& propName,
@ -2373,7 +2389,7 @@ std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTa
return lookupTableProp(constraint, subjectType, propName, context, inConditional, suppressSimplification, seen);
}
std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTableProp(
TablePropLookupResult ConstraintSolver::lookupTableProp(
NotNull<const Constraint> constraint,
TypeId subjectType,
const std::string& propName,
@ -2413,7 +2429,7 @@ std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTa
}
if (ttv->indexer && maybeString(ttv->indexer->indexType))
return {{}, ttv->indexer->indexResultType};
return {{}, ttv->indexer->indexResultType, /* isIndex = */ true};
if (ttv->state == TableState::Free)
{
@ -2455,9 +2471,9 @@ std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTa
}
else if (auto mt = get<MetatableType>(subjectType); mt && context == ValueContext::RValue)
{
auto [blocked, result] = lookupTableProp(constraint, mt->table, propName, context, inConditional, suppressSimplification, seen);
if (!blocked.empty() || result)
return {blocked, result};
auto result = lookupTableProp(constraint, mt->table, propName, context, inConditional, suppressSimplification, seen);
if (!result.blockedTypes.empty() || result.propType)
return result;
TypeId mtt = follow(mt->metatable);
@ -2467,7 +2483,7 @@ std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTa
{
auto indexProp = metatable->props.find("__index");
if (indexProp == metatable->props.end())
return {{}, result};
return {{}, result.propType};
// TODO: __index can be an overloaded function.
@ -2497,7 +2513,7 @@ std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTa
return {{}, context == ValueContext::RValue ? p->readTy : p->writeTy};
if (ct->indexer)
{
return {{}, ct->indexer->indexResultType};
return {{}, ct->indexer->indexResultType, /* isIndex = */ true};
}
}
else if (auto pt = get<PrimitiveType>(subjectType); pt && pt->metatable)
@ -2548,10 +2564,10 @@ std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTa
for (TypeId ty : utv)
{
auto [innerBlocked, innerResult] = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen);
blocked.insert(blocked.end(), innerBlocked.begin(), innerBlocked.end());
if (innerResult)
options.insert(*innerResult);
auto result = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen);
blocked.insert(blocked.end(), result.blockedTypes.begin(), result.blockedTypes.end());
if (result.propType)
options.insert(*result.propType);
}
if (!blocked.empty())
@ -2585,10 +2601,10 @@ std::pair<std::vector<TypeId>, std::optional<TypeId>> ConstraintSolver::lookupTa
for (TypeId ty : itv)
{
auto [innerBlocked, innerResult] = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen);
blocked.insert(blocked.end(), innerBlocked.begin(), innerBlocked.end());
if (innerResult)
options.insert(*innerResult);
auto result = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen);
blocked.insert(blocked.end(), result.blockedTypes.begin(), result.blockedTypes.end());
if (result.propType)
options.insert(*result.propType);
}
if (!blocked.empty())
@ -2920,7 +2936,7 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l
}
TypePackId modulePack = module->returnType;
if (get<Unifiable::Error>(modulePack))
if (get<ErrorTypePack>(modulePack))
return errorRecoveryType();
std::optional<TypeId> moduleType = first(modulePack);

View file

@ -184,7 +184,7 @@ DataFlowGraph DataFlowGraphBuilder::build(AstStatBlock* block, NotNull<InternalE
DataFlowGraphBuilder builder;
builder.handle = handle;
DfgScope* moduleScope = builder.makeChildScope(block->location);
DfgScope* moduleScope = builder.makeChildScope();
PushScope ps{builder.scopeStack, moduleScope};
builder.visitBlockWithoutChildScope(block);
builder.resolveCaptures();
@ -208,7 +208,7 @@ std::pair<std::shared_ptr<DataFlowGraph>, std::vector<std::unique_ptr<DfgScope>>
DataFlowGraphBuilder builder;
builder.handle = handle;
DfgScope* moduleScope = builder.makeChildScope(block->location);
DfgScope* moduleScope = builder.makeChildScope();
PushScope ps{builder.scopeStack, moduleScope};
builder.visitBlockWithoutChildScope(block);
builder.resolveCaptures();
@ -247,9 +247,9 @@ DfgScope* DataFlowGraphBuilder::currentScope()
return scopeStack.back();
}
DfgScope* DataFlowGraphBuilder::makeChildScope(Location loc, DfgScope::ScopeType scopeType)
DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType)
{
return scopes.emplace_back(new DfgScope{currentScope(), scopeType, loc}).get();
return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get();
}
void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b)
@ -397,7 +397,7 @@ DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key)
ControlFlow DataFlowGraphBuilder::visit(AstStatBlock* b)
{
DfgScope* child = makeChildScope(b->location);
DfgScope* child = makeChildScope();
ControlFlow cf;
{
@ -474,8 +474,8 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
{
visitExpr(i->condition);
DfgScope* thenScope = makeChildScope(i->thenbody->location);
DfgScope* elseScope = makeChildScope(i->elsebody ? i->elsebody->location : i->location);
DfgScope* thenScope = makeChildScope();
DfgScope* elseScope = makeChildScope();
ControlFlow thencf;
{
@ -509,7 +509,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i)
ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
{
// TODO(controlflow): entry point has a back edge from exit point
DfgScope* whileScope = makeChildScope(w->location, DfgScope::Loop);
DfgScope* whileScope = makeChildScope(DfgScope::Loop);
{
PushScope ps{scopeStack, whileScope};
@ -525,7 +525,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w)
ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r)
{
// TODO(controlflow): entry point has a back edge from exit point
DfgScope* repeatScope = makeChildScope(r->location, DfgScope::Loop);
DfgScope* repeatScope = makeChildScope(DfgScope::Loop);
{
PushScope ps{scopeStack, repeatScope};
@ -601,7 +601,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l)
ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
{
DfgScope* forScope = makeChildScope(f->location, DfgScope::Loop);
DfgScope* forScope = makeChildScope(DfgScope::Loop);
visitExpr(f->from);
visitExpr(f->to);
@ -630,7 +630,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f)
ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f)
{
DfgScope* forScope = makeChildScope(f->location, DfgScope::Loop);
DfgScope* forScope = makeChildScope(DfgScope::Loop);
{
PushScope ps{scopeStack, forScope};
@ -726,7 +726,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l)
ControlFlow DataFlowGraphBuilder::visit(AstStatTypeAlias* t)
{
DfgScope* unreachable = makeChildScope(t->location);
DfgScope* unreachable = makeChildScope();
PushScope ps{scopeStack, unreachable};
visitGenerics(t->generics);
@ -738,7 +738,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatTypeAlias* t)
ControlFlow DataFlowGraphBuilder::visit(AstStatTypeFunction* f)
{
DfgScope* unreachable = makeChildScope(f->location);
DfgScope* unreachable = makeChildScope();
PushScope ps{scopeStack, unreachable};
visitExpr(f->body);
@ -765,7 +765,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d)
currentScope()->bindings[d->name] = def;
captures[d->name].allVersions.push_back(def);
DfgScope* unreachable = makeChildScope(d->location);
DfgScope* unreachable = makeChildScope();
PushScope ps{scopeStack, unreachable};
visitGenerics(d->generics);
@ -781,7 +781,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareClass* d)
// This declaration does not "introduce" any bindings in value namespace,
// so there's no symbolic value to begin with. We'll traverse the properties
// because their type annotations may depend on something in the value namespace.
DfgScope* unreachable = makeChildScope(d->location);
DfgScope* unreachable = makeChildScope();
PushScope ps{scopeStack, unreachable};
for (AstDeclaredClassProp prop : d->props)
@ -792,7 +792,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareClass* d)
ControlFlow DataFlowGraphBuilder::visit(AstStatError* error)
{
DfgScope* unreachable = makeChildScope(error->location);
DfgScope* unreachable = makeChildScope();
PushScope ps{scopeStack, unreachable};
for (AstStat* s : error->statements)
@ -904,10 +904,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c)
LUAU_ASSERT(result);
Location location = currentScope()->location;
// This scope starts at the end of the call site and continues to the end of the original scope.
location.begin = c->location.end;
DfgScope* child = makeChildScope(location);
DfgScope* child = makeChildScope();
scopeStack.push_back(child);
auto [def, key] = *result;
@ -952,7 +949,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexExpr* i)
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f)
{
DfgScope* signatureScope = makeChildScope(f->location, DfgScope::Function);
DfgScope* signatureScope = makeChildScope(DfgScope::Function);
PushScope ps{scopeStack, signatureScope};
if (AstLocal* self = f->self)
@ -1056,7 +1053,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInterpString* i)
DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprError* error)
{
DfgScope* unreachable = makeChildScope(error->location);
DfgScope* unreachable = makeChildScope();
PushScope ps{scopeStack, unreachable};
for (AstExpr* e : error->expressions)

View file

@ -719,7 +719,7 @@ static DifferResult diffUsingEnv(DifferEnvironment& env, TypeId left, TypeId rig
env.popVisiting();
return diffRes;
}
if (auto le = get<Luau::Unifiable::Error>(left))
if (auto le = get<ErrorType>(left))
{
// TODO: return debug-friendly result state
env.popVisiting();

View file

@ -27,7 +27,6 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit);
LUAU_FASTINT(LuauTypeInferIterationLimit);
LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(LuauAllowFragmentParsing);
LUAU_FASTFLAG(LuauStoreDFGOnModule2);
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
namespace
@ -89,6 +88,25 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* ro
{
localStack.push_back(locFun->name);
localMap[locFun->name->name] = locFun->name;
if (locFun->location.contains(cursorPos))
{
for (AstLocal* loc : locFun->func->args)
{
localStack.push_back(loc);
localMap[loc->name] = loc;
}
}
}
else if (auto globFun = stat->as<AstStatFunction>())
{
if (globFun->location.contains(cursorPos))
{
for (AstLocal* loc : globFun->func->args)
{
localStack.push_back(loc);
localMap[loc->name] = loc;
}
}
}
}
}
@ -234,9 +252,9 @@ FragmentParseResult parseFragment(
// If we added to the end of the sourceModule, use the end of the nearest location
if (appended && multiline)
startPos = nearestStatement->location.end;
// Statement spans one line && cursorPos is on a different line
else if (!multiline && cursorPos.line != nearestStatement->location.end.line)
startPos = nearestStatement->location.end;
// Statement spans one line && cursorPos is either on the same line or after
else if (!multiline && cursorPos.line >= nearestStatement->location.end.line)
startPos = nearestStatement->location.begin;
else if (multiline && nearestStatement->location.end.line < cursorPos.line)
startPos = nearestStatement->location.end;
else
@ -300,6 +318,7 @@ struct MixedModeIncrementalTCDefFinder : public AstVisitor
referencedLocalDefs.push_back({local->local, local});
return true;
}
// ast defs is just a mapping from expr -> def in general
// will get built up by the dfg builder
@ -495,7 +514,6 @@ FragmentAutocompleteResult fragmentAutocomplete(
)
{
LUAU_ASSERT(FFlag::LuauAllowFragmentParsing);
LUAU_ASSERT(FFlag::LuauStoreDFGOnModule2);
LUAU_ASSERT(FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
const SourceModule* sourceModule = frontend.getSourceModule(moduleName);

View file

@ -45,11 +45,9 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile)
LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes)
LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode)
LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionNoEvaluation)
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false)
LUAU_FASTFLAG(StudioReportLuauAny2)
LUAU_FASTFLAGVARIABLE(LuauStoreDFGOnModule2)
LUAU_FASTFLAGVARIABLE(LuauStoreSolverTypeOnModule)
namespace Luau
@ -1307,19 +1305,7 @@ ModulePtr check(
}
}
DataFlowGraph oldDfg = DataFlowGraphBuilder::build(sourceModule.root, iceHandler);
DataFlowGraph* dfgForConstraintGeneration = nullptr;
if (FFlag::LuauStoreDFGOnModule2)
{
auto [dfg, scopes] = DataFlowGraphBuilder::buildShared(sourceModule.root, iceHandler);
result->dataFlowGraph = std::move(dfg);
result->dfgScopes = std::move(scopes);
dfgForConstraintGeneration = result->dataFlowGraph.get();
}
else
{
dfgForConstraintGeneration = &oldDfg;
}
DataFlowGraph dfg = DataFlowGraphBuilder::build(sourceModule.root, iceHandler);
UnifierSharedState unifierState{iceHandler};
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
@ -1329,8 +1315,7 @@ ModulePtr check(
SimplifierPtr simplifier = newSimplifier(NotNull{&result->internalTypes}, builtinTypes);
TypeFunctionRuntime typeFunctionRuntime{iceHandler, NotNull{&limits}};
if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation)
typeFunctionRuntime.allowEvaluation = sourceModule.parseErrors.empty();
typeFunctionRuntime.allowEvaluation = sourceModule.parseErrors.empty();
ConstraintGenerator cg{
result,
@ -1343,7 +1328,7 @@ ModulePtr check(
parentScope,
std::move(prepareModuleScope),
logger.get(),
NotNull{dfgForConstraintGeneration},
NotNull{&dfg},
requireCycles
};
@ -1360,7 +1345,7 @@ ModulePtr check(
moduleResolver,
requireCycles,
logger.get(),
NotNull{dfgForConstraintGeneration},
NotNull{&dfg},
limits
};
@ -1414,32 +1399,16 @@ ModulePtr check(
switch (mode)
{
case Mode::Nonstrict:
if (FFlag::LuauStoreDFGOnModule2)
{
Luau::checkNonStrict(
builtinTypes,
NotNull{&typeFunctionRuntime},
iceHandler,
NotNull{&unifierState},
NotNull{dfgForConstraintGeneration},
NotNull{&limits},
sourceModule,
result.get()
);
}
else
{
Luau::checkNonStrict(
builtinTypes,
NotNull{&typeFunctionRuntime},
iceHandler,
NotNull{&unifierState},
NotNull{&oldDfg},
NotNull{&limits},
sourceModule,
result.get()
);
}
Luau::checkNonStrict(
builtinTypes,
NotNull{&typeFunctionRuntime},
iceHandler,
NotNull{&unifierState},
NotNull{&dfg},
NotNull{&limits},
sourceModule,
result.get()
);
break;
case Mode::Definition:
// fallthrough intentional

View file

@ -9,6 +9,8 @@
#include "Luau/TypePack.h"
#include "Luau/VisitType.h"
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
namespace Luau
{
@ -445,7 +447,7 @@ struct FreeTypeSearcher : TypeVisitor
traverse(*prop.readTy);
else
{
LUAU_ASSERT(prop.isShared());
LUAU_ASSERT(prop.isShared() || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
Polarity p = polarity;
polarity = Both;
@ -894,7 +896,7 @@ struct TypeCacher : TypeOnceVisitor
return true;
}
bool visit(TypePackId tp, const Unifiable::Error& etp) override
bool visit(TypePackId tp, const ErrorTypePack& etp) override
{
return true;
}

View file

@ -17,7 +17,6 @@ LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAttribute)
LUAU_FASTFLAG(LuauNativeAttribute)
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
namespace Luau
@ -3239,7 +3238,6 @@ static void lintComments(LintContext& context, const std::vector<HotComment>& ho
static bool hasNativeCommentDirective(const std::vector<HotComment>& hotcomments)
{
LUAU_ASSERT(FFlag::LuauNativeAttribute);
LUAU_ASSERT(FFlag::LintRedundantNativeAttribute);
for (const HotComment& hc : hotcomments)
@ -3265,7 +3263,6 @@ struct LintRedundantNativeAttribute : AstVisitor
public:
LUAU_NOINLINE static void process(LintContext& context)
{
LUAU_ASSERT(FFlag::LuauNativeAttribute);
LUAU_ASSERT(FFlag::LintRedundantNativeAttribute);
LintRedundantNativeAttribute pass;
@ -3389,7 +3386,7 @@ std::vector<LintWarning> lint(
if (context.warningEnabled(LintWarning::Code_ComparisonPrecedence))
LintComparisonPrecedence::process(context);
if (FFlag::LuauNativeAttribute && FFlag::LintRedundantNativeAttribute && context.warningEnabled(LintWarning::Code_RedundantNativeAttribute))
if (FFlag::LintRedundantNativeAttribute && context.warningEnabled(LintWarning::Code_RedundantNativeAttribute))
{
if (hasNativeCommentDirective(hotcomments))
LintRedundantNativeAttribute::process(context);

View file

@ -218,7 +218,7 @@ struct NonStrictTypeChecker
return result;
}
else if (get<Unifiable::Error>(pack))
else if (get<ErrorTypePack>(pack))
return builtinTypes->errorRecoveryType();
else if (finite(pack) && size(pack) == 0)
return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil`

View file

@ -417,8 +417,8 @@ std::optional<TypeId> selectOverload(
TypePackId argsPack
)
{
OverloadResolver resolver{builtinTypes, arena, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location};
auto [status, overload] = resolver.selectOverload(fn, argsPack);
auto resolver = std::make_unique<OverloadResolver>(builtinTypes, arena, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location);
auto [status, overload] = resolver->selectOverload(fn, argsPack);
if (status == OverloadResolver::Analysis::Ok)
return overload;
@ -456,9 +456,9 @@ SolveResult solveFunctionCall(
if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty())
{
Instantiation2 instantiation{arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions)};
auto instantiation = std::make_unique<Instantiation2>(arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions));
std::optional<TypePackId> subst = instantiation.substitute(resultPack);
std::optional<TypePackId> subst = instantiation->substitute(resultPack);
if (!subst)
return {SolveResult::CodeTooComplex};

View file

@ -4,13 +4,15 @@
#include "Luau/Common.h"
#include "Luau/Clone.h"
#include "Luau/TxnLog.h"
#include "Luau/Type.h"
#include <algorithm>
#include <stdexcept>
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256);
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
LUAU_FASTFLAG(LuauSyntheticErrors)
namespace Luau
{
@ -57,8 +59,25 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
}
else if constexpr (std::is_same_v<T, ErrorType>)
{
LUAU_ASSERT(ty->persistent);
return ty;
if (FFlag::LuauSyntheticErrors)
{
LUAU_ASSERT(ty->persistent || a.synthetic);
if (ty->persistent)
return ty;
// While this code intentionally works (and clones) even if `a.synthetic` is `std::nullopt`,
// we still assert above because we consider it a bug to have a non-persistent error type
// without any associated metadata. We should always use the persistent version in such cases.
ErrorType clone = ErrorType{};
clone.synthetic = a.synthetic;
return dest.addType(clone);
}
else
{
LUAU_ASSERT(ty->persistent);
return ty;
}
}
else if constexpr (std::is_same_v<T, UnknownType>)
{

View file

@ -420,7 +420,7 @@ void StateDot::visitChildren(TypePackId tp, int index)
finishNodeLabel(tp);
finishNode();
}
else if (get<Unifiable::Error>(tp))
else if (get<ErrorTypePack>(tp))
{
formatAppend(result, "ErrorTypePack %d", index);
finishNodeLabel(tp);

View file

@ -20,6 +20,7 @@
#include <string>
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors)
/*
* Enables increasing levels of verbosity for Luau type names when stringifying.
@ -998,7 +999,15 @@ struct TypeStringifier
void operator()(TypeId, const ErrorType& tv)
{
state.result.error = true;
state.emit("*error-type*");
if (FFlag::LuauSyntheticErrors && tv.synthetic)
{
state.emit("*error-type<");
stringify(*tv.synthetic);
state.emit(">*");
}
else
state.emit("*error-type*");
}
void operator()(TypeId, const LazyType& ltv)
@ -1173,10 +1182,18 @@ struct TypePackStringifier
state.unsee(&tp);
}
void operator()(TypePackId, const Unifiable::Error& error)
void operator()(TypePackId, const ErrorTypePack& error)
{
state.result.error = true;
state.emit("*error-type*");
if (FFlag::LuauSyntheticErrors && error.synthetic)
{
state.emit("*");
stringify(*error.synthetic);
state.emit("*");
}
else
state.emit("*error-type*");
}
void operator()(TypePackId, const VariadicTypePack& pack)

View file

@ -1045,7 +1045,7 @@ BuiltinTypes::BuiltinTypes()
, unknownTypePack(arena->addTypePack(TypePackVar{VariadicTypePack{unknownType}, /*persistent*/ true}))
, neverTypePack(arena->addTypePack(TypePackVar{VariadicTypePack{neverType}, /*persistent*/ true}))
, uninhabitableTypePack(arena->addTypePack(TypePackVar{TypePack{{neverType}, neverTypePack}, /*persistent*/ true}))
, errorTypePack(arena->addTypePack(TypePackVar{Unifiable::Error{}, /*persistent*/ true}))
, errorTypePack(arena->addTypePack(TypePackVar{ErrorTypePack{}, /*persistent*/ true}))
{
freeze(*arena);
}

View file

@ -329,7 +329,7 @@ public:
Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation}
);
}
AstType* operator()(const Unifiable::Error&)
AstType* operator()(const ErrorType&)
{
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("Unifiable<Error>"), std::nullopt, Location());
}
@ -458,7 +458,7 @@ public:
return allocator->alloc<AstTypePackGeneric>(Location(), AstName("free"));
}
AstTypePack* operator()(const Unifiable::Error&) const
AstTypePack* operator()(const ErrorTypePack&) const
{
return allocator->alloc<AstTypePackGeneric>(Location(), AstName("Unifiable<Error>"));
}

View file

@ -31,7 +31,6 @@
#include <ostream>
LUAU_FASTFLAG(DebugLuauMagicTypes)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
LUAU_FASTFLAGVARIABLE(LuauTableKeysAreRValues)
@ -1200,8 +1199,6 @@ void TypeChecker2::visit(AstStatTypeAlias* stat)
void TypeChecker2::visit(AstStatTypeFunction* stat)
{
// TODO: add type checking for user-defined type functions
if (!FFlag::LuauUserDefinedTypeFunctions2)
reportError(TypeError{stat->location, GenericError{"This syntax is not supported"}});
}
void TypeChecker2::visit(AstTypeList types)
@ -2353,7 +2350,7 @@ TypeId TypeChecker2::flattenPack(TypePackId pack)
return result;
}
else if (get<Unifiable::Error>(pack))
else if (get<ErrorTypePack>(pack))
return builtinTypes->errorRecoveryType();
else if (finite(pack) && size(pack) == 0)
return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil`

View file

@ -46,8 +46,6 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1);
LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
LUAU_FASTFLAG(LuauUserTypeFunFixRegister)
LUAU_FASTFLAG(LuauRemoveNotAnyHack)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionResetState)
@ -634,12 +632,9 @@ TypeFunctionReductionResult<TypeId> userDefinedTypeFunction(
}
}
if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation)
{
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
if (!ctx->typeFunctionRuntime->allowEvaluation)
return {ctx->builtins->errorRecoveryType(), false, {}, {}};
}
// If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones
if (!ctx->typeFunctionRuntime->allowEvaluation)
return {ctx->builtins->errorRecoveryType(), false, {}, {}};
for (auto typeParam : typeParams)
{
@ -994,12 +989,9 @@ TypeFunctionRuntime::~TypeFunctionRuntime() {}
std::optional<std::string> TypeFunctionRuntime::registerFunction(AstStatTypeFunction* function)
{
if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation)
{
// If evaluation is disabled, we do not generate additional error messages
if (!allowEvaluation)
return std::nullopt;
}
// If evaluation is disabled, we do not generate additional error messages
if (!allowEvaluation)
return std::nullopt;
prepareState();

View file

@ -964,7 +964,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assig
else if (auto tail = valueIter.tail())
{
TypePackId tailPack = follow(*tail);
if (get<Unifiable::Error>(tailPack))
if (get<ErrorTypePack>(tailPack))
right = errorRecoveryType(scope);
else if (auto vtp = get<VariadicTypePack>(tailPack))
right = vtp->ty;
@ -1244,7 +1244,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
iterTy = freshType(scope);
unify(callRetPack, addTypePack({{iterTy}, freshTypePack(scope)}), scope, forin.location);
}
else if (get<Unifiable::Error>(callRetPack) || !first(callRetPack))
else if (get<ErrorTypePack>(callRetPack) || !first(callRetPack))
{
for (TypeId var : varTypes)
unify(errorRecoveryType(scope), var, scope, forin.location);
@ -1972,7 +1972,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
*asMutable(varargPack) = TypePack{{head}, tail};
return WithPredicate{head};
}
if (get<ErrorType>(varargPack))
if (get<ErrorTypePack>(varargPack))
return WithPredicate{errorRecoveryType(scope)};
else if (auto vtp = get<VariadicTypePack>(varargPack))
return WithPredicate{vtp->ty};
@ -2002,7 +2002,7 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
unify(pack, retPack, scope, expr.location);
return {head, std::move(result.predicates)};
}
if (get<Unifiable::Error>(retPack))
if (get<ErrorTypePack>(retPack))
return {errorRecoveryType(scope), std::move(result.predicates)};
else if (auto vtp = get<VariadicTypePack>(retPack))
return {vtp->ty, std::move(result.predicates)};
@ -4093,7 +4093,7 @@ void TypeChecker::checkArgumentList(
if (argIter.tail())
{
TypePackId tail = *argIter.tail();
if (state.log.getMutable<Unifiable::Error>(tail))
if (state.log.getMutable<ErrorTypePack>(tail))
{
// Unify remaining parameters so we don't leave any free-types hanging around.
while (paramIter != endIter)
@ -4178,7 +4178,7 @@ void TypeChecker::checkArgumentList(
}
TypePackId tail = state.log.follow(*paramIter.tail());
if (state.log.getMutable<Unifiable::Error>(tail))
if (state.log.getMutable<ErrorTypePack>(tail))
{
// Function is variadic. Ok.
return;
@ -4314,7 +4314,7 @@ WithPredicate<TypePackId> TypeChecker::checkExprPackHelper(const ScopePtr& scope
WithPredicate<TypePackId> argListResult = checkExprList(scope, expr.location, expr.args, false, {}, expectedTypes);
TypePackId argPack = argListResult.type;
if (get<Unifiable::Error>(argPack))
if (get<ErrorTypePack>(argPack))
return WithPredicate{errorRecoveryTypePack(scope)};
TypePack* args = nullptr;
@ -4904,7 +4904,7 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module
TypePackId modulePack = module->returnType;
if (get<Unifiable::Error>(modulePack))
if (get<ErrorTypePack>(modulePack))
return errorRecoveryType(scope);
std::optional<TypeId> moduleType = first(modulePack);

View file

@ -10,6 +10,7 @@
#include <algorithm>
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete);
namespace Luau
{
@ -331,7 +332,7 @@ TypePack extendTypePack(
return result;
}
else if (const Unifiable::Error* etp = getMutable<Unifiable::Error>(pack))
else if (auto etp = getMutable<ErrorTypePack>(pack))
{
while (result.head.size() < length)
result.head.push_back(builtinTypes->errorRecoveryType());
@ -426,7 +427,7 @@ TypeId stripNil(NotNull<BuiltinTypes> builtinTypes, TypeArena& arena, TypeId ty)
ErrorSuppression shouldSuppressErrors(NotNull<Normalizer> normalizer, TypeId ty)
{
LUAU_ASSERT(FFlag::LuauSolverV2);
LUAU_ASSERT(FFlag::LuauSolverV2 || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete);
std::shared_ptr<const NormalizedType> normType = normalizer->normalize(ty);
if (!normType)

View file

@ -1,5 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Unifiable.h"
#include "Luau/TypeFwd.h"
#include "Luau/TypePack.h"
namespace Luau
{
@ -13,12 +15,17 @@ int freshIndex()
return ++nextIndex;
}
Error::Error()
template<typename Id>
Error<Id>::Error()
: index(++nextIndex)
{
}
int Error::nextIndex = 0;
template<typename Id>
int Error<Id>::nextIndex = 0;
template struct Error<TypeId>;
template struct Error<TypePackId>;
} // namespace Unifiable
} // namespace Luau

View file

@ -1616,9 +1616,9 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
log.replace(subTp, Unifiable::Bound<TypePackId>(superTp));
}
}
else if (log.getMutable<Unifiable::Error>(superTp))
else if (log.getMutable<ErrorTypePack>(superTp))
tryUnifyWithAny(subTp, superTp);
else if (log.getMutable<Unifiable::Error>(subTp))
else if (log.getMutable<ErrorTypePack>(subTp))
tryUnifyWithAny(superTp, subTp);
else if (log.getMutable<VariadicTypePack>(superTp))
tryUnifyVariadics(subTp, superTp, false);
@ -2741,7 +2741,7 @@ void Unifier::tryUnifyVariadics(TypePackId subTp, TypePackId superTp, bool rever
else
log.replace(tail, BoundTypePack{superTp});
}
else if (get<Unifiable::Error>(tail))
else if (get<ErrorTypePack>(tail))
{
// Nothing to do here.
}
@ -2845,7 +2845,7 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
{
LUAU_ASSERT(get<Unifiable::Error>(anyTp));
LUAU_ASSERT(get<ErrorTypePack>(anyTp));
const TypeId anyTy = builtinTypes->errorRecoveryType();
@ -2997,7 +2997,7 @@ bool Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, Typ
RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit);
while (!log.getMutable<ErrorType>(haystack))
while (!log.getMutable<ErrorTypePack>(haystack))
{
if (needle == haystack)
return true;

View file

@ -908,7 +908,7 @@ OccursCheckResult Unifier2::occursCheck(DenseHashSet<TypePackId>& seen, TypePack
RecursionLimiter _ra(&recursionCount, recursionLimit);
while (!getMutable<Unifiable::Error>(haystack))
while (!getMutable<ErrorTypePack>(haystack))
{
if (needle == haystack)
return OccursCheckResult::Fail;

View file

@ -3,8 +3,6 @@
#include "Luau/Common.h"
LUAU_FASTFLAG(LuauNativeAttribute)
namespace Luau
{
@ -239,8 +237,6 @@ void AstExprFunction::visit(AstVisitor* visitor)
bool AstExprFunction::hasNativeAttribute() const
{
LUAU_ASSERT(FFlag::LuauNativeAttribute);
for (const auto attribute : attributes)
{
if (attribute->type == AstAttr::Type::Native)

View file

@ -18,13 +18,12 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
// flag so that we don't break production games by reverting syntax changes.
// See docs/SyntaxChanges.md for an explanation.
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
LUAU_FASTFLAGVARIABLE(LuauNativeAttribute)
LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionsSyntax2)
LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunParseExport)
LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing)
LUAU_FASTFLAGVARIABLE(LuauPortableStringZeroCheck)
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
namespace Luau
{
@ -724,10 +723,6 @@ std::pair<bool, AstAttr::Type> Parser::validateAttribute(const char* attributeNa
if (found)
{
type = kAttributeEntries[i].type;
if (!FFlag::LuauNativeAttribute && type == AstAttr::Type::Native)
found = false;
break;
}
}
@ -1278,6 +1273,19 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
MatchLexeme matchParen = lexer.current();
expectAndConsume('(', "function");
// NOTE: This was added in conjunction with passing `searchForMissing` to
// `expectMatchAndConsume` inside `parseTableType` so that the behavior of
// parsing code like below (note the missing `}`):
//
// function (t: { a: number ) end
//
// ... will still parse as (roughly):
//
// function (t: { a: number }) end
//
if (FFlag::LuauErrorRecoveryForTableTypes)
matchRecoveryStopOnToken[')']++;
TempVector<Binding> args(scratchBinding);
bool vararg = false;
@ -1294,6 +1302,9 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
expectMatchAndConsume(')', matchParen, true);
if (FFlag::LuauErrorRecoveryForTableTypes)
matchRecoveryStopOnToken[')']--;
std::optional<AstTypeList> typelist = parseOptionalReturnType();
AstLocal* funLocal = nullptr;
@ -1678,7 +1689,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext)
Location end = lexer.current().location;
if (!expectMatchAndConsume('}', matchBrace))
if (!expectMatchAndConsume('}', matchBrace, /* searchForMissing = */ FFlag::LuauErrorRecoveryForTableTypes))
end = lexer.previousLocation();
return allocator.alloc<AstTypeTable>(Location(start, end), copy(props), indexer);
@ -2526,7 +2537,7 @@ AstExpr* Parser::parseSimpleExpr()
AstArray<AstAttr*> attributes{nullptr, 0};
if (FFlag::LuauAttributeSyntaxFunExpr && lexer.current().type == Lexeme::Attribute)
if (lexer.current().type == Lexeme::Attribute)
{
attributes = parseAttributes();

View file

@ -5,6 +5,8 @@
#include "Luau/Common.h"
#include "Luau/IrData.h"
LUAU_FASTFLAG(LuauVectorLibNativeDot);
namespace Luau
{
namespace CodeGen
@ -177,6 +179,9 @@ inline bool hasResult(IrCmd cmd)
case IrCmd::MUL_VEC:
case IrCmd::DIV_VEC:
case IrCmd::DOT_VEC:
if (cmd == IrCmd::DOT_VEC)
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
LUAU_FALLTHROUGH;
case IrCmd::UNM_VEC:
case IrCmd::NOT_ANY:
case IrCmd::CMP_ANY:

View file

@ -7,6 +7,8 @@
#include <stdarg.h>
#include <stdio.h>
LUAU_FASTFLAG(LuauVectorLibNativeDot);
namespace Luau
{
namespace CodeGen
@ -588,6 +590,7 @@ void AssemblyBuilderA64::fabs(RegisterA64 dst, RegisterA64 src)
void AssemblyBuilderA64::faddp(RegisterA64 dst, RegisterA64 src)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s);
CODEGEN_ASSERT(dst.kind == src.kind);

View file

@ -6,6 +6,8 @@
#include <stdarg.h>
#include <stdio.h>
LUAU_FASTFLAG(LuauVectorLibNativeDot);
namespace Luau
{
namespace CodeGen
@ -948,6 +950,7 @@ void AssemblyBuilderX64::vpinsrd(RegisterX64 dst, RegisterX64 src1, OperandX64 s
void AssemblyBuilderX64::vdpps(OperandX64 dst, OperandX64 src1, OperandX64 src2, uint8_t mask)
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
placeAvx("vdpps", dst, src1, src2, mask, 0x40, false, AVX_0F3A, AVX_66);
}

View file

@ -8,8 +8,6 @@
#include "lobject.h"
#include "lstate.h"
LUAU_FASTFLAG(LuauNativeAttribute)
namespace Luau
{
namespace CodeGen
@ -58,10 +56,7 @@ std::vector<FunctionBytecodeSummary> summarizeBytecode(lua_State* L, int idx, un
Proto* root = clvalue(func)->l.p;
std::vector<Proto*> protos;
if (FFlag::LuauNativeAttribute)
gatherFunctions(protos, root, CodeGen_ColdFunctions, root->flags & LPF_NATIVE_FUNCTION);
else
gatherFunctions_DEPRECATED(protos, root, CodeGen_ColdFunctions);
gatherFunctions(protos, root, CodeGen_ColdFunctions, root->flags & LPF_NATIVE_FUNCTION);
std::vector<FunctionBytecodeSummary> summaries;
summaries.reserve(protos.size());

View file

@ -12,8 +12,6 @@
#include "lapi.h"
LUAU_FASTFLAG(LuauNativeAttribute)
namespace Luau
{
namespace CodeGen
@ -155,10 +153,7 @@ static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, A
}
std::vector<Proto*> protos;
if (FFlag::LuauNativeAttribute)
gatherFunctions(protos, root, options.compilationOptions.flags, root->flags & LPF_NATIVE_FUNCTION);
else
gatherFunctions_DEPRECATED(protos, root, options.compilationOptions.flags);
gatherFunctions(protos, root, options.compilationOptions.flags, root->flags & LPF_NATIVE_FUNCTION);
protos.erase(
std::remove_if(

View file

@ -14,7 +14,6 @@
LUAU_FASTINTVARIABLE(LuauCodeGenBlockSize, 4 * 1024 * 1024)
LUAU_FASTINTVARIABLE(LuauCodeGenMaxTotalSize, 256 * 1024 * 1024)
LUAU_FASTFLAG(LuauNativeAttribute)
namespace Luau
{
@ -510,10 +509,7 @@ template<typename AssemblyBuilder>
return CompilationResult{CodeGenCompilationResult::CodeGenNotInitialized};
std::vector<Proto*> protos;
if (FFlag::LuauNativeAttribute)
gatherFunctions(protos, root, options.flags, root->flags & LPF_NATIVE_FUNCTION);
else
gatherFunctions_DEPRECATED(protos, root, options.flags);
gatherFunctions(protos, root, options.flags, root->flags & LPF_NATIVE_FUNCTION);
// Skip protos that have been compiled during previous invocations of CodeGen::compile
protos.erase(

View file

@ -27,31 +27,12 @@ LUAU_FASTFLAG(DebugCodegenSkipNumbering)
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
LUAU_FASTINT(CodegenHeuristicsBlockLimit)
LUAU_FASTINT(CodegenHeuristicsBlockInstructionLimit)
LUAU_FASTFLAG(LuauNativeAttribute)
namespace Luau
{
namespace CodeGen
{
inline void gatherFunctions_DEPRECATED(std::vector<Proto*>& results, Proto* proto, unsigned int flags)
{
if (results.size() <= size_t(proto->bytecodeid))
results.resize(proto->bytecodeid + 1);
// Skip protos that we've already compiled in this run: this happens because at -O2, inlined functions get their protos reused
if (results[proto->bytecodeid])
return;
// Only compile cold functions if requested
if ((proto->flags & LPF_NATIVE_COLD) == 0 || (flags & CodeGen_ColdFunctions) != 0)
results[proto->bytecodeid] = proto;
// Recursively traverse child protos even if we aren't compiling this one
for (int i = 0; i < proto->sizep; i++)
gatherFunctions_DEPRECATED(results, proto->p[i], flags);
}
inline void gatherFunctionsHelper(
std::vector<Proto*>& results,
Proto* proto,
@ -82,7 +63,6 @@ inline void gatherFunctionsHelper(
inline void gatherFunctions(std::vector<Proto*>& results, Proto* root, const unsigned int flags, const bool hasNativeFunctions = false)
{
LUAU_ASSERT(FFlag::LuauNativeAttribute);
gatherFunctionsHelper(results, root, flags, hasNativeFunctions, true);
}

View file

@ -7,6 +7,8 @@
#include <stdarg.h>
LUAU_FASTFLAG(LuauVectorLibNativeDot);
namespace Luau
{
namespace CodeGen
@ -164,6 +166,7 @@ const char* getCmdName(IrCmd cmd)
case IrCmd::UNM_VEC:
return "UNM_VEC";
case IrCmd::DOT_VEC:
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
return "DOT_VEC";
case IrCmd::NOT_ANY:
return "NOT_ANY";

View file

@ -11,6 +11,8 @@
#include "lstate.h"
#include "lgc.h"
LUAU_FASTFLAG(LuauVectorLibNativeDot);
namespace Luau
{
namespace CodeGen
@ -730,6 +732,8 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
}
case IrCmd::DOT_VEC:
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
inst.regA64 = regs.allocReg(KindA64::d, index);
RegisterA64 temp = regs.allocTemp(KindA64::q);

View file

@ -15,6 +15,8 @@
#include "lstate.h"
#include "lgc.h"
LUAU_FASTFLAG(LuauVectorLibNativeDot);
namespace Luau
{
namespace CodeGen
@ -677,6 +679,8 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
}
case IrCmd::DOT_VEC:
{
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.b});
ScopedRegX64 tmp1{regs};

View file

@ -12,6 +12,8 @@
#include <limits.h>
#include <math.h>
LUAU_FASTFLAG(LuauVectorLibNativeDot);
namespace Luau
{
namespace CodeGen
@ -76,6 +78,7 @@ IrValueKind getCmdValueKind(IrCmd cmd)
case IrCmd::UNM_VEC:
return IrValueKind::Tvalue;
case IrCmd::DOT_VEC:
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
return IrValueKind::Double;
case IrCmd::NOT_ANY:
case IrCmd::CMP_ANY:

View file

@ -18,6 +18,7 @@ LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3)
LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64)
LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks)
LUAU_FASTFLAG(LuauVectorLibNativeDot);
namespace Luau
{
@ -1344,6 +1345,9 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
case IrCmd::MUL_VEC:
case IrCmd::DIV_VEC:
case IrCmd::DOT_VEC:
if (inst.cmd == IrCmd::DOT_VEC)
LUAU_ASSERT(FFlag::LuauVectorLibNativeDot);
if (IrInst* a = function.asInstOp(inst.a); a && a->cmd == IrCmd::TAG_VECTOR)
replace(function, inst.a, a->a);

View file

@ -26,7 +26,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25)
LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300)
LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
LUAU_FASTFLAG(LuauNativeAttribute)
LUAU_FASTFLAGVARIABLE(LuauCompileOptimizeRevArith)
namespace Luau
@ -286,7 +285,7 @@ struct Compiler
if (func->functionDepth == 0 && !hasLoops)
protoflags |= LPF_NATIVE_COLD;
if (FFlag::LuauNativeAttribute && func->hasNativeAttribute())
if (func->hasNativeAttribute())
protoflags |= LPF_NATIVE_FUNCTION;
bytecode.endFunction(uint8_t(stackSize), uint8_t(upvals.size()), protoflags);
@ -3927,7 +3926,7 @@ struct Compiler
// this makes sure all functions that are used when compiling this one have been already added to the vector
functions.push_back(node);
if (FFlag::LuauNativeAttribute && !hasNativeFunction && node->hasNativeAttribute())
if (!hasNativeFunction && node->hasNativeAttribute())
hasNativeFunction = true;
return false;
@ -4272,7 +4271,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c
// If a function has native attribute and the whole module is not native, we set LPF_NATIVE_FUNCTION flag
// This ensures that LPF_NATIVE_MODULE and LPF_NATIVE_FUNCTION are exclusive.
if (FFlag::LuauNativeAttribute && (protoflags & LPF_NATIVE_FUNCTION) && !(mainFlags & LPF_NATIVE_MODULE))
if ((protoflags & LPF_NATIVE_FUNCTION) && !(mainFlags & LPF_NATIVE_MODULE))
mainFlags |= LPF_NATIVE_FUNCTION;
}

View file

@ -46,7 +46,7 @@ struct Config
DenseHashMap<std::string, AliasInfo> aliases{""};
void setAlias(std::string alias, const std::string& value, const std::string configLocation);
void setAlias(std::string alias, std::string value, const std::string& configLocation);
private:
// Prevents making unnecessary copies of the same config location string.

View file

@ -28,15 +28,7 @@ Config::Config(const Config& other)
{
for (const auto& [alias, aliasInfo] : other.aliases)
{
std::string configLocation = std::string(aliasInfo.configLocation);
if (!configLocationCache.contains(configLocation))
configLocationCache[configLocation] = std::make_unique<std::string>(configLocation);
AliasInfo newAliasInfo;
newAliasInfo.value = aliasInfo.value;
newAliasInfo.configLocation = *configLocationCache[configLocation];
aliases[alias] = std::move(newAliasInfo);
setAlias(alias, aliasInfo.value, std::string(aliasInfo.configLocation));
}
}
@ -50,10 +42,10 @@ Config& Config::operator=(const Config& other)
return *this;
}
void Config::setAlias(std::string alias, const std::string& value, const std::string configLocation)
void Config::setAlias(std::string alias, std::string value, const std::string& configLocation)
{
AliasInfo& info = aliases[alias];
info.value = value;
info.value = std::move(value);
if (!configLocationCache.contains(configLocation))
configLocationCache[configLocation] = std::make_unique<std::string>(configLocation);

View file

@ -6,6 +6,8 @@
#include <math.h>
LUAU_FASTFLAGVARIABLE(LuauVectorMetatable)
static int vector_create(lua_State* L)
{
double x = luaL_checknumber(L, 1);
@ -254,6 +256,35 @@ static int vector_max(lua_State* L)
return 1;
}
static int vector_index(lua_State* L)
{
LUAU_ASSERT(FFlag::LuauVectorMetatable);
const float* v = luaL_checkvector(L, 1);
size_t namelen = 0;
const char* name = luaL_checklstring(L, 2, &namelen);
// field access implementation mirrors the fast-path we have in the VM
if (namelen == 1)
{
int ic = (name[0] | ' ') - 'x';
#if LUA_VECTOR_SIZE == 4
// 'w' is before 'x' in ascii, so ic is -1 when indexing with 'w'
if (ic == -1)
ic = 3;
#endif
if (unsigned(ic) < LUA_VECTOR_SIZE)
{
lua_pushnumber(L, v[ic]);
return 1;
}
}
luaL_error(L, "attempt to index vector with '%s'", name);
}
static const luaL_Reg vectorlib[] = {
{"create", vector_create},
{"magnitude", vector_magnitude},
@ -271,6 +302,30 @@ static const luaL_Reg vectorlib[] = {
{NULL, NULL},
};
static void createmetatable(lua_State* L)
{
LUAU_ASSERT(FFlag::LuauVectorMetatable);
lua_createtable(L, 0, 1); // create metatable for vectors
// push dummy vector
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
#else
lua_pushvector(L, 0.0f, 0.0f, 0.0f);
#endif
lua_pushvalue(L, -2);
lua_setmetatable(L, -2); // set vector metatable
lua_pop(L, 1); // pop dummy vector
lua_pushcfunction(L, vector_index, nullptr);
lua_setfield(L, -2, "__index");
lua_setreadonly(L, -1, true);
lua_pop(L, 1); // pop the metatable
}
int luaopen_vector(lua_State* L)
{
luaL_register(L, LUA_VECLIBNAME, vectorlib);
@ -287,5 +342,8 @@ int luaopen_vector(lua_State* L)
lua_setfield(L, -2, "one");
#endif
if (FFlag::LuauVectorMetatable)
createmetatable(L);
return 1;
}

View file

@ -10,6 +10,8 @@
using namespace Luau::CodeGen;
using namespace Luau::CodeGen::A64;
LUAU_FASTFLAG(LuauVectorLibNativeDot);
static std::string bytecodeAsArray(const std::vector<uint8_t>& bytecode)
{
std::string result = "{";
@ -387,6 +389,8 @@ TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "FPBasic")
TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "FPMath")
{
ScopedFastFlag sff{FFlag::LuauVectorLibNativeDot, true};
SINGLE_COMPARE(fabs(d1, d2), 0x1E60C041);
SINGLE_COMPARE(fadd(d1, d2, d3), 0x1E632841);
SINGLE_COMPARE(fadd(s29, s29, s28), 0x1E3C2BBD);

View file

@ -10,6 +10,8 @@
using namespace Luau::CodeGen;
using namespace Luau::CodeGen::X64;
LUAU_FASTFLAG(LuauVectorLibNativeDot);
static std::string bytecodeAsArray(const std::vector<uint8_t>& bytecode)
{
std::string result = "{";
@ -568,6 +570,8 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXConversionInstructionForms")
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXTernaryInstructionForms")
{
ScopedFastFlag sff{FFlag::LuauVectorLibNativeDot, true};
SINGLE_COMPARE(vroundsd(xmm7, xmm12, xmm3, RoundingModeX64::RoundToNegativeInfinity), 0xc4, 0xe3, 0x19, 0x0b, 0xfb, 0x09);
SINGLE_COMPARE(
vroundsd(xmm8, xmm13, xmmword[r13 + rdx], RoundingModeX64::RoundToPositiveInfinity), 0xc4, 0x43, 0x11, 0x0b, 0x44, 0x15, 0x00, 0x0a

View file

@ -34,11 +34,13 @@ void luaC_validate(lua_State* L);
LUAU_FASTFLAG(LuauMathMap)
LUAU_FASTFLAG(DebugLuauAbortingChecks)
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
LUAU_FASTFLAG(LuauNativeAttribute)
LUAU_DYNAMIC_FASTFLAG(LuauStackLimit)
LUAU_FASTFLAG(LuauVectorDefinitions)
LUAU_DYNAMIC_FASTFLAG(LuauDebugInfoInvArgLeftovers)
LUAU_FASTFLAG(LuauVectorLibNativeCodegen)
LUAU_FASTFLAG(LuauVectorLibNativeDot)
LUAU_FASTFLAG(LuauVectorBuiltins)
LUAU_FASTFLAG(LuauVectorMetatable)
static lua_CompileOptions defaultOptions()
{
@ -889,7 +891,10 @@ TEST_CASE("Vector")
TEST_CASE("VectorLibrary")
{
ScopedFastFlag luauVectorBuiltins{FFlag::LuauVectorBuiltins, true};
ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true};
ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true};
ScopedFastFlag luauVectorMetatable{FFlag::LuauVectorMetatable, true};
lua_CompileOptions copts = defaultOptions();
@ -2951,8 +2956,6 @@ TEST_CASE("NativeAttribute")
if (!codegen || !luau_codegen_supported())
return;
ScopedFastFlag sffs[] = {{FFlag::LuauNativeAttribute, true}};
std::string source = R"R(
@native
local function sum(x, y)

View file

@ -562,12 +562,14 @@ void Fixture::validateErrors(const std::vector<Luau::TypeError>& errors)
}
}
LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source)
LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool forAutocomplete)
{
unfreeze(frontend.globals.globalTypes);
LoadDefinitionFileResult result =
frontend.loadDefinitionFile(frontend.globals, frontend.globals.globalScope, source, "@test", /* captureComments */ false);
freeze(frontend.globals.globalTypes);
GlobalTypes& globals = forAutocomplete ? frontend.globalsForAutocomplete : frontend.globals;
unfreeze(globals.globalTypes);
LoadDefinitionFileResult result = frontend.loadDefinitionFile(
globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typecheckForAutocomplete */ forAutocomplete
);
freeze(globals.globalTypes);
if (result.module)
dumpErrors(result.module);

View file

@ -139,7 +139,7 @@ struct Fixture
void registerTestTypes();
LoadDefinitionFileResult loadDefinition(const std::string& source);
LoadDefinitionFileResult loadDefinition(const std::string& source, bool forAutocomplete = false);
};
struct BuiltinsFixture : Fixture

View file

@ -15,12 +15,12 @@
#include <ctime>
#include <iomanip>
#include <iostream>
#include <optional>
using namespace Luau;
LUAU_FASTFLAG(LuauAllowFragmentParsing);
LUAU_FASTFLAG(LuauStoreDFGOnModule2);
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
LUAU_FASTFLAG(LuauSymbolEquality);
LUAU_FASTFLAG(LuauStoreSolverTypeOnModule);
@ -46,12 +46,11 @@ static FrontendOptions getOptions()
template<class BaseType>
struct FragmentAutocompleteFixtureImpl : BaseType
{
ScopedFastFlag sffs[5] = {
ScopedFastFlag sffs[4] = {
{FFlag::LuauAllowFragmentParsing, true},
{FFlag::LuauStoreDFGOnModule2, true},
{FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true},
{FFlag::LuauStoreSolverTypeOnModule, true},
{FFlag::LuauSymbolEquality, true}
{FFlag::LuauSymbolEquality, true},
};
FragmentAutocompleteFixtureImpl()
@ -140,6 +139,25 @@ struct FragmentAutocompleteFixture : FragmentAutocompleteFixtureImpl<Fixture>
struct FragmentAutocompleteBuiltinsFixture : FragmentAutocompleteFixtureImpl<BuiltinsFixture>
{
FragmentAutocompleteBuiltinsFixture()
: FragmentAutocompleteFixtureImpl<BuiltinsFixture>()
{
const std::string fakeVecDecl = R"(
declare class FakeVec
function dot(self, x: FakeVec) : FakeVec
zero : FakeVec
end
)";
// The old solver always performs a strict mode check and populates the module resolver and globals
// for autocomplete.
// The new solver just populates the globals and the moduleResolver.
// Because these tests run in both the old solver and the new solver, and the test suite
// now picks the module resolver as appropriate in order to better mimic the studio code path,
// we have to load the definition file into both the 'globals'/'resolver' and the equivalent
// 'for autocomplete'.
loadDefinition(fakeVecDecl);
loadDefinition(fakeVecDecl, /* For Autocomplete Module */ true);
}
};
TEST_SUITE_BEGIN("FragmentAutocompleteTraversalTests");
@ -316,11 +334,11 @@ local z = x + y
Position{3, 15}
);
CHECK_EQ("\nlocal z = x + y", fragment.fragmentToParse);
CHECK_EQ("local y = 5\nlocal z = x + y", fragment.fragmentToParse);
CHECK_EQ(5, fragment.ancestry.size());
REQUIRE(fragment.root);
CHECK_EQ(1, fragment.root->body.size);
auto stat = fragment.root->body.data[0]->as<AstStatLocal>();
CHECK_EQ(2, fragment.root->body.size);
auto stat = fragment.root->body.data[1]->as<AstStatLocal>();
REQUIRE(stat);
CHECK_EQ(1, stat->vars.size);
CHECK_EQ(1, stat->values.size);
@ -422,7 +440,7 @@ abc("bar")
Position{1, 10}
);
CHECK_EQ("\nabc(\"foo\")", callFragment.fragmentToParse);
CHECK_EQ("function abc(foo: string) end\nabc(\"foo\")", callFragment.fragmentToParse);
CHECK(callFragment.nearestStatement->is<AstStatFunction>());
CHECK_GE(callFragment.ancestry.size(), 2);
@ -447,7 +465,7 @@ abc("bar")
Position{1, 9}
);
CHECK_EQ("\nabc(\"foo\"", stringFragment.fragmentToParse);
CHECK_EQ("function abc(foo: string) end\nabc(\"foo\"", stringFragment.fragmentToParse);
CHECK(stringFragment.nearestStatement->is<AstStatFunction>());
CHECK_GE(stringFragment.ancestry.size(), 1);
@ -482,7 +500,7 @@ abc("bar")
Position{3, 1}
);
CHECK_EQ("\nabc(\n\"foo\"\n)", fragment.fragmentToParse);
CHECK_EQ("function abc(foo: string) end\nabc(\n\"foo\"\n)", fragment.fragmentToParse);
CHECK(fragment.nearestStatement->is<AstStatFunction>());
CHECK_GE(fragment.ancestry.size(), 2);
@ -1223,4 +1241,140 @@ TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "method_call_inside_function_body
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tbl_function_parameter")
{
const std::string source = R"(
--!strict
type Foo = {x : number, y : number}
local function func(abc : Foo)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{4, 7},
[](FragmentAutocompleteResult& result)
{
CHECK_EQ(2, result.acResults.entryMap.size());
CHECK(result.acResults.entryMap.count("x"));
CHECK(result.acResults.entryMap.count("y"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tbl_local_function_parameter")
{
const std::string source = R"(
--!strict
type Foo = {x : number, y : number}
local function func(abc : Foo)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{4, 7},
[](FragmentAutocompleteResult& result)
{
CHECK_EQ(2, result.acResults.entryMap.size());
CHECK(result.acResults.entryMap.count("x"));
CHECK(result.acResults.entryMap.count("y"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "vec3_function_parameter")
{
const std::string source = R"(
--!strict
local function func(abc : FakeVec)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 7},
[](FragmentAutocompleteResult& result)
{
CHECK_EQ(2, result.acResults.entryMap.size());
CHECK(result.acResults.entryMap.count("zero"));
CHECK(result.acResults.entryMap.count("dot"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "vec3_local_function_parameter")
{
const std::string source = R"(
--!strict
local function func(abc : FakeVec)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 7},
[](FragmentAutocompleteResult& result)
{
CHECK_EQ(2, result.acResults.entryMap.size());
CHECK(result.acResults.entryMap.count("zero"));
CHECK(result.acResults.entryMap.count("dot"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "function_parameter_not_recommending_out_of_scope_argument")
{
const std::string source = R"(
--!strict
local function foo(abd: FakeVec)
end
local function bar(abc : FakeVec)
a
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{5, 5},
[](FragmentAutocompleteResult& result)
{
CHECK(result.acResults.entryMap.count("abc"));
CHECK(!result.acResults.entryMap.count("abd"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "bad_range")
{
const std::string source = R"(
l
)";
const std::string updated = R"(
local t = 1
t
)";
autocompleteFragmentInBothSolvers(
source,
updated,
Position{2, 1},
[](FragmentAutocompleteResult& result)
{
auto opt = linearSearchForBinding(result.freshScope, "t");
REQUIRE(opt);
CHECK_EQ("number", toString(*opt));
}
);
}
TEST_SUITE_END();

View file

@ -8,7 +8,6 @@
#include "doctest.h"
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauNativeAttribute);
LUAU_FASTFLAG(LintRedundantNativeAttribute);
using namespace Luau;
@ -1999,7 +1998,7 @@ local _ = a <= (b == 0)
TEST_CASE_FIXTURE(Fixture, "RedundantNativeAttribute")
{
ScopedFastFlag sff[] = {{FFlag::LuauNativeAttribute, true}, {FFlag::LintRedundantNativeAttribute, true}};
ScopedFastFlag sff[] = {{FFlag::LintRedundantNativeAttribute, true}};
LintResult result = lint(R"(
--!native

View file

@ -16,10 +16,10 @@ LUAU_FASTINT(LuauRecursionLimit)
LUAU_FASTINT(LuauTypeLengthLimit)
LUAU_FASTINT(LuauParseErrorLimit)
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunParseExport)
LUAU_FASTFLAG(LuauAllowComplexTypesInGenericParams)
LUAU_FASTFLAG(LuauErrorRecoveryForTableTypes)
namespace
{
@ -3351,8 +3351,6 @@ end)");
TEST_CASE_FIXTURE(Fixture, "parse_attribute_for_function_expression")
{
ScopedFastFlag sff[] = {{FFlag::LuauAttributeSyntaxFunExpr, true}};
AstStatBlock* stat1 = parse(R"(
local function invoker(f)
return f(1)
@ -3521,8 +3519,6 @@ function foo1 () @checked return 'a' end
TEST_CASE_FIXTURE(Fixture, "dont_parse_attribute_on_argument_non_function")
{
ScopedFastFlag sff[] = {{FFlag::LuauAttributeSyntaxFunExpr, true}};
ParseResult pr = tryParse(R"(
local function invoker(f, y)
return f(y)
@ -3743,5 +3739,18 @@ TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty")
}
}
TEST_CASE_FIXTURE(Fixture, "recover_from_bad_table_type")
{
ScopedFastFlag _{FFlag::LuauErrorRecoveryForTableTypes, true};
ParseOptions opts;
opts.allowDeclarationSyntax = true;
const auto result = tryParse(R"(
declare class Widget
state: {string: function(string, Widget)}
end
)", opts);
CHECK_EQ(result.errors.size(), 2);
}
TEST_SUITE_END();

View file

@ -431,7 +431,7 @@ n1 [label="FreeTypePack 1"];
TEST_CASE_FIXTURE(Fixture, "error_pack")
{
TypePackVar pack{TypePackVariant{Unifiable::Error{}}};
TypePackVar pack{TypePackVariant{ErrorTypePack{}}};
ToDotOptions opts;
opts.showPointers = false;

View file

@ -13,7 +13,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction);
LUAU_FASTFLAG(LuauSolverV2);
LUAU_FASTFLAG(LuauAttributeSyntax);
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
TEST_SUITE_BEGIN("ToString");
@ -969,7 +968,7 @@ TEST_CASE_FIXTURE(Fixture, "correct_stringification_user_defined_type_functions"
Type tv{tftt};
if (FFlag::LuauSolverV2 && FFlag::LuauUserDefinedTypeFunctions2)
if (FFlag::LuauSolverV2)
CHECK_EQ(toString(&tv, {}), "woohoo<number>");
}

View file

@ -13,7 +13,6 @@
using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit)
struct TypeFunctionFixture : Fixture

View file

@ -9,8 +9,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation)
LUAU_FASTFLAG(LuauUserTypeFunFixRegister)
LUAU_FASTFLAG(LuauUserTypeFunFixNoReadWrite)
LUAU_FASTFLAG(LuauUserTypeFunFixMetatable)
@ -25,7 +23,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_nil(arg)
@ -42,7 +39,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getnil()
@ -63,7 +59,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_unknown(arg)
@ -80,7 +75,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getunknown()
@ -101,7 +95,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_never(arg)
@ -118,7 +111,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getnever()
@ -139,7 +131,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_any(arg)
@ -156,7 +147,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getany()
@ -177,7 +167,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_bool(arg)
@ -194,7 +183,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getboolean()
@ -215,7 +203,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_num(arg)
@ -232,7 +219,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getnumber()
@ -253,7 +239,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_str(arg)
@ -270,7 +255,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getstring()
@ -291,7 +275,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_boolsingleton(arg)
@ -308,7 +291,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getboolsingleton()
@ -329,7 +311,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_strsingleton(arg)
@ -346,7 +327,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getstrsingleton()
@ -367,7 +347,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_union(arg)
@ -388,7 +367,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getunion()
@ -418,7 +396,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_intersection(arg)
@ -439,7 +416,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getintersection()
@ -475,7 +451,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getnegation()
@ -501,7 +476,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_table(arg)
@ -522,7 +496,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function gettable()
@ -562,7 +535,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getmetatable()
@ -596,7 +568,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_func(arg)
@ -613,7 +584,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getfunction()
@ -644,7 +614,6 @@ TEST_CASE_FIXTURE(ClassFixture, "udtf_class_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_class(arg)
@ -660,7 +629,6 @@ TEST_CASE_FIXTURE(ClassFixture, "udtf_class_methods_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
@ -684,7 +652,6 @@ TEST_CASE_FIXTURE(ClassFixture, "write_of_readonly_is_nil")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag udtfRwFix{FFlag::LuauUserTypeFunFixNoReadWrite, true};
@ -714,7 +681,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function checkmut()
@ -747,7 +713,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function getcopy()
@ -781,7 +746,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_simple_cyclic_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_cycle(arg)
@ -803,7 +767,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_createtable_bad_metatable")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function badmetatable()
@ -825,7 +788,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_complex_cyclic_serialization_works")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function serialize_cycle2(arg)
@ -855,7 +817,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_user_error_is_reported")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function errors_if_string(arg)
@ -878,7 +839,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_call_metamethod")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function hello(arg)
@ -897,7 +857,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_eq_metamethod")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function hello()
@ -923,7 +882,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_type_cant_call_get_props")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function hello(arg)
@ -945,7 +903,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function foo()
@ -967,7 +924,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_no_shared_state")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function foo()
@ -996,7 +952,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_math_reset")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserDefinedTypeFunctionResetState{FFlag::LuauUserDefinedTypeFunctionResetState, true};
CheckResult result = check(R"(
@ -1013,7 +968,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function optionify(tbl)
@ -1043,7 +997,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_illegal_global")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function illegal(arg)
@ -1065,7 +1018,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function foo(tbl)
@ -1091,8 +1043,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recovery_no_upvalues")
{
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
ScopedFastFlag userDefinedTypeFunctionsSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag userDefinedTypeFunctions{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag userDefinedTypeFunctionNoEvaluation{FFlag::LuauUserDefinedTypeFunctionNoEvaluation, true};
CheckResult result = check(R"(
local var
@ -1116,7 +1066,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_follow")
{
ScopedFastFlag solverV2{FFlag::LuauSolverV2, true};
ScopedFastFlag userDefinedTypeFunctionsSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag userDefinedTypeFunctions{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type t0 = any
@ -1133,7 +1082,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strip_indexer")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
CheckResult result = check(R"(
type function stripindexer(tbl)
@ -1159,7 +1107,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_type_methods_on_types")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
CheckResult result = check(R"(
@ -1177,7 +1124,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_types_functions_on_type")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
CheckResult result = check(R"(
@ -1195,7 +1141,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_metatable_writes")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
CheckResult result = check(R"(
@ -1215,7 +1160,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_eq_field")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
CheckResult result = check(R"(
@ -1233,7 +1177,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
CheckResult result = check(R"(
@ -1256,7 +1199,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_serialization")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
ScopedFastFlag luauUserTypeFunFixMetatable{FFlag::LuauUserTypeFunFixMetatable, true};
@ -1288,7 +1230,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonstrict_mode")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
ScopedFastFlag luauUserTypeFunNonstrict{FFlag::LuauUserTypeFunNonstrict, true};
@ -1304,7 +1245,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "implicit_export")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
@ -1335,7 +1275,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "local_scope")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
@ -1361,7 +1300,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "explicit_export")
{
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true};
ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true};
ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true};
ScopedFastFlag luauUserDefinedTypeFunParseExport{FFlag::LuauUserDefinedTypeFunParseExport, true};

View file

@ -10,7 +10,6 @@ using namespace Luau;
LUAU_FASTFLAG(LuauSolverV2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2)
LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2)
TEST_SUITE_BEGIN("TypeAliases");
@ -1154,7 +1153,7 @@ type Foo<T> = Foo<T> | string
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_adds_reduce_constraint_for_type_function")
{
if (!FFlag::LuauSolverV2 || !FFlag::LuauUserDefinedTypeFunctions2)
if (!FFlag::LuauSolverV2)
return;
CheckResult result = check(R"(
@ -1166,20 +1165,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_adds_reduce_constraint_for_type_f
LUAU_CHECK_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "user_defined_type_function_errors")
{
ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true};
ScopedFastFlag noUDTFimpl{FFlag::LuauUserDefinedTypeFunctions2, false};
CheckResult result = check(R"(
type function foo()
return nil
end
)");
LUAU_CHECK_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) == "This syntax is not supported");
}
TEST_CASE_FIXTURE(Fixture, "bound_type_in_alias_segfault")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};

View file

@ -2049,10 +2049,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinements_should_preserve_error_suppressio
end
)");
if (FFlag::LuauSolverV2)
LUAU_REQUIRE_NO_ERRORS(result);
else
LUAU_REQUIRE_NO_ERRORS(result);
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "many_refinements_on_val")

View file

@ -20,6 +20,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping)
LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
LUAU_FASTFLAG(LuauRetrySubtypingWithoutHiddenPack)
LUAU_FASTFLAG(LuauTableKeysAreRValues)
LUAU_FASTFLAG(LuauAllowNilAssignmentToIndexer)
TEST_SUITE_BEGIN("TableTests");
@ -1925,18 +1926,128 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_on_massive_table_is_cut_short")
TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr")
{
// CLI-100076 Assigning nil to an indexer should always succeed
DOES_NOT_PASS_NEW_SOLVER_GUARD();
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}};
CheckResult result = check(R"(
LUAU_REQUIRE_NO_ERRORS(check(R"(
local function f(): { [string]: number }
return { ["foo"] = 1 }
end
f()["foo"] = nil
)"));
LUAU_REQUIRE_NO_ERRORS(check(R"(
local function f(
t: {known_prop: boolean, [string]: number},
key: string
)
t[key] = nil
t["hello"] = nil
t.undefined = nil
end
)"));
auto result = check(R"(
local function f(t: {known_prop: boolean, [string]: number, })
t.known_prop = nil
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(Location{{2, 27}, {2, 30}}, result.errors[0].location);
CHECK_EQ("Type 'nil' could not be converted into 'boolean'", toString(result.errors[0]));
loadDefinition(R"(
declare class FancyHashtable
[string]: number
real_property: string
end
)");
LUAU_REQUIRE_NO_ERRORS(check(R"(
local function removekey(fh: FancyHashtable, other_key: string)
fh["hmmm"] = nil
fh[other_key] = nil
fh.dne = nil
end
)"));
result = check(R"(
local function removekey(fh: FancyHashtable)
fh.real_property = nil
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(result.errors[0].location, Location{{2, 31}, {2, 34}});
CHECK_EQ(toString(result.errors[0]), "Type 'nil' could not be converted into 'string'");
}
TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_on_generic_map")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}};
LUAU_REQUIRE_NO_ERRORS(check(R"(
type MyMap<K, V> = { [K]: V }
function set<K, V>(m: MyMap<K, V>, k: K, v: V)
m[k] = v
end
function unset<K, V>(m: MyMap<K, V>, k: K)
m[k] = nil
end
local m: MyMap<string, boolean> = {}
set(m, "foo", true)
unset(m, "foo")
)"));
}
TEST_CASE_FIXTURE(Fixture, "key_setting_inference_given_nil_upper_bound")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}};
LUAU_REQUIRE_NO_ERRORS(check(R"(
local function setkey_object(t: { [string]: number }, v)
t.foo = v
t.foo = nil
end
local function setkey_constindex(t: { [string]: number }, v)
t["foo"] = v
t["foo"] = nil
end
local function setkey_unknown(t: { [string]: number }, k, v)
t[k] = v
t[k] = nil
end
)"));
CHECK_EQ(toString(requireType("setkey_object")), "({ [string]: number }, number) -> ()");
CHECK_EQ(toString(requireType("setkey_constindex")), "({ [string]: number }, number) -> ()");
CHECK_EQ(toString(requireType("setkey_unknown")), "({ [string]: number }, string, number) -> ()");
LUAU_REQUIRE_NO_ERRORS(check(R"(
local function on_number(v: number): () end
local function setkey_object(t: { [string]: number }, v)
t.foo = v
on_number(v)
end
)"));
CHECK_EQ(toString(requireType("setkey_object")), "({ [string]: number }, number) -> ()");
}
TEST_CASE_FIXTURE(Fixture, "explicit_nil_indexer")
{
ScopedFastFlag _{FFlag::LuauSolverV2, true};
auto result = check(R"(
local function _(t: { [string]: number? }): number
return t.hello
end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ(result.errors[0].location, Location{{2, 12}, {2, 26}});
CHECK(get<TypePackMismatch>(result.errors[0]));
}
TEST_CASE_FIXTURE(Fixture, "ok_to_provide_a_subtype_during_construction")
@ -2843,17 +2954,20 @@ TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_indexer")
TEST_CASE_FIXTURE(Fixture, "wrong_assign_does_hit_indexer")
{
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}};
CheckResult result = check(R"(
local a = {}
a[0] = 7
a[0] = 't'
a[0] = nil
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK((Location{Position{3, 15}, Position{3, 18}}) == result.errors[0].location);
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm);
CHECK(tm->wantedType == builtinTypes->numberType);
CHECK_EQ("number?", toString(tm->wantedType));
CHECK(tm->givenType == builtinTypes->stringType);
}

View file

@ -199,14 +199,14 @@ TEST_CASE_FIXTURE(TypePackFixture, "std_distance")
TEST_CASE("content_reassignment")
{
TypePackVar myError{Unifiable::Error{}, /*presistent*/ true};
TypePackVar myError{ErrorTypePack{}, /*presistent*/ true};
TypeArena arena;
TypePackId futureError = arena.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}});
asMutable(futureError)->reassign(myError);
CHECK(get<ErrorType>(futureError) != nullptr);
CHECK(get<ErrorTypePack>(futureError) != nullptr);
CHECK(!futureError->persistent);
CHECK(futureError->owningArena == &arena);
}

View file

@ -1,6 +1,9 @@
-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
print('testing vector library')
-- detect vector size
local vector_size = if pcall(function() return vector(0, 0, 0).w end) then 4 else 3
function ecall(fn, ...)
local ok, err = pcall(fn, ...)
assert(not ok)
@ -156,4 +159,32 @@ assert(vector.clamp(vector.create(1, 1, 1), vector.create(0, 1, 2), vector.creat
assert(vector.clamp(vector.create(1, 1, 1), vector.create(-1, -1, -1), vector.create(0, 1, 2)) == vector.create(0, 1, 1))
assert(select("#", vector.clamp(vector.zero, vector.zero, vector.one)) == 1)
-- validate component access
assert(vector.create(1, 2, 3).x == 1)
assert(vector.create(1, 2, 3).X == 1)
assert(vector.create(1, 2, 3)['X'] == 1)
assert(vector.create(1, 2, 3).y == 2)
assert(vector.create(1, 2, 3).Y == 2)
assert(vector.create(1, 2, 3)['Y'] == 2)
assert(vector.create(1, 2, 3).z == 3)
assert(vector.create(1, 2, 3).Z == 3)
assert(vector.create(1, 2, 3)['Z'] == 3)
local function getcomp(v: vector, field: string)
return v[field]
end
assert(getcomp(vector.create(1, 2, 3), 'x') == 1)
assert(getcomp(vector.create(1, 2, 3), 'y') == 2)
assert(getcomp(vector.create(1, 2, 3), 'z') == 3)
assert(ecall(function() return vector.create(1, 2, 3).zz end) == "attempt to index vector with 'zz'")
-- additional checks for 4-component vectors
if vector_size == 4 then
assert(vector.create(1, 2, 3, 4).w == 4)
assert(vector.create(1, 2, 3, 4).W == 4)
assert(vector.create(1, 2, 3, 4)['W'] == 4)
end
return 'OK'