mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-13 21:40:43 +00:00
Merge branch 'upstream' into merge
This commit is contained in:
commit
bf74a347c3
26 changed files with 1137 additions and 2168 deletions
|
@ -14,61 +14,6 @@ namespace Luau
|
|||
|
||||
using TypeOrPackId = const void*;
|
||||
|
||||
// Log of where what TypeIds we are rebinding and what they used to be
|
||||
// Remove with LuauUseCommitTxnLog
|
||||
struct DEPRECATED_TxnLog
|
||||
{
|
||||
DEPRECATED_TxnLog()
|
||||
: originalSeenSize(0)
|
||||
, ownedSeen()
|
||||
, sharedSeen(&ownedSeen)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DEPRECATED_TxnLog(std::vector<std::pair<TypeOrPackId, TypeOrPackId>>* sharedSeen)
|
||||
: originalSeenSize(sharedSeen->size())
|
||||
, ownedSeen()
|
||||
, sharedSeen(sharedSeen)
|
||||
{
|
||||
}
|
||||
|
||||
DEPRECATED_TxnLog(const DEPRECATED_TxnLog&) = delete;
|
||||
DEPRECATED_TxnLog& operator=(const DEPRECATED_TxnLog&) = delete;
|
||||
|
||||
DEPRECATED_TxnLog(DEPRECATED_TxnLog&&) = default;
|
||||
DEPRECATED_TxnLog& operator=(DEPRECATED_TxnLog&&) = default;
|
||||
|
||||
void operator()(TypeId a);
|
||||
void operator()(TypePackId a);
|
||||
void operator()(TableTypeVar* a);
|
||||
|
||||
void rollback();
|
||||
|
||||
void concat(DEPRECATED_TxnLog rhs);
|
||||
|
||||
bool haveSeen(TypeId lhs, TypeId rhs);
|
||||
void pushSeen(TypeId lhs, TypeId rhs);
|
||||
void popSeen(TypeId lhs, TypeId rhs);
|
||||
|
||||
bool haveSeen(TypePackId lhs, TypePackId rhs);
|
||||
void pushSeen(TypePackId lhs, TypePackId rhs);
|
||||
void popSeen(TypePackId lhs, TypePackId rhs);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<TypeId, TypeVar>> typeVarChanges;
|
||||
std::vector<std::pair<TypePackId, TypePackVar>> typePackChanges;
|
||||
std::vector<std::pair<TableTypeVar*, std::optional<TypeId>>> tableChanges;
|
||||
size_t originalSeenSize;
|
||||
|
||||
bool haveSeen(TypeOrPackId lhs, TypeOrPackId rhs);
|
||||
void pushSeen(TypeOrPackId lhs, TypeOrPackId rhs);
|
||||
void popSeen(TypeOrPackId lhs, TypeOrPackId rhs);
|
||||
|
||||
public:
|
||||
std::vector<std::pair<TypeOrPackId, TypeOrPackId>> ownedSeen; // used to avoid infinite recursion when types are cyclic
|
||||
std::vector<std::pair<TypeOrPackId, TypeOrPackId>>* sharedSeen; // shared with all the descendent logs
|
||||
};
|
||||
|
||||
// Pending state for a TypeVar. Generated by a TxnLog and committed via
|
||||
// TxnLog::commit.
|
||||
struct PendingType
|
||||
|
|
|
@ -105,7 +105,6 @@ private:
|
|||
const TypePack* tp = nullptr;
|
||||
size_t currentIndex = 0;
|
||||
|
||||
// Only used if LuauUseCommittingTxnLog is true.
|
||||
const TxnLog* log;
|
||||
};
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ struct Unifier
|
|||
TypeArena* const types;
|
||||
Mode mode;
|
||||
|
||||
DEPRECATED_TxnLog DEPRECATED_log;
|
||||
TxnLog log;
|
||||
ErrorVec errors;
|
||||
Location location;
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteAvoidMutation, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauMissingFollowACMetatables, false);
|
||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExprFixCompletionIssue, false);
|
||||
|
||||
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
||||
|
@ -240,28 +237,9 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
|
|||
UnifierSharedState unifierState(&iceReporter);
|
||||
Unifier unifier(typeArena, Mode::Strict, Location(), Variance::Covariant, unifierState);
|
||||
|
||||
if (FFlag::LuauAutocompleteAvoidMutation && !FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
SeenTypes seenTypes;
|
||||
SeenTypePacks seenTypePacks;
|
||||
CloneState cloneState;
|
||||
superTy = clone(superTy, *typeArena, seenTypes, seenTypePacks, cloneState);
|
||||
subTy = clone(subTy, *typeArena, seenTypes, seenTypePacks, cloneState);
|
||||
|
||||
auto errors = unifier.canUnify(subTy, superTy);
|
||||
return errors.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
unifier.tryUnify(subTy, superTy);
|
||||
|
||||
bool ok = unifier.errors.empty();
|
||||
|
||||
if (!FFlag::LuauUseCommittingTxnLog)
|
||||
unifier.DEPRECATED_log.rollback();
|
||||
|
||||
return ok;
|
||||
}
|
||||
unifier.tryUnify(subTy, superTy);
|
||||
bool ok = unifier.errors.empty();
|
||||
return ok;
|
||||
};
|
||||
|
||||
auto typeAtPosition = findExpectedTypeAt(module, node, position);
|
||||
|
@ -403,28 +381,14 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
|||
auto indexIt = mtable->props.find("__index");
|
||||
if (indexIt != mtable->props.end())
|
||||
{
|
||||
if (FFlag::LuauMissingFollowACMetatables)
|
||||
TypeId followed = follow(indexIt->second.type);
|
||||
if (get<TableTypeVar>(followed) || get<MetatableTypeVar>(followed))
|
||||
autocompleteProps(module, typeArena, followed, indexType, nodes, result, seen);
|
||||
else if (auto indexFunction = get<FunctionTypeVar>(followed))
|
||||
{
|
||||
TypeId followed = follow(indexIt->second.type);
|
||||
if (get<TableTypeVar>(followed) || get<MetatableTypeVar>(followed))
|
||||
autocompleteProps(module, typeArena, followed, indexType, nodes, result, seen);
|
||||
else if (auto indexFunction = get<FunctionTypeVar>(followed))
|
||||
{
|
||||
std::optional<TypeId> indexFunctionResult = first(indexFunction->retType);
|
||||
if (indexFunctionResult)
|
||||
autocompleteProps(module, typeArena, *indexFunctionResult, indexType, nodes, result, seen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (get<TableTypeVar>(indexIt->second.type) || get<MetatableTypeVar>(indexIt->second.type))
|
||||
autocompleteProps(module, typeArena, indexIt->second.type, indexType, nodes, result, seen);
|
||||
else if (auto indexFunction = get<FunctionTypeVar>(indexIt->second.type))
|
||||
{
|
||||
std::optional<TypeId> indexFunctionResult = first(indexFunction->retType);
|
||||
if (indexFunctionResult)
|
||||
autocompleteProps(module, typeArena, *indexFunctionResult, indexType, nodes, result, seen);
|
||||
}
|
||||
std::optional<TypeId> indexFunctionResult = first(indexFunction->retType);
|
||||
if (indexFunctionResult)
|
||||
autocompleteProps(module, typeArena, *indexFunctionResult, indexType, nodes, result, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauAssertStripsFalsyTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false)
|
||||
|
||||
/** FIXME: Many of these type definitions are not quite completely accurate.
|
||||
*
|
||||
|
@ -376,11 +377,19 @@ static std::optional<ExprResult<TypePackId>> magicFunctionSetMetaTable(
|
|||
|
||||
TypeId mtTy = arena.addType(mtv);
|
||||
|
||||
AstExpr* targetExpr = expr.args.data[0];
|
||||
if (AstExprLocal* targetLocal = targetExpr->as<AstExprLocal>())
|
||||
if (FFlag::LuauSetMetaTableArgsCheck && expr.args.size < 1)
|
||||
{
|
||||
const Name targetName(targetLocal->local->name.value);
|
||||
scope->bindings[targetLocal->local] = Binding{mtTy, expr.location};
|
||||
return ExprResult<TypePackId>{};
|
||||
}
|
||||
|
||||
if (!FFlag::LuauSetMetaTableArgsCheck || !expr.self)
|
||||
{
|
||||
AstExpr* targetExpr = expr.args.data[0];
|
||||
if (AstExprLocal* targetLocal = targetExpr->as<AstExprLocal>())
|
||||
{
|
||||
const Name targetName(targetLocal->local->name.value);
|
||||
scope->bindings[targetLocal->local] = Binding{mtTy, expr.location};
|
||||
}
|
||||
}
|
||||
|
||||
return ExprResult<TypePackId>{arena.addTypePack({mtTy})};
|
||||
|
|
|
@ -7,110 +7,9 @@
|
|||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauUseCommittingTxnLog, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
void DEPRECATED_TxnLog::operator()(TypeId a)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
||||
typeVarChanges.emplace_back(a, *a);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::operator()(TypePackId a)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
||||
typePackChanges.emplace_back(a, *a);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::operator()(TableTypeVar* a)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
||||
tableChanges.emplace_back(a, a->boundTo);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::rollback()
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
||||
for (auto it = typeVarChanges.rbegin(); it != typeVarChanges.rend(); ++it)
|
||||
std::swap(*asMutable(it->first), it->second);
|
||||
|
||||
for (auto it = typePackChanges.rbegin(); it != typePackChanges.rend(); ++it)
|
||||
std::swap(*asMutable(it->first), it->second);
|
||||
|
||||
for (auto it = tableChanges.rbegin(); it != tableChanges.rend(); ++it)
|
||||
std::swap(it->first->boundTo, it->second);
|
||||
|
||||
LUAU_ASSERT(originalSeenSize <= sharedSeen->size());
|
||||
sharedSeen->resize(originalSeenSize);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::concat(DEPRECATED_TxnLog rhs)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
||||
typeVarChanges.insert(typeVarChanges.end(), rhs.typeVarChanges.begin(), rhs.typeVarChanges.end());
|
||||
rhs.typeVarChanges.clear();
|
||||
|
||||
typePackChanges.insert(typePackChanges.end(), rhs.typePackChanges.begin(), rhs.typePackChanges.end());
|
||||
rhs.typePackChanges.clear();
|
||||
|
||||
tableChanges.insert(tableChanges.end(), rhs.tableChanges.begin(), rhs.tableChanges.end());
|
||||
rhs.tableChanges.clear();
|
||||
}
|
||||
|
||||
bool DEPRECATED_TxnLog::haveSeen(TypeId lhs, TypeId rhs)
|
||||
{
|
||||
return haveSeen((TypeOrPackId)lhs, (TypeOrPackId)rhs);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::pushSeen(TypeId lhs, TypeId rhs)
|
||||
{
|
||||
pushSeen((TypeOrPackId)lhs, (TypeOrPackId)rhs);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::popSeen(TypeId lhs, TypeId rhs)
|
||||
{
|
||||
popSeen((TypeOrPackId)lhs, (TypeOrPackId)rhs);
|
||||
}
|
||||
|
||||
bool DEPRECATED_TxnLog::haveSeen(TypePackId lhs, TypePackId rhs)
|
||||
{
|
||||
return haveSeen((TypeOrPackId)lhs, (TypeOrPackId)rhs);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::pushSeen(TypePackId lhs, TypePackId rhs)
|
||||
{
|
||||
pushSeen((TypeOrPackId)lhs, (TypeOrPackId)rhs);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::popSeen(TypePackId lhs, TypePackId rhs)
|
||||
{
|
||||
popSeen((TypeOrPackId)lhs, (TypeOrPackId)rhs);
|
||||
}
|
||||
|
||||
bool DEPRECATED_TxnLog::haveSeen(TypeOrPackId lhs, TypeOrPackId rhs)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
||||
const std::pair<TypeOrPackId, TypeOrPackId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
return (sharedSeen->end() != std::find(sharedSeen->begin(), sharedSeen->end(), sortedPair));
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::pushSeen(TypeOrPackId lhs, TypeOrPackId rhs)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
||||
const std::pair<TypeOrPackId, TypeOrPackId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
sharedSeen->push_back(sortedPair);
|
||||
}
|
||||
|
||||
void DEPRECATED_TxnLog::popSeen(TypeOrPackId lhs, TypeOrPackId rhs)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
||||
const std::pair<TypeOrPackId, TypeOrPackId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
LUAU_ASSERT(sortedPair == sharedSeen->back());
|
||||
sharedSeen->pop_back();
|
||||
}
|
||||
|
||||
const std::string nullPendingResult = "<nullptr>";
|
||||
|
||||
std::string toString(PendingType* pending)
|
||||
|
@ -170,8 +69,6 @@ const TxnLog* TxnLog::empty()
|
|||
|
||||
void TxnLog::concat(TxnLog rhs)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
for (auto& [ty, rep] : rhs.typeVarChanges)
|
||||
typeVarChanges[ty] = std::move(rep);
|
||||
|
||||
|
@ -181,8 +78,6 @@ void TxnLog::concat(TxnLog rhs)
|
|||
|
||||
void TxnLog::commit()
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
for (auto& [ty, rep] : typeVarChanges)
|
||||
*asMutable(ty) = rep.get()->pending;
|
||||
|
||||
|
@ -194,16 +89,12 @@ void TxnLog::commit()
|
|||
|
||||
void TxnLog::clear()
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
typeVarChanges.clear();
|
||||
typePackChanges.clear();
|
||||
}
|
||||
|
||||
TxnLog TxnLog::inverse()
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
TxnLog inversed(sharedSeen);
|
||||
|
||||
for (auto& [ty, _rep] : typeVarChanges)
|
||||
|
@ -247,8 +138,6 @@ void TxnLog::popSeen(TypePackId lhs, TypePackId rhs)
|
|||
|
||||
bool TxnLog::haveSeen(TypeOrPackId lhs, TypeOrPackId rhs) const
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
const std::pair<TypeOrPackId, TypeOrPackId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
if (sharedSeen->end() != std::find(sharedSeen->begin(), sharedSeen->end(), sortedPair))
|
||||
{
|
||||
|
@ -265,16 +154,12 @@ bool TxnLog::haveSeen(TypeOrPackId lhs, TypeOrPackId rhs) const
|
|||
|
||||
void TxnLog::pushSeen(TypeOrPackId lhs, TypeOrPackId rhs)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
const std::pair<TypeOrPackId, TypeOrPackId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
sharedSeen->push_back(sortedPair);
|
||||
}
|
||||
|
||||
void TxnLog::popSeen(TypeOrPackId lhs, TypeOrPackId rhs)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
const std::pair<TypeOrPackId, TypeOrPackId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
||||
LUAU_ASSERT(sortedPair == sharedSeen->back());
|
||||
sharedSeen->pop_back();
|
||||
|
@ -282,7 +167,6 @@ void TxnLog::popSeen(TypeOrPackId lhs, TypeOrPackId rhs)
|
|||
|
||||
PendingType* TxnLog::queue(TypeId ty)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
LUAU_ASSERT(!ty->persistent);
|
||||
|
||||
// Explicitly don't look in ancestors. If we have discovered something new
|
||||
|
@ -296,7 +180,6 @@ PendingType* TxnLog::queue(TypeId ty)
|
|||
|
||||
PendingTypePack* TxnLog::queue(TypePackId tp)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
||||
LUAU_ASSERT(!tp->persistent);
|
||||
|
||||
// Explicitly don't look in ancestors. If we have discovered something new
|
||||
|
|
|
@ -24,7 +24,6 @@ LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 500)
|
|||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false.
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false)
|
||||
|
@ -36,6 +35,7 @@ LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTwoPassAliasDefinitionFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAssertStripsFalsyTypes, false)
|
||||
|
@ -43,6 +43,8 @@ LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as
|
|||
LUAU_FASTFLAG(LuauWidenIfSupertypeIsFree)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDoNotTryToReduce, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDoNotAccidentallyDependOnPointerOrdering, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixArgumentCountMismatchAmountWithGenericTypes, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixIncorrectLineNumberDuplicateType, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -652,18 +654,15 @@ ErrorVec TypeChecker::tryUnify_(Id subTy, Id superTy, const Location& location)
|
|||
{
|
||||
Unifier state = mkUnifier(location);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog && FFlag::DebugLuauFreezeDuringUnification)
|
||||
if (FFlag::DebugLuauFreezeDuringUnification)
|
||||
freeze(currentModule->internalTypes);
|
||||
|
||||
state.tryUnify(subTy, superTy);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog && FFlag::DebugLuauFreezeDuringUnification)
|
||||
if (FFlag::DebugLuauFreezeDuringUnification)
|
||||
unfreeze(currentModule->internalTypes);
|
||||
|
||||
if (!state.errors.empty() && !FFlag::LuauUseCommittingTxnLog)
|
||||
state.DEPRECATED_log.rollback();
|
||||
|
||||
if (state.errors.empty() && FFlag::LuauUseCommittingTxnLog)
|
||||
if (state.errors.empty())
|
||||
state.log.commit();
|
||||
|
||||
return state.errors;
|
||||
|
@ -847,8 +846,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local)
|
|||
state.tryUnify(valuePack, variablePack);
|
||||
reportErrors(state.errors);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
// In the code 'local T = {}', we wish to ascribe the name 'T' to the type of the table for error-reporting purposes.
|
||||
// We also want to do this for 'local T = setmetatable(...)'.
|
||||
|
@ -1040,8 +1038,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
|
|||
Unifier state = mkUnifier(firstValue->location);
|
||||
checkArgumentList(loopScope, state, argPack, iterFunc->argTypes, /*argLocations*/ {});
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
reportErrors(state.errors);
|
||||
}
|
||||
|
@ -1102,8 +1099,53 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
|||
scope->bindings[name->local] = {anyIfNonstrict(quantify(funScope, ty, name->local->location)), name->local->location};
|
||||
return;
|
||||
}
|
||||
else if (auto name = function.name->as<AstExprIndexName>(); name && FFlag::LuauStatFunctionSimplify)
|
||||
{
|
||||
TypeId exprTy = checkExpr(scope, *name->expr).type;
|
||||
TableTypeVar* ttv = getMutableTableType(exprTy);
|
||||
if (!ttv)
|
||||
{
|
||||
if (isTableIntersection(exprTy))
|
||||
reportError(TypeError{function.location, CannotExtendTable{exprTy, CannotExtendTable::Property, name->index.value}});
|
||||
else if (!get<ErrorTypeVar>(exprTy) && !get<AnyTypeVar>(exprTy))
|
||||
reportError(TypeError{function.location, OnlyTablesCanHaveMethods{exprTy}});
|
||||
}
|
||||
else if (ttv->state == TableState::Sealed)
|
||||
reportError(TypeError{function.location, CannotExtendTable{exprTy, CannotExtendTable::Property, name->index.value}});
|
||||
|
||||
ty = follow(ty);
|
||||
|
||||
if (ttv && ttv->state != TableState::Sealed)
|
||||
ttv->props[name->index.value] = {ty, /* deprecated */ false, {}, name->indexLocation};
|
||||
|
||||
if (function.func->self)
|
||||
{
|
||||
const FunctionTypeVar* funTy = get<FunctionTypeVar>(ty);
|
||||
if (!funTy)
|
||||
ice("Methods should be functions");
|
||||
|
||||
std::optional<TypeId> arg0 = first(funTy->argTypes);
|
||||
if (!arg0)
|
||||
ice("Methods should always have at least 1 argument (self)");
|
||||
}
|
||||
|
||||
checkFunctionBody(funScope, ty, *function.func);
|
||||
|
||||
if (ttv && ttv->state != TableState::Sealed)
|
||||
ttv->props[name->index.value] = {follow(quantify(funScope, ty, name->indexLocation)), /* deprecated */ false, {}, name->indexLocation};
|
||||
}
|
||||
else if (FFlag::LuauStatFunctionSimplify)
|
||||
{
|
||||
LUAU_ASSERT(function.name->is<AstExprError>());
|
||||
|
||||
ty = follow(ty);
|
||||
|
||||
checkFunctionBody(funScope, ty, *function.func);
|
||||
}
|
||||
else if (function.func->self)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify);
|
||||
|
||||
AstExprIndexName* indexName = function.name->as<AstExprIndexName>();
|
||||
if (!indexName)
|
||||
ice("member function declaration has malformed name expression");
|
||||
|
@ -1141,6 +1183,8 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
|||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify);
|
||||
|
||||
TypeId leftType = checkLValueBinding(scope, *function.name);
|
||||
|
||||
checkFunctionBody(funScope, ty, *function.func);
|
||||
|
@ -1217,6 +1261,9 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
|||
LUAU_ASSERT(ftv);
|
||||
ftv->forwardedTypeAlias = true;
|
||||
bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty};
|
||||
|
||||
if (FFlag::LuauFixIncorrectLineNumberDuplicateType)
|
||||
scope->typeAliasLocations[name] = typealias.location;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2102,9 +2149,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprUn
|
|||
|
||||
Unifier state = mkUnifier(expr.location);
|
||||
state.tryUnify(actualFunctionType, expectedFunctionType, /*isFunctionCall*/ true);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
TypeId retType = first(retTypePack).value_or(nilType);
|
||||
if (!state.errors.empty())
|
||||
|
@ -2283,9 +2328,7 @@ TypeId TypeChecker::checkRelationalOperation(
|
|||
if (!isEquality)
|
||||
{
|
||||
state.tryUnify(rhsType, lhsType);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
}
|
||||
|
||||
bool needsMetamethod = !isEquality;
|
||||
|
@ -2336,8 +2379,7 @@ TypeId TypeChecker::checkRelationalOperation(
|
|||
return errorRecoveryType(booleanType);
|
||||
}
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2347,8 +2389,7 @@ TypeId TypeChecker::checkRelationalOperation(
|
|||
state.tryUnify(
|
||||
instantiate(scope, actualFunctionType, expr.location), instantiate(scope, *metamethod, expr.location), /*isFunctionCall*/ true);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
reportErrors(state.errors);
|
||||
return booleanType;
|
||||
|
@ -2464,25 +2505,15 @@ TypeId TypeChecker::checkBinaryOperation(
|
|||
TypePackId fallbackArguments = freshTypePack(scope);
|
||||
TypeId fallbackFunctionType = addType(FunctionTypeVar(scope->level, fallbackArguments, retTypePack));
|
||||
state.errors.clear();
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
state.log.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
state.DEPRECATED_log.rollback();
|
||||
}
|
||||
state.log.clear();
|
||||
|
||||
state.tryUnify(actualFunctionType, fallbackFunctionType, /*isFunctionCall*/ true);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog && state.errors.empty())
|
||||
if (state.errors.empty())
|
||||
state.log.commit();
|
||||
else if (!state.errors.empty() && !FFlag::LuauUseCommittingTxnLog)
|
||||
state.DEPRECATED_log.rollback();
|
||||
}
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog && !hasErrors)
|
||||
if (!hasErrors)
|
||||
{
|
||||
state.log.commit();
|
||||
}
|
||||
|
@ -2729,13 +2760,11 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex
|
|||
TypeId retType = indexer->indexResultType;
|
||||
if (!state.errors.empty())
|
||||
{
|
||||
if (!FFlag::LuauUseCommittingTxnLog)
|
||||
state.DEPRECATED_log.rollback();
|
||||
|
||||
reportError(expr.location, UnknownProperty{lhs, name});
|
||||
retType = errorRecoveryType(retType);
|
||||
}
|
||||
else if (FFlag::LuauUseCommittingTxnLog)
|
||||
else
|
||||
state.log.commit();
|
||||
|
||||
return retType;
|
||||
|
@ -3209,7 +3238,7 @@ ExprResult<TypePackId> TypeChecker::checkExprPack(const ScopePtr& scope, const A
|
|||
}
|
||||
|
||||
// Returns the minimum number of arguments the argument list can accept.
|
||||
static size_t getMinParameterCount(TypePackId tp)
|
||||
static size_t getMinParameterCount_DEPRECATED(TypePackId tp)
|
||||
{
|
||||
size_t minCount = 0;
|
||||
size_t optionalCount = 0;
|
||||
|
@ -3235,6 +3264,32 @@ static size_t getMinParameterCount(TypePackId tp)
|
|||
return minCount;
|
||||
}
|
||||
|
||||
static size_t getMinParameterCount(TxnLog* log, TypePackId tp)
|
||||
{
|
||||
size_t minCount = 0;
|
||||
size_t optionalCount = 0;
|
||||
|
||||
auto it = begin(tp, log);
|
||||
auto endIter = end(tp);
|
||||
|
||||
while (it != endIter)
|
||||
{
|
||||
TypeId ty = *it;
|
||||
if (isOptional(ty))
|
||||
++optionalCount;
|
||||
else
|
||||
{
|
||||
minCount += optionalCount;
|
||||
optionalCount = 0;
|
||||
minCount++;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
return minCount;
|
||||
}
|
||||
|
||||
void TypeChecker::checkArgumentList(
|
||||
const ScopePtr& scope, Unifier& state, TypePackId argPack, TypePackId paramPack, const std::vector<Location>& argLocations)
|
||||
{
|
||||
|
@ -3248,396 +3303,199 @@ void TypeChecker::checkArgumentList(
|
|||
|
||||
size_t paramIndex = 0;
|
||||
|
||||
size_t minParams = getMinParameterCount(paramPack);
|
||||
size_t minParams = FFlag::LuauFixIncorrectLineNumberDuplicateType ? 0 : getMinParameterCount_DEPRECATED(paramPack);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
while (true)
|
||||
{
|
||||
while (true)
|
||||
state.location = paramIndex < argLocations.size() ? argLocations[paramIndex] : state.location;
|
||||
|
||||
if (argIter == endIter && paramIter == endIter)
|
||||
{
|
||||
state.location = paramIndex < argLocations.size() ? argLocations[paramIndex] : state.location;
|
||||
std::optional<TypePackId> argTail = argIter.tail();
|
||||
std::optional<TypePackId> paramTail = paramIter.tail();
|
||||
|
||||
if (argIter == endIter && paramIter == endIter)
|
||||
// If we hit the end of both type packs simultaneously, then there are definitely no further type
|
||||
// errors to report. All we need to do is tie up any free tails.
|
||||
//
|
||||
// If one side has a free tail and the other has none at all, we create an empty pack and bind the
|
||||
// free tail to that.
|
||||
|
||||
if (argTail)
|
||||
{
|
||||
std::optional<TypePackId> argTail = argIter.tail();
|
||||
std::optional<TypePackId> paramTail = paramIter.tail();
|
||||
|
||||
// If we hit the end of both type packs simultaneously, then there are definitely no further type
|
||||
// errors to report. All we need to do is tie up any free tails.
|
||||
//
|
||||
// If one side has a free tail and the other has none at all, we create an empty pack and bind the
|
||||
// free tail to that.
|
||||
|
||||
if (argTail)
|
||||
if (state.log.getMutable<Unifiable::Free>(state.log.follow(*argTail)))
|
||||
{
|
||||
if (state.log.getMutable<Unifiable::Free>(state.log.follow(*argTail)))
|
||||
{
|
||||
if (paramTail)
|
||||
state.tryUnify(*paramTail, *argTail);
|
||||
else
|
||||
state.log.replace(*argTail, TypePackVar(TypePack{{}}));
|
||||
}
|
||||
}
|
||||
else if (paramTail)
|
||||
{
|
||||
// argTail is definitely empty
|
||||
if (state.log.getMutable<Unifiable::Free>(state.log.follow(*paramTail)))
|
||||
state.log.replace(*paramTail, TypePackVar(TypePack{{}}));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (argIter == endIter)
|
||||
{
|
||||
// Not enough arguments.
|
||||
|
||||
// Might be ok if we are forwarding a vararg along. This is a common thing to occur in nonstrict mode.
|
||||
if (argIter.tail())
|
||||
{
|
||||
TypePackId tail = *argIter.tail();
|
||||
if (state.log.getMutable<Unifiable::Error>(tail))
|
||||
{
|
||||
// Unify remaining parameters so we don't leave any free-types hanging around.
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
state.tryUnify(errorRecoveryType(anyType), *paramIter);
|
||||
++paramIter;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
||||
{
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
state.tryUnify(vtp->ty, *paramIter);
|
||||
++paramIter;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (state.log.getMutable<FreeTypePack>(tail))
|
||||
{
|
||||
std::vector<TypeId> rest;
|
||||
rest.reserve(std::distance(paramIter, endIter));
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
rest.push_back(*paramIter);
|
||||
++paramIter;
|
||||
}
|
||||
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}});
|
||||
state.tryUnify(varPack, tail);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If any remaining unfulfilled parameters are nonoptional, this is a problem.
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
TypeId t = state.log.follow(*paramIter);
|
||||
if (isOptional(t))
|
||||
{
|
||||
} // ok
|
||||
else if (state.log.getMutable<ErrorTypeVar>(t))
|
||||
{
|
||||
} // ok
|
||||
else if (isNonstrictMode() && state.log.getMutable<AnyTypeVar>(t))
|
||||
{
|
||||
} // ok
|
||||
if (paramTail)
|
||||
state.tryUnify(*paramTail, *argTail);
|
||||
else
|
||||
{
|
||||
state.reportError(TypeError{state.location, CountMismatch{minParams, paramIndex}});
|
||||
return;
|
||||
}
|
||||
++paramIter;
|
||||
state.log.replace(*argTail, TypePackVar(TypePack{{}}));
|
||||
}
|
||||
}
|
||||
else if (paramIter == endIter)
|
||||
else if (paramTail)
|
||||
{
|
||||
// too many parameters passed
|
||||
if (!paramIter.tail())
|
||||
{
|
||||
while (argIter != endIter)
|
||||
{
|
||||
// The use of unify here is deliberate. We don't want this unification
|
||||
// to be undoable.
|
||||
unify(errorRecoveryType(scope), *argIter, state.location);
|
||||
++argIter;
|
||||
}
|
||||
// For this case, we want the error span to cover every errant extra parameter
|
||||
Location location = state.location;
|
||||
if (!argLocations.empty())
|
||||
location = {state.location.begin, argLocations.back().end};
|
||||
state.reportError(TypeError{location, CountMismatch{minParams, std::distance(begin(argPack), end(argPack))}});
|
||||
return;
|
||||
}
|
||||
TypePackId tail = state.log.follow(*paramIter.tail());
|
||||
// argTail is definitely empty
|
||||
if (state.log.getMutable<Unifiable::Free>(state.log.follow(*paramTail)))
|
||||
state.log.replace(*paramTail, TypePackVar(TypePack{{}}));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (argIter == endIter)
|
||||
{
|
||||
// Not enough arguments.
|
||||
|
||||
// Might be ok if we are forwarding a vararg along. This is a common thing to occur in nonstrict mode.
|
||||
if (argIter.tail())
|
||||
{
|
||||
TypePackId tail = *argIter.tail();
|
||||
if (state.log.getMutable<Unifiable::Error>(tail))
|
||||
{
|
||||
// Function is variadic. Ok.
|
||||
// Unify remaining parameters so we don't leave any free-types hanging around.
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
state.tryUnify(errorRecoveryType(anyType), *paramIter);
|
||||
++paramIter;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
||||
{
|
||||
// Function is variadic and requires that all subsequent parameters
|
||||
// be compatible with a type.
|
||||
size_t argIndex = paramIndex;
|
||||
while (argIter != endIter)
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
Location location = state.location;
|
||||
|
||||
if (argIndex < argLocations.size())
|
||||
location = argLocations[argIndex];
|
||||
|
||||
unify(*argIter, vtp->ty, location);
|
||||
++argIter;
|
||||
++argIndex;
|
||||
state.tryUnify(vtp->ty, *paramIter);
|
||||
++paramIter;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (state.log.getMutable<FreeTypePack>(tail))
|
||||
{
|
||||
// Create a type pack out of the remaining argument types
|
||||
// and unify it with the tail.
|
||||
std::vector<TypeId> rest;
|
||||
rest.reserve(std::distance(argIter, endIter));
|
||||
while (argIter != endIter)
|
||||
rest.reserve(std::distance(paramIter, endIter));
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
rest.push_back(*argIter);
|
||||
++argIter;
|
||||
rest.push_back(*paramIter);
|
||||
++paramIter;
|
||||
}
|
||||
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}});
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}});
|
||||
state.tryUnify(varPack, tail);
|
||||
return;
|
||||
}
|
||||
else if (state.log.getMutable<FreeTypePack>(tail))
|
||||
{
|
||||
state.log.replace(tail, TypePackVar(TypePack{{}}));
|
||||
return;
|
||||
}
|
||||
else if (state.log.getMutable<GenericTypePack>(tail))
|
||||
{
|
||||
// For this case, we want the error span to cover every errant extra parameter
|
||||
Location location = state.location;
|
||||
if (!argLocations.empty())
|
||||
location = {state.location.begin, argLocations.back().end};
|
||||
// TODO: Better error message?
|
||||
state.reportError(TypeError{location, CountMismatch{minParams, std::distance(begin(argPack), end(argPack))}});
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// If any remaining unfulfilled parameters are nonoptional, this is a problem.
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
unifyWithInstantiationIfNeeded(scope, *argIter, *paramIter, state);
|
||||
++argIter;
|
||||
TypeId t = state.log.follow(*paramIter);
|
||||
if (isOptional(t))
|
||||
{
|
||||
} // ok
|
||||
else if (state.log.getMutable<ErrorTypeVar>(t))
|
||||
{
|
||||
} // ok
|
||||
else if (isNonstrictMode() && state.log.getMutable<AnyTypeVar>(t))
|
||||
{
|
||||
} // ok
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes)
|
||||
minParams = getMinParameterCount(&state.log, paramPack);
|
||||
state.reportError(TypeError{state.location, CountMismatch{minParams, paramIndex}});
|
||||
return;
|
||||
}
|
||||
++paramIter;
|
||||
}
|
||||
|
||||
++paramIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
else if (paramIter == endIter)
|
||||
{
|
||||
state.location = paramIndex < argLocations.size() ? argLocations[paramIndex] : state.location;
|
||||
|
||||
if (argIter == endIter && paramIter == endIter)
|
||||
// too many parameters passed
|
||||
if (!paramIter.tail())
|
||||
{
|
||||
std::optional<TypePackId> argTail = argIter.tail();
|
||||
std::optional<TypePackId> paramTail = paramIter.tail();
|
||||
|
||||
// If we hit the end of both type packs simultaneously, then there are definitely no further type
|
||||
// errors to report. All we need to do is tie up any free tails.
|
||||
//
|
||||
// If one side has a free tail and the other has none at all, we create an empty pack and bind the
|
||||
// free tail to that.
|
||||
|
||||
if (argTail)
|
||||
while (argIter != endIter)
|
||||
{
|
||||
if (get<Unifiable::Free>(*argTail))
|
||||
{
|
||||
if (paramTail)
|
||||
state.tryUnify(*paramTail, *argTail);
|
||||
else
|
||||
{
|
||||
state.DEPRECATED_log(*argTail);
|
||||
*asMutable(*argTail) = TypePack{{}};
|
||||
}
|
||||
}
|
||||
// The use of unify here is deliberate. We don't want this unification
|
||||
// to be undoable.
|
||||
unify(errorRecoveryType(scope), *argIter, state.location);
|
||||
++argIter;
|
||||
}
|
||||
else if (paramTail)
|
||||
// For this case, we want the error span to cover every errant extra parameter
|
||||
Location location = state.location;
|
||||
if (!argLocations.empty())
|
||||
location = {state.location.begin, argLocations.back().end};
|
||||
|
||||
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes)
|
||||
minParams = getMinParameterCount(&state.log, paramPack);
|
||||
state.reportError(TypeError{location, CountMismatch{minParams, std::distance(begin(argPack), end(argPack))}});
|
||||
return;
|
||||
}
|
||||
TypePackId tail = state.log.follow(*paramIter.tail());
|
||||
|
||||
if (state.log.getMutable<Unifiable::Error>(tail))
|
||||
{
|
||||
// Function is variadic. Ok.
|
||||
return;
|
||||
}
|
||||
else if (auto vtp = state.log.getMutable<VariadicTypePack>(tail))
|
||||
{
|
||||
// Function is variadic and requires that all subsequent parameters
|
||||
// be compatible with a type.
|
||||
size_t argIndex = paramIndex;
|
||||
while (argIter != endIter)
|
||||
{
|
||||
// argTail is definitely empty
|
||||
if (get<Unifiable::Free>(*paramTail))
|
||||
{
|
||||
state.DEPRECATED_log(*paramTail);
|
||||
*asMutable(*paramTail) = TypePack{{}};
|
||||
}
|
||||
Location location = state.location;
|
||||
|
||||
if (argIndex < argLocations.size())
|
||||
location = argLocations[argIndex];
|
||||
|
||||
unify(*argIter, vtp->ty, location);
|
||||
++argIter;
|
||||
++argIndex;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (argIter == endIter)
|
||||
else if (state.log.getMutable<FreeTypePack>(tail))
|
||||
{
|
||||
// Not enough arguments.
|
||||
|
||||
// Might be ok if we are forwarding a vararg along. This is a common thing to occur in nonstrict mode.
|
||||
if (argIter.tail())
|
||||
// Create a type pack out of the remaining argument types
|
||||
// and unify it with the tail.
|
||||
std::vector<TypeId> rest;
|
||||
rest.reserve(std::distance(argIter, endIter));
|
||||
while (argIter != endIter)
|
||||
{
|
||||
TypePackId tail = *argIter.tail();
|
||||
if (get<Unifiable::Error>(tail))
|
||||
{
|
||||
// Unify remaining parameters so we don't leave any free-types hanging around.
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
state.tryUnify(*paramIter, errorRecoveryType(anyType));
|
||||
++paramIter;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (auto vtp = get<VariadicTypePack>(tail))
|
||||
{
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
state.tryUnify(*paramIter, vtp->ty);
|
||||
++paramIter;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (get<FreeTypePack>(tail))
|
||||
{
|
||||
std::vector<TypeId> rest;
|
||||
rest.reserve(std::distance(paramIter, endIter));
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
rest.push_back(*paramIter);
|
||||
++paramIter;
|
||||
}
|
||||
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}});
|
||||
state.tryUnify(varPack, tail);
|
||||
return;
|
||||
}
|
||||
rest.push_back(*argIter);
|
||||
++argIter;
|
||||
}
|
||||
|
||||
// If any remaining unfulfilled parameters are nonoptional, this is a problem.
|
||||
while (paramIter != endIter)
|
||||
{
|
||||
TypeId t = follow(*paramIter);
|
||||
if (isOptional(t))
|
||||
{
|
||||
} // ok
|
||||
else if (get<ErrorTypeVar>(t))
|
||||
{
|
||||
} // ok
|
||||
else if (isNonstrictMode() && get<AnyTypeVar>(t))
|
||||
{
|
||||
} // ok
|
||||
else
|
||||
{
|
||||
state.reportError(TypeError{state.location, CountMismatch{minParams, paramIndex}});
|
||||
return;
|
||||
}
|
||||
++paramIter;
|
||||
}
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}});
|
||||
state.tryUnify(varPack, tail);
|
||||
return;
|
||||
}
|
||||
else if (paramIter == endIter)
|
||||
else if (state.log.getMutable<FreeTypePack>(tail))
|
||||
{
|
||||
// too many parameters passed
|
||||
if (!paramIter.tail())
|
||||
{
|
||||
while (argIter != endIter)
|
||||
{
|
||||
unify(*argIter, errorRecoveryType(scope), state.location);
|
||||
++argIter;
|
||||
}
|
||||
// For this case, we want the error span to cover every errant extra parameter
|
||||
Location location = state.location;
|
||||
if (!argLocations.empty())
|
||||
location = {state.location.begin, argLocations.back().end};
|
||||
state.reportError(TypeError{location, CountMismatch{minParams, std::distance(begin(argPack), end(argPack))}});
|
||||
return;
|
||||
}
|
||||
TypePackId tail = *paramIter.tail();
|
||||
|
||||
if (get<Unifiable::Error>(tail))
|
||||
{
|
||||
// Function is variadic. Ok.
|
||||
return;
|
||||
}
|
||||
else if (auto vtp = get<VariadicTypePack>(tail))
|
||||
{
|
||||
// Function is variadic and requires that all subsequent parameters
|
||||
// be compatible with a type.
|
||||
size_t argIndex = paramIndex;
|
||||
while (argIter != endIter)
|
||||
{
|
||||
Location location = state.location;
|
||||
|
||||
if (argIndex < argLocations.size())
|
||||
location = argLocations[argIndex];
|
||||
|
||||
unify(*argIter, vtp->ty, location);
|
||||
++argIter;
|
||||
++argIndex;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (get<FreeTypePack>(tail))
|
||||
{
|
||||
// Create a type pack out of the remaining argument types
|
||||
// and unify it with the tail.
|
||||
std::vector<TypeId> rest;
|
||||
rest.reserve(std::distance(argIter, endIter));
|
||||
while (argIter != endIter)
|
||||
{
|
||||
rest.push_back(*argIter);
|
||||
++argIter;
|
||||
}
|
||||
|
||||
TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, argIter.tail()}});
|
||||
state.tryUnify(tail, varPack);
|
||||
return;
|
||||
}
|
||||
else if (get<FreeTypePack>(tail))
|
||||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
state.log.replace(tail, TypePackVar(TypePack{{}}));
|
||||
}
|
||||
else
|
||||
{
|
||||
state.DEPRECATED_log(tail);
|
||||
*asMutable(tail) = TypePack{};
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (get<GenericTypePack>(tail))
|
||||
{
|
||||
// For this case, we want the error span to cover every errant extra parameter
|
||||
Location location = state.location;
|
||||
if (!argLocations.empty())
|
||||
location = {state.location.begin, argLocations.back().end};
|
||||
// TODO: Better error message?
|
||||
state.reportError(TypeError{location, CountMismatch{minParams, std::distance(begin(argPack), end(argPack))}});
|
||||
return;
|
||||
}
|
||||
state.log.replace(tail, TypePackVar(TypePack{{}}));
|
||||
return;
|
||||
}
|
||||
else
|
||||
else if (state.log.getMutable<GenericTypePack>(tail))
|
||||
{
|
||||
unifyWithInstantiationIfNeeded(scope, *argIter, *paramIter, state);
|
||||
++argIter;
|
||||
++paramIter;
|
||||
// For this case, we want the error span to cover every errant extra parameter
|
||||
Location location = state.location;
|
||||
if (!argLocations.empty())
|
||||
location = {state.location.begin, argLocations.back().end};
|
||||
// TODO: Better error message?
|
||||
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes)
|
||||
minParams = getMinParameterCount(&state.log, paramPack);
|
||||
state.reportError(TypeError{location, CountMismatch{minParams, std::distance(begin(argPack), end(argPack))}});
|
||||
return;
|
||||
}
|
||||
|
||||
++paramIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
unifyWithInstantiationIfNeeded(scope, *argIter, *paramIter, state);
|
||||
++argIter;
|
||||
++paramIter;
|
||||
}
|
||||
|
||||
++paramIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3882,9 +3740,6 @@ std::optional<ExprResult<TypePackId>> TypeChecker::checkCallOverload(const Scope
|
|||
checkArgumentList(scope, state, retPack, ftv->retType, /*argLocations*/ {});
|
||||
if (!state.errors.empty())
|
||||
{
|
||||
if (!FFlag::LuauUseCommittingTxnLog)
|
||||
state.DEPRECATED_log.rollback();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -3912,14 +3767,10 @@ std::optional<ExprResult<TypePackId>> TypeChecker::checkCallOverload(const Scope
|
|||
overloadsThatDont.push_back(fn);
|
||||
|
||||
errors.emplace_back(std::move(state.errors), args->head, ftv);
|
||||
|
||||
if (!FFlag::LuauUseCommittingTxnLog)
|
||||
state.DEPRECATED_log.rollback();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
if (isNonstrictMode() && !expr.self && expr.func->is<AstExprIndexName>() && ftv->hasSelf)
|
||||
{
|
||||
|
@ -3976,8 +3827,7 @@ bool TypeChecker::handleSelfCallMismatch(const ScopePtr& scope, const AstExprCal
|
|||
|
||||
if (editedState.errors.empty())
|
||||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
editedState.log.commit();
|
||||
editedState.log.commit();
|
||||
|
||||
reportError(TypeError{expr.location, FunctionDoesNotTakeSelf{}});
|
||||
// This is a little bit suspect: If this overload would work with a . replaced by a :
|
||||
|
@ -3987,8 +3837,6 @@ bool TypeChecker::handleSelfCallMismatch(const ScopePtr& scope, const AstExprCal
|
|||
// checkArgumentList(scope, editedState, retPack, ftv->retType, retLocations, CountMismatch::Return);
|
||||
return true;
|
||||
}
|
||||
else if (!FFlag::LuauUseCommittingTxnLog)
|
||||
editedState.DEPRECATED_log.rollback();
|
||||
}
|
||||
else if (ftv->hasSelf)
|
||||
{
|
||||
|
@ -4010,8 +3858,7 @@ bool TypeChecker::handleSelfCallMismatch(const ScopePtr& scope, const AstExprCal
|
|||
|
||||
if (editedState.errors.empty())
|
||||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
editedState.log.commit();
|
||||
editedState.log.commit();
|
||||
|
||||
reportError(TypeError{expr.location, FunctionRequiresSelf{}});
|
||||
// This is a little bit suspect: If this overload would work with a : replaced by a .
|
||||
|
@ -4021,8 +3868,6 @@ bool TypeChecker::handleSelfCallMismatch(const ScopePtr& scope, const AstExprCal
|
|||
// checkArgumentList(scope, editedState, retPack, ftv->retType, retLocations, CountMismatch::Return);
|
||||
return true;
|
||||
}
|
||||
else if (!FFlag::LuauUseCommittingTxnLog)
|
||||
editedState.DEPRECATED_log.rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4082,7 +3927,7 @@ void TypeChecker::reportOverloadResolutionError(const ScopePtr& scope, const Ast
|
|||
checkArgumentList(scope, state, argPack, ftv->argTypes, argLocations);
|
||||
}
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog && state.errors.empty())
|
||||
if (state.errors.empty())
|
||||
state.log.commit();
|
||||
|
||||
if (i > 0)
|
||||
|
@ -4092,9 +3937,6 @@ void TypeChecker::reportOverloadResolutionError(const ScopePtr& scope, const Ast
|
|||
s += "and ";
|
||||
|
||||
s += toString(overload);
|
||||
|
||||
if (!FFlag::LuauUseCommittingTxnLog)
|
||||
state.DEPRECATED_log.rollback();
|
||||
}
|
||||
|
||||
if (overloadsThatMatchArgCount.size() == 0)
|
||||
|
@ -4168,24 +4010,16 @@ ExprResult<TypePackId> TypeChecker::checkExprList(const ScopePtr& scope, const L
|
|||
// just performed. There's not a great way to pass that into checkExpr. Instead, we store
|
||||
// the inverse of the current log, and commit it. When we're done, we'll commit all the
|
||||
// inverses. This isn't optimal, and a better solution is welcome here.
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
inverseLogs.push_back(state.log.inverse());
|
||||
state.log.commit();
|
||||
}
|
||||
inverseLogs.push_back(state.log.inverse());
|
||||
state.log.commit();
|
||||
}
|
||||
|
||||
tp->head.push_back(actualType);
|
||||
}
|
||||
}
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
for (TxnLog& log : inverseLogs)
|
||||
log.commit();
|
||||
}
|
||||
else
|
||||
state.DEPRECATED_log.rollback();
|
||||
for (TxnLog& log : inverseLogs)
|
||||
log.commit();
|
||||
|
||||
return {pack, predicates};
|
||||
}
|
||||
|
@ -4294,8 +4128,7 @@ bool TypeChecker::unify(TypeId subTy, TypeId superTy, const Location& location,
|
|||
Unifier state = mkUnifier(location);
|
||||
state.tryUnify(subTy, superTy, options.isFunctionCall);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
reportErrors(state.errors);
|
||||
|
||||
|
@ -4308,8 +4141,7 @@ bool TypeChecker::unify(TypePackId subTy, TypePackId superTy, const Location& lo
|
|||
state.ctx = ctx;
|
||||
state.tryUnify(subTy, superTy);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
reportErrors(state.errors);
|
||||
|
||||
|
@ -4321,8 +4153,7 @@ bool TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId s
|
|||
Unifier state = mkUnifier(location);
|
||||
unifyWithInstantiationIfNeeded(scope, subTy, superTy, state);
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
reportErrors(state.errors);
|
||||
|
||||
|
@ -4352,31 +4183,18 @@ void TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId s
|
|||
if (subTy == instantiated)
|
||||
{
|
||||
// Instantiating the argument made no difference, so just report any child errors
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.concat(std::move(child.log));
|
||||
else
|
||||
state.DEPRECATED_log.concat(std::move(child.DEPRECATED_log));
|
||||
state.log.concat(std::move(child.log));
|
||||
|
||||
state.errors.insert(state.errors.end(), child.errors.begin(), child.errors.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FFlag::LuauUseCommittingTxnLog)
|
||||
child.DEPRECATED_log.rollback();
|
||||
|
||||
state.tryUnify(instantiated, superTy, /*isFunctionCall*/ false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
state.log.concat(std::move(child.log));
|
||||
}
|
||||
else
|
||||
{
|
||||
state.DEPRECATED_log.concat(std::move(child.DEPRECATED_log));
|
||||
}
|
||||
state.log.concat(std::move(child.log));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4540,7 +4358,7 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
|
|||
|
||||
TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log)
|
||||
{
|
||||
Instantiation instantiation{FFlag::LuauUseCommittingTxnLog ? log : TxnLog::empty(), ¤tModule->internalTypes, scope->level};
|
||||
Instantiation instantiation{log, ¤tModule->internalTypes, scope->level};
|
||||
std::optional<TypeId> instantiated = instantiation.substitute(ty);
|
||||
if (instantiated.has_value())
|
||||
return *instantiated;
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
#include <stdexcept>
|
||||
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -51,16 +49,8 @@ TypePackIterator::TypePackIterator(TypePackId typePack, const TxnLog* log)
|
|||
{
|
||||
while (tp && tp->head.empty())
|
||||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||
tp = currentTypePack ? log->getMutable<TypePack>(currentTypePack) : nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentTypePack = tp->tail ? follow(*tp->tail) : nullptr;
|
||||
tp = currentTypePack ? get<TypePack>(currentTypePack) : nullptr;
|
||||
}
|
||||
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||
tp = currentTypePack ? log->getMutable<TypePack>(currentTypePack) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,16 +61,8 @@ TypePackIterator& TypePackIterator::operator++()
|
|||
++currentIndex;
|
||||
while (tp && currentIndex >= tp->head.size())
|
||||
{
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
{
|
||||
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||
tp = currentTypePack ? log->getMutable<TypePack>(currentTypePack) : nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentTypePack = tp->tail ? follow(*tp->tail) : nullptr;
|
||||
tp = currentTypePack ? get<TypePack>(currentTypePack) : nullptr;
|
||||
}
|
||||
currentTypePack = tp->tail ? log->follow(*tp->tail) : nullptr;
|
||||
tp = currentTypePack ? log->getMutable<TypePack>(currentTypePack) : nullptr;
|
||||
|
||||
currentIndex = 0;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -229,19 +229,46 @@ LUA_API void lua_setthreaddata(lua_State* L, void* data);
|
|||
|
||||
enum lua_GCOp
|
||||
{
|
||||
/* stop and resume incremental garbage collection */
|
||||
LUA_GCSTOP,
|
||||
LUA_GCRESTART,
|
||||
|
||||
/* run a full GC cycle; not recommended for latency sensitive applications */
|
||||
LUA_GCCOLLECT,
|
||||
|
||||
/* return the heap size in KB and the remainder in bytes */
|
||||
LUA_GCCOUNT,
|
||||
LUA_GCCOUNTB,
|
||||
|
||||
/* return 1 if GC is active (not stopped); note that GC may not be actively collecting even if it's running */
|
||||
LUA_GCISRUNNING,
|
||||
|
||||
// garbage collection is handled by 'assists' that perform some amount of GC work matching pace of allocation
|
||||
// explicit GC steps allow to perform some amount of work at custom points to offset the need for GC assists
|
||||
// note that GC might also be paused for some duration (until bytes allocated meet the threshold)
|
||||
// if an explicit step is performed during this pause, it will trigger the start of the next collection cycle
|
||||
/*
|
||||
** perform an explicit GC step, with the step size specified in KB
|
||||
**
|
||||
** garbage collection is handled by 'assists' that perform some amount of GC work matching pace of allocation
|
||||
** explicit GC steps allow to perform some amount of work at custom points to offset the need for GC assists
|
||||
** note that GC might also be paused for some duration (until bytes allocated meet the threshold)
|
||||
** if an explicit step is performed during this pause, it will trigger the start of the next collection cycle
|
||||
*/
|
||||
LUA_GCSTEP,
|
||||
|
||||
/*
|
||||
** tune GC parameters G (goal), S (step multiplier) and step size (usually best left ignored)
|
||||
**
|
||||
** garbage collection is incremental and tries to maintain the heap size to balance memory and performance overhead
|
||||
** this overhead is determined by G (goal) which is the ratio between total heap size and the amount of live data in it
|
||||
** G is specified in percentages; by default G=200% which means that the heap is allowed to grow to ~2x the size of live data.
|
||||
**
|
||||
** collector tries to collect S% of allocated bytes by interrupting the application after step size bytes were allocated.
|
||||
** when S is too small, collector may not be able to catch up and the effective goal that can be reached will be larger.
|
||||
** S is specified in percentages; by default S=200% which means that collector will run at ~2x the pace of allocations.
|
||||
**
|
||||
** it is recommended to set S in the interval [100 / (G - 100), 100 + 100 / (G - 100))] with a minimum value of 150%; for example:
|
||||
** - for G=200%, S should be in the interval [150%, 200%]
|
||||
** - for G=150%, S should be in the interval [200%, 300%]
|
||||
** - for G=125%, S should be in the interval [400%, 500%]
|
||||
*/
|
||||
LUA_GCSETGOAL,
|
||||
LUA_GCSETSTEPMUL,
|
||||
LUA_GCSETSTEPSIZE,
|
||||
|
|
|
@ -59,33 +59,6 @@
|
|||
#define LUA_IDSIZE 256
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ LUAI_GCGOAL defines the desired top heap size in relation to the live heap
|
||||
@* size at the end of the GC cycle
|
||||
** CHANGE it if you want the GC to run faster or slower (higher values
|
||||
** mean larger GC pauses which mean slower collection.) You can also change
|
||||
** this value dynamically.
|
||||
*/
|
||||
#ifndef LUAI_GCGOAL
|
||||
#define LUAI_GCGOAL 200 /* 200% (allow heap to double compared to live heap size) */
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ LUAI_GCSTEPMUL / LUAI_GCSTEPSIZE define the default speed of garbage collection
|
||||
@* relative to memory allocation.
|
||||
** Every LUAI_GCSTEPSIZE KB allocated, incremental collector collects LUAI_GCSTEPSIZE
|
||||
** times LUAI_GCSTEPMUL% bytes.
|
||||
** CHANGE it if you want to change the granularity of the garbage
|
||||
** collection.
|
||||
*/
|
||||
#ifndef LUAI_GCSTEPMUL
|
||||
#define LUAI_GCSTEPMUL 200 /* GC runs 'twice the speed' of memory allocation */
|
||||
#endif
|
||||
|
||||
#ifndef LUAI_GCSTEPSIZE
|
||||
#define LUAI_GCSTEPSIZE 1 /* GC runs every KB of memory allocation */
|
||||
#endif
|
||||
|
||||
/* LUA_MINSTACK is the guaranteed number of Lua stack slots available to a C function */
|
||||
#ifndef LUA_MINSTACK
|
||||
#define LUA_MINSTACK 20
|
||||
|
|
|
@ -54,6 +54,8 @@ LUALIB_API lua_State* luaL_newstate(void);
|
|||
|
||||
LUALIB_API const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint);
|
||||
|
||||
LUALIB_API const char* luaL_typename(lua_State* L, int idx);
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
** some useful macros
|
||||
|
@ -66,8 +68,6 @@ LUALIB_API const char* luaL_findtable(lua_State* L, int idx, const char* fname,
|
|||
#define luaL_checkstring(L, n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L, n, d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
|
||||
#define luaL_typename(L, i) lua_typename(L, lua_type(L, (i)))
|
||||
|
||||
#define luaL_getmetatable(L, n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||
|
||||
#define luaL_opt(L, f, n, d) (lua_isnoneornil(L, (n)) ? (d) : f(L, (n)))
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauMorePreciseLuaLTypeName, false)
|
||||
|
||||
/* convert a stack index to positive */
|
||||
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
||||
|
||||
|
@ -333,6 +335,19 @@ const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
const char* luaL_typename(lua_State* L, int idx)
|
||||
{
|
||||
if (DFFlag::LuauMorePreciseLuaLTypeName)
|
||||
{
|
||||
const TValue* obj = luaA_toobject(L, idx);
|
||||
return luaT_objtypename(L, obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
return lua_typename(L, lua_type(L, idx));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauMorePreciseLuaLTypeName)
|
||||
|
||||
static void writestring(const char* s, size_t l)
|
||||
{
|
||||
fwrite(s, 1, l, stdout);
|
||||
|
@ -186,7 +188,14 @@ static int luaB_gcinfo(lua_State* L)
|
|||
static int luaB_type(lua_State* L)
|
||||
{
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushstring(L, luaL_typename(L, 1));
|
||||
if (DFFlag::LuauMorePreciseLuaLTypeName)
|
||||
{
|
||||
lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pushstring(L, luaL_typename(L, 1));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,13 @@
|
|||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
/*
|
||||
** Default settings for GC tunables (settable via lua_gc)
|
||||
*/
|
||||
#define LUAI_GCGOAL 200 /* 200% (allow heap to double compared to live heap size) */
|
||||
#define LUAI_GCSTEPMUL 200 /* GC runs 'twice the speed' of memory allocation */
|
||||
#define LUAI_GCSTEPSIZE 1 /* GC runs every KB of memory allocation */
|
||||
|
||||
/*
|
||||
** Possible states of the Garbage Collector
|
||||
*/
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
||||
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
LUAU_FASTFLAG(LuauTableCloneType)
|
||||
|
||||
using namespace Luau;
|
||||
|
@ -1912,14 +1911,9 @@ local bar: @1= foo
|
|||
CHECK(!ac.entryMap.count("foo"));
|
||||
}
|
||||
|
||||
// Switch back to TEST_CASE_FIXTURE with regular ACFixture when removing the
|
||||
// LuauUseCommittingTxnLog flag.
|
||||
TEST_CASE("type_correct_function_no_parenthesis")
|
||||
TEST_CASE_FIXTURE(ACFixture, "type_correct_function_no_parenthesis")
|
||||
{
|
||||
ScopedFastFlag sff_LuauUseCommittingTxnLog = ScopedFastFlag("LuauUseCommittingTxnLog", true);
|
||||
ACFixture fix;
|
||||
|
||||
fix.check(R"(
|
||||
check(R"(
|
||||
local function target(a: (number) -> number) return a(4) end
|
||||
local function bar1(a: number) return -a end
|
||||
local function bar2(a: string) return a .. 'x' end
|
||||
|
@ -1927,7 +1921,7 @@ local function bar2(a: string) return a .. 'x' end
|
|||
return target(b@1
|
||||
)");
|
||||
|
||||
auto ac = fix.autocomplete('1');
|
||||
auto ac = autocomplete('1');
|
||||
|
||||
CHECK(ac.entryMap.count("bar1"));
|
||||
CHECK(ac.entryMap["bar1"].typeCorrect == TypeCorrectKind::Correct);
|
||||
|
@ -1937,8 +1931,6 @@ return target(b@1
|
|||
|
||||
TEST_CASE_FIXTURE(ACFixture, "function_in_assignment_has_parentheses")
|
||||
{
|
||||
ScopedFastFlag luauAutocompleteAvoidMutation("LuauAutocompleteAvoidMutation", true);
|
||||
|
||||
check(R"(
|
||||
local function bar(a: number) return -a end
|
||||
local abc = b@1
|
||||
|
@ -1952,8 +1944,6 @@ local abc = b@1
|
|||
|
||||
TEST_CASE_FIXTURE(ACFixture, "function_result_passed_to_function_has_parentheses")
|
||||
{
|
||||
ScopedFastFlag luauAutocompleteAvoidMutation("LuauAutocompleteAvoidMutation", true);
|
||||
|
||||
check(R"(
|
||||
local function foo() return 1 end
|
||||
local function bar(a: number) return -a end
|
||||
|
@ -1978,14 +1968,9 @@ local fp: @1= f
|
|||
CHECK(ac.entryMap.count("({ x: number, y: number }) -> number"));
|
||||
}
|
||||
|
||||
// Switch back to TEST_CASE_FIXTURE with regular ACFixture when removing the
|
||||
// LuauUseCommittingTxnLog flag.
|
||||
TEST_CASE("type_correct_keywords")
|
||||
TEST_CASE_FIXTURE(ACFixture, "type_correct_keywords")
|
||||
{
|
||||
ScopedFastFlag sff_LuauUseCommittingTxnLog = ScopedFastFlag("LuauUseCommittingTxnLog", true);
|
||||
ACFixture fix;
|
||||
|
||||
fix.check(R"(
|
||||
check(R"(
|
||||
local function a(x: boolean) end
|
||||
local function b(x: number?) end
|
||||
local function c(x: (number) -> string) end
|
||||
|
@ -2002,26 +1987,26 @@ local dc = d(f@4)
|
|||
local ec = e(f@5)
|
||||
)");
|
||||
|
||||
auto ac = fix.autocomplete('1');
|
||||
auto ac = autocomplete('1');
|
||||
CHECK(ac.entryMap.count("tru"));
|
||||
CHECK(ac.entryMap["tru"].typeCorrect == TypeCorrectKind::None);
|
||||
CHECK(ac.entryMap["true"].typeCorrect == TypeCorrectKind::Correct);
|
||||
CHECK(ac.entryMap["false"].typeCorrect == TypeCorrectKind::Correct);
|
||||
|
||||
ac = fix.autocomplete('2');
|
||||
ac = autocomplete('2');
|
||||
CHECK(ac.entryMap.count("ni"));
|
||||
CHECK(ac.entryMap["ni"].typeCorrect == TypeCorrectKind::None);
|
||||
CHECK(ac.entryMap["nil"].typeCorrect == TypeCorrectKind::Correct);
|
||||
|
||||
ac = fix.autocomplete('3');
|
||||
ac = autocomplete('3');
|
||||
CHECK(ac.entryMap.count("false"));
|
||||
CHECK(ac.entryMap["false"].typeCorrect == TypeCorrectKind::None);
|
||||
CHECK(ac.entryMap["function"].typeCorrect == TypeCorrectKind::Correct);
|
||||
|
||||
ac = fix.autocomplete('4');
|
||||
ac = autocomplete('4');
|
||||
CHECK(ac.entryMap["function"].typeCorrect == TypeCorrectKind::Correct);
|
||||
|
||||
ac = fix.autocomplete('5');
|
||||
ac = autocomplete('5');
|
||||
CHECK(ac.entryMap["function"].typeCorrect == TypeCorrectKind::Correct);
|
||||
}
|
||||
|
||||
|
@ -2512,23 +2497,21 @@ local t = {
|
|||
CHECK(ac.entryMap.count("second"));
|
||||
}
|
||||
|
||||
TEST_CASE("autocomplete_documentation_symbols")
|
||||
TEST_CASE_FIXTURE(Fixture, "autocomplete_documentation_symbols")
|
||||
{
|
||||
Fixture fix(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
fix.loadDefinition(R"(
|
||||
loadDefinition(R"(
|
||||
declare y: {
|
||||
x: number,
|
||||
}
|
||||
)");
|
||||
|
||||
fix.fileResolver.source["Module/A"] = R"(
|
||||
fileResolver.source["Module/A"] = R"(
|
||||
local a = y.
|
||||
)";
|
||||
|
||||
fix.frontend.check("Module/A");
|
||||
frontend.check("Module/A");
|
||||
|
||||
auto ac = autocomplete(fix.frontend, "Module/A", Position{1, 21}, nullCallback);
|
||||
auto ac = autocomplete(frontend, "Module/A", Position{1, 21}, nullCallback);
|
||||
|
||||
REQUIRE(ac.entryMap.count("x"));
|
||||
CHECK_EQ(ac.entryMap["x"].documentationSymbol, "@test/global/y.x");
|
||||
|
@ -2646,8 +2629,6 @@ local a: A<(number, s@1>
|
|||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_first_function_arg_expected_type")
|
||||
{
|
||||
ScopedFastFlag luauAutocompleteAvoidMutation("LuauAutocompleteAvoidMutation", true);
|
||||
|
||||
check(R"(
|
||||
local function foo1() return 1 end
|
||||
local function foo2() return "1" end
|
||||
|
@ -2720,7 +2701,6 @@ type A<T... = ...@1> = () -> T
|
|||
|
||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_oop_implicit_self")
|
||||
{
|
||||
ScopedFastFlag flag("LuauMissingFollowACMetatables", true);
|
||||
check(R"(
|
||||
--!strict
|
||||
local Class = {}
|
||||
|
@ -2764,8 +2744,6 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_on_string_singletons")
|
|||
|
||||
TEST_CASE_FIXTURE(ACFixture, "function_in_assignment_has_parentheses_2")
|
||||
{
|
||||
ScopedFastFlag luauAutocompleteAvoidMutation("LuauAutocompleteAvoidMutation", true);
|
||||
|
||||
check(R"(
|
||||
local bar: ((number) -> number) & (number, number) -> number)
|
||||
local abc = b@1
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauFixIncorrectLineNumberDuplicateType)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeAliases");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "cyclic_function_type_in_type_alias")
|
||||
|
@ -241,6 +243,27 @@ TEST_CASE_FIXTURE(Fixture, "export_type_and_type_alias_are_duplicates")
|
|||
CHECK_EQ(dtd->name, "Foo");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "reported_location_is_correct_when_type_alias_are_duplicates")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
type A = string
|
||||
type B = number
|
||||
type C = string
|
||||
type B = number
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
auto dtd = get<DuplicateTypeDefinition>(result.errors[0]);
|
||||
REQUIRE(dtd);
|
||||
CHECK_EQ(dtd->name, "B");
|
||||
|
||||
if (FFlag::LuauFixIncorrectLineNumberDuplicateType)
|
||||
CHECK_EQ(dtd->previousLocation.begin.line + 1, 3);
|
||||
else
|
||||
CHECK_EQ(dtd->previousLocation.begin.line + 1, 1);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "stringify_optional_parameterized_alias")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
|
||||
TEST_SUITE_BEGIN("BuiltinTests");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "math_things_are_defined")
|
||||
|
@ -443,28 +441,19 @@ TEST_CASE_FIXTURE(Fixture, "os_time_takes_optional_date_table")
|
|||
CHECK_EQ(*typeChecker.numberType, *requireType("n3"));
|
||||
}
|
||||
|
||||
// Switch back to TEST_CASE_FIXTURE with regular Fixture when removing the
|
||||
// LuauUseCommittingTxnLog flag.
|
||||
TEST_CASE("thread_is_a_type")
|
||||
TEST_CASE_FIXTURE(Fixture, "thread_is_a_type")
|
||||
{
|
||||
Fixture fix(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
CheckResult result = fix.check(R"(
|
||||
CheckResult result = check(R"(
|
||||
local co = coroutine.create(function() end)
|
||||
)");
|
||||
|
||||
// Replace with LUAU_REQUIRE_NO_ERRORS(result) when using TEST_CASE_FIXTURE.
|
||||
CHECK(result.errors.size() == 0);
|
||||
CHECK_EQ(*fix.typeChecker.threadType, *fix.requireType("co"));
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ(*typeChecker.threadType, *requireType("co"));
|
||||
}
|
||||
|
||||
// Switch back to TEST_CASE_FIXTURE with regular Fixture when removing the
|
||||
// LuauUseCommittingTxnLog flag.
|
||||
TEST_CASE("coroutine_resume_anything_goes")
|
||||
TEST_CASE_FIXTURE(Fixture, "coroutine_resume_anything_goes")
|
||||
{
|
||||
Fixture fix(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
CheckResult result = fix.check(R"(
|
||||
CheckResult result = check(R"(
|
||||
local function nifty(x, y)
|
||||
print(x, y)
|
||||
local z = coroutine.yield(1, 2)
|
||||
|
@ -477,17 +466,12 @@ TEST_CASE("coroutine_resume_anything_goes")
|
|||
local answer = coroutine.resume(co, 3)
|
||||
)");
|
||||
|
||||
// Replace with LUAU_REQUIRE_NO_ERRORS(result) when using TEST_CASE_FIXTURE.
|
||||
CHECK(result.errors.size() == 0);
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
// Switch back to TEST_CASE_FIXTURE with regular Fixture when removing the
|
||||
// LuauUseCommittingTxnLog flag.
|
||||
TEST_CASE("coroutine_wrap_anything_goes")
|
||||
TEST_CASE_FIXTURE(Fixture, "coroutine_wrap_anything_goes")
|
||||
{
|
||||
Fixture fix(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
CheckResult result = fix.check(R"(
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
local function nifty(x, y)
|
||||
print(x, y)
|
||||
|
@ -501,8 +485,7 @@ TEST_CASE("coroutine_wrap_anything_goes")
|
|||
local answer = f(3)
|
||||
)");
|
||||
|
||||
// Replace with LUAU_REQUIRE_NO_ERRORS(result) when using TEST_CASE_FIXTURE.
|
||||
CHECK(result.errors.size() == 0);
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "setmetatable_should_not_mutate_persisted_types")
|
||||
|
@ -961,4 +944,18 @@ TEST_CASE_FIXTURE(Fixture, "table_freeze_is_generic")
|
|||
CHECK_EQ("*unknown*", toString(requireType("d")));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "set_metatable_needs_arguments")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauSetMetaTableArgsCheck", true};
|
||||
CheckResult result = check(R"(
|
||||
local a = {b=setmetatable}
|
||||
a.b()
|
||||
a:b()
|
||||
a:b({})
|
||||
)");
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(result.errors[0], (TypeError{Location{{2, 0}, {2, 5}}, CountMismatch{2, 0}}));
|
||||
CHECK_EQ(result.errors[1], (TypeError{Location{{3, 0}, {3, 5}}, CountMismatch{2, 1}}));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauFixArgumentCountMismatchAmountWithGenericTypes)
|
||||
|
||||
TEST_SUITE_BEGIN("GenericsTests");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "check_generic_function")
|
||||
|
@ -786,4 +788,47 @@ local TheDispatcher: Dispatcher = {
|
|||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_few")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
function test(a: number)
|
||||
return 1
|
||||
end
|
||||
|
||||
function wrapper<A...>(f: (A...) -> number, ...: A...)
|
||||
end
|
||||
|
||||
wrapper(test)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes)
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 2 arguments, but only 1 is specified)");
|
||||
else
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 1 argument, but 1 is specified)");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_argument_count_too_many")
|
||||
{
|
||||
CheckResult result = check(R"(
|
||||
function test2(a: number, b: string)
|
||||
return 1
|
||||
end
|
||||
|
||||
function wrapper<A...>(f: (A...) -> number, ...: A...)
|
||||
end
|
||||
|
||||
wrapper(test2, 1, "", 3)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauFixArgumentCountMismatchAmountWithGenericTypes)
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 3 arguments, but 4 are specified)");
|
||||
else
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Argument count mismatch. Function expects 1 argument, but 4 are specified)");
|
||||
}
|
||||
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -2334,4 +2334,54 @@ TEST_CASE_FIXTURE(Fixture, "pass_a_union_of_tables_to_a_function_that_requires_a
|
|||
REQUIRE_EQ("{| [any]: any, x: number, y: number |} | {| y: number |}", toString(requireType("b")));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "unifying_tables_shouldnt_uaf1")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTxnLogCheckForInvalidation", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
-- This example produced a UAF at one point, caused by pointers to table types becoming
|
||||
-- invalidated by child unifiers. (Calling log.concat can cause pointers to become invalid.)
|
||||
type _Entry = {
|
||||
a: number,
|
||||
|
||||
middle: (self: _Entry) -> (),
|
||||
|
||||
z: number
|
||||
}
|
||||
|
||||
export type AnyEntry = _Entry
|
||||
|
||||
local Entry = {}
|
||||
Entry.__index = Entry
|
||||
|
||||
function Entry:dispose()
|
||||
self:middle()
|
||||
forgetChildren(self) -- unify free with sealed AnyEntry
|
||||
end
|
||||
|
||||
function forgetChildren(parent: AnyEntry)
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "unifying_tables_shouldnt_uaf2")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTxnLogCheckForInvalidation", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
-- Another example that UAFd, this time found by fuzzing.
|
||||
local _
|
||||
do
|
||||
_._ *= (_[{n0=_[{[{[_]=_,}]=_,}],}])[_]
|
||||
_ = (_.n0)
|
||||
end
|
||||
_._ *= (_[false])[_]
|
||||
_ = (_.cos)
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -5144,7 +5144,6 @@ end
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
|
||||
{
|
||||
ScopedFastFlag committingTxnLog{"LuauUseCommittingTxnLog", true};
|
||||
ScopedFastFlag subtypingVariance{"LuauTableSubtypingVariance2", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
|
@ -5355,4 +5354,41 @@ end
|
|||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_decl_quantify_right_type")
|
||||
{
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify", true};
|
||||
|
||||
fileResolver.source["game/isAMagicMock"] = R"(
|
||||
--!nonstrict
|
||||
return function(value)
|
||||
return false
|
||||
end
|
||||
)";
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
local MagicMock = {}
|
||||
MagicMock.is = require(game.isAMagicMock)
|
||||
|
||||
function MagicMock.is(value)
|
||||
return false
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_decl_non_self_sealed_overwrite")
|
||||
{
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function string.len(): number
|
||||
return 1
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
|
||||
struct TryUnifyFixture : Fixture
|
||||
{
|
||||
TypeArena arena;
|
||||
|
@ -43,8 +41,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "compatible_functions_are_unified")
|
|||
state.tryUnify(&functionTwo, &functionOne);
|
||||
CHECK(state.errors.empty());
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
CHECK_EQ(functionOne, functionTwo);
|
||||
}
|
||||
|
@ -86,8 +83,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "tables_can_be_unified")
|
|||
|
||||
CHECK(state.errors.empty());
|
||||
|
||||
if (FFlag::LuauUseCommittingTxnLog)
|
||||
state.log.commit();
|
||||
state.log.commit();
|
||||
|
||||
CHECK_EQ(*getMutable<TableTypeVar>(&tableOne)->props["foo"].type, *getMutable<TableTypeVar>(&tableTwo)->props["foo"].type);
|
||||
}
|
||||
|
@ -110,9 +106,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_tables_are_preserved")
|
|||
|
||||
CHECK_EQ(1, state.errors.size());
|
||||
|
||||
if (!FFlag::LuauUseCommittingTxnLog)
|
||||
state.DEPRECATED_log.rollback();
|
||||
|
||||
CHECK_NE(*getMutable<TableTypeVar>(&tableOne)->props["foo"].type, *getMutable<TableTypeVar>(&tableTwo)->props["foo"].type);
|
||||
}
|
||||
|
||||
|
@ -217,34 +210,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "cli_41095_concat_log_in_sealed_table_unifica
|
|||
CHECK_EQ(toString(result.errors[1]), "Available overloads: ({a}, a) -> (); and ({a}, number, a) -> ()");
|
||||
}
|
||||
|
||||
TEST_CASE("undo_new_prop_on_unsealed_table")
|
||||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{"LuauTableSubtypingVariance2", true},
|
||||
// This test makes no sense with a committing TxnLog.
|
||||
{"LuauUseCommittingTxnLog", false},
|
||||
};
|
||||
// I am not sure how to make this happen in Luau code.
|
||||
|
||||
TryUnifyFixture fix;
|
||||
|
||||
TypeId unsealedTable = fix.arena.addType(TableTypeVar{TableState::Unsealed, TypeLevel{}});
|
||||
TypeId sealedTable =
|
||||
fix.arena.addType(TableTypeVar{{{"prop", Property{getSingletonTypes().numberType}}}, std::nullopt, TypeLevel{}, TableState::Sealed});
|
||||
|
||||
const TableTypeVar* ttv = get<TableTypeVar>(unsealedTable);
|
||||
REQUIRE(ttv);
|
||||
|
||||
fix.state.tryUnify(sealedTable, unsealedTable);
|
||||
|
||||
// To be honest, it's really quite spooky here that we're amending an unsealed table in this case.
|
||||
CHECK(!ttv->props.empty());
|
||||
|
||||
fix.state.DEPRECATED_log.rollback();
|
||||
|
||||
CHECK(ttv->props.empty());
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(TryUnifyFixture, "free_tail_is_grown_properly")
|
||||
{
|
||||
TypePackId threeNumbers = arena.addTypePack(TypePack{{typeChecker.numberType, typeChecker.numberType, typeChecker.numberType}, std::nullopt});
|
||||
|
@ -267,11 +232,6 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "recursive_metatable_getmatchtag")
|
|||
|
||||
TEST_CASE_FIXTURE(TryUnifyFixture, "cli_50320_follow_in_any_unification")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{"LuauUseCommittingTxnLog", true},
|
||||
{"LuauFollowWithCommittingTxnLogInAnyUnification", true},
|
||||
};
|
||||
|
||||
TypePackVar free{FreeTypePack{TypeLevel{}}};
|
||||
TypePackVar target{TypePack{}};
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
|
||||
TEST_SUITE_BEGIN("TypePackTests");
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "infer_multi_return")
|
||||
|
@ -264,13 +262,9 @@ TEST_CASE_FIXTURE(Fixture, "variadic_pack_syntax")
|
|||
CHECK_EQ(toString(requireType("foo")), "(...number) -> ()");
|
||||
}
|
||||
|
||||
// Switch back to TEST_CASE_FIXTURE with regular Fixture when removing the
|
||||
// LuauUseCommittingTxnLog flag.
|
||||
TEST_CASE("type_pack_hidden_free_tail_infinite_growth")
|
||||
TEST_CASE_FIXTURE(Fixture, "type_pack_hidden_free_tail_infinite_growth")
|
||||
{
|
||||
Fixture fix(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
CheckResult result = fix.check(R"(
|
||||
CheckResult result = check(R"(
|
||||
--!nonstrict
|
||||
if _ then
|
||||
_[function(l0)end],l0 = _
|
||||
|
@ -282,8 +276,7 @@ elseif _ then
|
|||
end
|
||||
)");
|
||||
|
||||
// Switch back to LUAU_REQUIRE_ERRORS(result) when using TEST_CASE_FIXTURE.
|
||||
CHECK(result.errors.size() > 0);
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "variadic_argument_tail")
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "doctest.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauEqConstraint)
|
||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -282,19 +281,16 @@ local c = b:foo(1, 2)
|
|||
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE("optional_union_follow")
|
||||
TEST_CASE_FIXTURE(Fixture, "optional_union_follow")
|
||||
{
|
||||
Fixture fix(FFlag::LuauUseCommittingTxnLog);
|
||||
|
||||
CheckResult result = fix.check(R"(
|
||||
CheckResult result = check(R"(
|
||||
local y: number? = 2
|
||||
local x = y
|
||||
local function f(a: number, b: typeof(x), c: typeof(x)) return -a end
|
||||
return f()
|
||||
)");
|
||||
|
||||
REQUIRE_EQ(result.errors.size(), 1);
|
||||
// LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
auto acm = get<CountMismatch>(result.errors[0]);
|
||||
REQUIRE(acm);
|
||||
|
|
107
tools/LuauVisualize.py
Normal file
107
tools/LuauVisualize.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
# This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||
|
||||
# HACK: LLDB's python API doesn't afford anything helpful for getting at variadic template parameters.
|
||||
# We're forced to resort to parsing names as strings.
|
||||
def templateParams(s):
|
||||
depth = 0
|
||||
start = s.find('<') + 1
|
||||
result = []
|
||||
for i, c in enumerate(s[start:], start):
|
||||
if c == '<':
|
||||
depth += 1
|
||||
elif c == '>':
|
||||
if depth == 0:
|
||||
result.append(s[start: i].strip())
|
||||
break
|
||||
depth -= 1
|
||||
elif c == ',' and depth == 0:
|
||||
result.append(s[start: i].strip())
|
||||
start = i + 1
|
||||
return result
|
||||
|
||||
def getType(target, typeName):
|
||||
stars = 0
|
||||
|
||||
typeName = typeName.strip()
|
||||
while typeName.endswith('*'):
|
||||
stars += 1
|
||||
typeName = typeName[:-1]
|
||||
|
||||
if typeName.startswith('const '):
|
||||
typeName = typeName[6:]
|
||||
|
||||
ty = target.FindFirstType(typeName.strip())
|
||||
for _ in range(stars):
|
||||
ty = ty.GetPointerType()
|
||||
|
||||
return ty
|
||||
|
||||
def luau_variant_summary(valobj, internal_dict, options):
|
||||
type_id = valobj.GetChildMemberWithName("typeid").GetValueAsUnsigned()
|
||||
storage = valobj.GetChildMemberWithName("storage")
|
||||
params = templateParams(valobj.GetType().GetCanonicalType().GetName())
|
||||
stored_type = params[type_id]
|
||||
value = storage.Cast(stored_type.GetPointerType()).Dereference()
|
||||
return stored_type.GetDisplayTypeName() + " [" + value.GetValue() + "]"
|
||||
|
||||
class LuauVariantSyntheticChildrenProvider:
|
||||
node_names = ["type", "value"]
|
||||
|
||||
def __init__(self, valobj, internal_dict):
|
||||
self.valobj = valobj
|
||||
self.type_index = None
|
||||
self.current_type = None
|
||||
self.type_params = []
|
||||
self.stored_value = None
|
||||
|
||||
def num_children(self):
|
||||
return len(self.node_names)
|
||||
|
||||
def has_children(self):
|
||||
return True
|
||||
|
||||
def get_child_index(self, name):
|
||||
try:
|
||||
return self.node_names.index(name)
|
||||
except ValueError:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
try:
|
||||
node = self.node_names[index]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
if node == "type":
|
||||
if self.current_type:
|
||||
return self.valobj.CreateValueFromExpression(node, f"(const char*)\"{self.current_type.GetDisplayTypeName()}\"")
|
||||
else:
|
||||
return self.valobj.CreateValueFromExpression(node, "(const char*)\"<unknown type>\"")
|
||||
elif node == "value":
|
||||
if self.stored_value is not None:
|
||||
if self.current_type is not None:
|
||||
return self.valobj.CreateValueFromData(node, self.stored_value.GetData(), self.current_type)
|
||||
else:
|
||||
return self.valobj.CreateValueExpression(node, "(const char*)\"<unknown type>\"")
|
||||
else:
|
||||
return self.valobj.CreateValueFromExpression(node, "(const char*)\"<no stored value>\"")
|
||||
else:
|
||||
return None
|
||||
|
||||
def update(self):
|
||||
self.type_index = self.valobj.GetChildMemberWithName("typeid").GetValueAsSigned()
|
||||
self.type_params = templateParams(self.valobj.GetType().GetCanonicalType().GetName())
|
||||
|
||||
if len(self.type_params) > self.type_index:
|
||||
self.current_type = getType(self.valobj.GetTarget(), self.type_params[self.type_index])
|
||||
|
||||
if self.current_type:
|
||||
storage = self.valobj.GetChildMemberWithName("storage")
|
||||
self.stored_value = storage.Cast(self.current_type.GetPointerType()).Dereference()
|
||||
else:
|
||||
self.stored_value = None
|
||||
else:
|
||||
self.current_type = None
|
||||
self.stored_value = None
|
||||
|
||||
return False
|
2
tools/lldb-formatters.lldb
Normal file
2
tools/lldb-formatters.lldb
Normal file
|
@ -0,0 +1,2 @@
|
|||
type synthetic add -x "^Luau::Variant<.+>$" -l LuauVisualize.LuauVariantSyntheticChildrenProvider
|
||||
type summary add -x "^Luau::Variant<.+>$" -l LuauVisualize.luau_variant_summary
|
Loading…
Reference in a new issue