2021-10-29 21:25:12 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
#include "Luau/TxnLog.h"
|
|
|
|
|
2022-02-03 23:09:37 +00:00
|
|
|
#include "Luau/ToString.h"
|
2021-10-29 21:25:12 +01:00
|
|
|
#include "Luau/TypePack.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
2022-01-06 22:10:07 +00:00
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
LUAU_FASTFLAGVARIABLE(LuauUseCommittingTxnLog, false)
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
void DEPRECATED_TxnLog::operator()(TypeId a)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-01-06 22:10:07 +00:00
|
|
|
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
2021-10-29 21:25:12 +01:00
|
|
|
typeVarChanges.emplace_back(a, *a);
|
|
|
|
}
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
void DEPRECATED_TxnLog::operator()(TypePackId a)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-01-06 22:10:07 +00:00
|
|
|
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
2021-10-29 21:25:12 +01:00
|
|
|
typePackChanges.emplace_back(a, *a);
|
|
|
|
}
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
void DEPRECATED_TxnLog::operator()(TableTypeVar* a)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-01-06 22:10:07 +00:00
|
|
|
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
2021-10-29 21:25:12 +01:00
|
|
|
tableChanges.emplace_back(a, a->boundTo);
|
|
|
|
}
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
void DEPRECATED_TxnLog::rollback()
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-01-06 22:10:07 +00:00
|
|
|
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
2021-10-29 21:25:12 +01:00
|
|
|
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);
|
2021-11-05 02:42:00 +00:00
|
|
|
|
2021-12-02 23:20:08 +00:00
|
|
|
LUAU_ASSERT(originalSeenSize <= sharedSeen->size());
|
|
|
|
sharedSeen->resize(originalSeenSize);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
void DEPRECATED_TxnLog::concat(DEPRECATED_TxnLog rhs)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-01-06 22:10:07 +00:00
|
|
|
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
2021-10-29 21:25:12 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
bool DEPRECATED_TxnLog::haveSeen(TypeId lhs, TypeId rhs)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-01-06 22:10:07 +00:00
|
|
|
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
2021-10-29 21:25:12 +01:00
|
|
|
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
2021-12-02 23:20:08 +00:00
|
|
|
return (sharedSeen->end() != std::find(sharedSeen->begin(), sharedSeen->end(), sortedPair));
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
void DEPRECATED_TxnLog::pushSeen(TypeId lhs, TypeId rhs)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
|
|
|
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
|
|
|
sharedSeen->push_back(sortedPair);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DEPRECATED_TxnLog::popSeen(TypeId lhs, TypeId rhs)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(!FFlag::LuauUseCommittingTxnLog);
|
|
|
|
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
|
|
|
LUAU_ASSERT(sortedPair == sharedSeen->back());
|
|
|
|
sharedSeen->pop_back();
|
|
|
|
}
|
|
|
|
|
2022-02-03 23:09:37 +00:00
|
|
|
const std::string nullPendingResult = "<nullptr>";
|
|
|
|
|
|
|
|
std::string toString(PendingType* pending)
|
|
|
|
{
|
|
|
|
if (pending == nullptr)
|
|
|
|
return nullPendingResult;
|
|
|
|
|
|
|
|
return toString(pending->pending);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string dump(PendingType* pending)
|
|
|
|
{
|
|
|
|
if (pending == nullptr)
|
|
|
|
{
|
|
|
|
printf("%s\n", nullPendingResult.c_str());
|
|
|
|
return nullPendingResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
ToStringOptions opts;
|
|
|
|
opts.exhaustive = true;
|
|
|
|
opts.functionTypeArguments = true;
|
|
|
|
std::string result = toString(pending->pending, opts);
|
|
|
|
printf("%s\n", result.c_str());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string toString(PendingTypePack* pending)
|
|
|
|
{
|
|
|
|
if (pending == nullptr)
|
|
|
|
return nullPendingResult;
|
|
|
|
|
|
|
|
return toString(pending->pending);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string dump(PendingTypePack* pending)
|
|
|
|
{
|
|
|
|
if (pending == nullptr)
|
|
|
|
{
|
|
|
|
printf("%s\n", nullPendingResult.c_str());
|
|
|
|
return nullPendingResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
ToStringOptions opts;
|
|
|
|
opts.exhaustive = true;
|
|
|
|
opts.functionTypeArguments = true;
|
|
|
|
std::string result = toString(pending->pending, opts);
|
|
|
|
printf("%s\n", result.c_str());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
static const TxnLog emptyLog;
|
|
|
|
|
|
|
|
const TxnLog* TxnLog::empty()
|
|
|
|
{
|
|
|
|
return &emptyLog;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TxnLog::concat(TxnLog rhs)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
|
|
|
|
|
|
|
for (auto& [ty, rep] : rhs.typeVarChanges)
|
|
|
|
typeVarChanges[ty] = std::move(rep);
|
|
|
|
|
|
|
|
for (auto& [tp, rep] : rhs.typePackChanges)
|
|
|
|
typePackChanges[tp] = std::move(rep);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TxnLog::commit()
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
|
|
|
|
|
|
|
for (auto& [ty, rep] : typeVarChanges)
|
|
|
|
*asMutable(ty) = rep.get()->pending;
|
|
|
|
|
|
|
|
for (auto& [tp, rep] : typePackChanges)
|
|
|
|
*asMutable(tp) = rep.get()->pending;
|
|
|
|
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
inversed.typeVarChanges[ty] = std::make_unique<PendingType>(*ty);
|
|
|
|
|
|
|
|
for (auto& [tp, _rep] : typePackChanges)
|
|
|
|
inversed.typePackChanges[tp] = std::make_unique<PendingTypePack>(*tp);
|
|
|
|
|
|
|
|
return inversed;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TxnLog::haveSeen(TypeId lhs, TypeId rhs) const
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
|
|
|
|
|
|
|
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
|
|
|
if (sharedSeen->end() != std::find(sharedSeen->begin(), sharedSeen->end(), sortedPair))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent)
|
|
|
|
{
|
|
|
|
return parent->haveSeen(lhs, rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
void TxnLog::pushSeen(TypeId lhs, TypeId rhs)
|
|
|
|
{
|
2022-01-06 22:10:07 +00:00
|
|
|
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
2021-12-02 23:20:08 +00:00
|
|
|
sharedSeen->push_back(sortedPair);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void TxnLog::popSeen(TypeId lhs, TypeId rhs)
|
|
|
|
{
|
2022-01-06 22:10:07 +00:00
|
|
|
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
const std::pair<TypeId, TypeId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
|
2021-12-02 23:20:08 +00:00
|
|
|
LUAU_ASSERT(sortedPair == sharedSeen->back());
|
|
|
|
sharedSeen->pop_back();
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
2022-01-06 22:10:07 +00:00
|
|
|
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
|
|
|
|
// about this type, we don't want to mutate the parent's state.
|
|
|
|
auto& pending = typeVarChanges[ty];
|
|
|
|
if (!pending)
|
|
|
|
pending = std::make_unique<PendingType>(*ty);
|
|
|
|
|
|
|
|
return pending.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
// about this type, we don't want to mutate the parent's state.
|
|
|
|
auto& pending = typePackChanges[tp];
|
|
|
|
if (!pending)
|
|
|
|
pending = std::make_unique<PendingTypePack>(*tp);
|
|
|
|
|
|
|
|
return pending.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingType* TxnLog::pending(TypeId ty) const
|
|
|
|
{
|
|
|
|
for (const TxnLog* current = this; current; current = current->parent)
|
|
|
|
{
|
|
|
|
if (auto it = current->typeVarChanges.find(ty); it != current->typeVarChanges.end())
|
|
|
|
return it->second.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingTypePack* TxnLog::pending(TypePackId tp) const
|
|
|
|
{
|
|
|
|
for (const TxnLog* current = this; current; current = current->parent)
|
|
|
|
{
|
|
|
|
if (auto it = current->typePackChanges.find(tp); it != current->typePackChanges.end())
|
|
|
|
return it->second.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingType* TxnLog::replace(TypeId ty, TypeVar replacement)
|
|
|
|
{
|
|
|
|
PendingType* newTy = queue(ty);
|
|
|
|
newTy->pending = replacement;
|
|
|
|
return newTy;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingTypePack* TxnLog::replace(TypePackId tp, TypePackVar replacement)
|
|
|
|
{
|
|
|
|
PendingTypePack* newTp = queue(tp);
|
|
|
|
newTp->pending = replacement;
|
|
|
|
return newTp;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingType* TxnLog::bindTable(TypeId ty, std::optional<TypeId> newBoundTo)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(get<TableTypeVar>(ty));
|
|
|
|
|
|
|
|
PendingType* newTy = queue(ty);
|
|
|
|
if (TableTypeVar* ttv = Luau::getMutable<TableTypeVar>(newTy))
|
|
|
|
ttv->boundTo = newBoundTo;
|
|
|
|
|
|
|
|
return newTy;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingType* TxnLog::changeLevel(TypeId ty, TypeLevel newLevel)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(get<FreeTypeVar>(ty) || get<TableTypeVar>(ty) || get<FunctionTypeVar>(ty));
|
|
|
|
|
|
|
|
PendingType* newTy = queue(ty);
|
|
|
|
if (FreeTypeVar* ftv = Luau::getMutable<FreeTypeVar>(newTy))
|
|
|
|
{
|
|
|
|
ftv->level = newLevel;
|
|
|
|
}
|
|
|
|
else if (TableTypeVar* ttv = Luau::getMutable<TableTypeVar>(newTy))
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(ttv->state == TableState::Free || ttv->state == TableState::Generic);
|
|
|
|
ttv->level = newLevel;
|
|
|
|
}
|
|
|
|
else if (FunctionTypeVar* ftv = Luau::getMutable<FunctionTypeVar>(newTy))
|
|
|
|
{
|
|
|
|
ftv->level = newLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newTy;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(get<FreeTypePack>(tp));
|
|
|
|
|
|
|
|
PendingTypePack* newTp = queue(tp);
|
|
|
|
if (FreeTypePack* ftp = Luau::getMutable<FreeTypePack>(newTp))
|
|
|
|
{
|
|
|
|
ftp->level = newLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newTp;
|
|
|
|
}
|
|
|
|
|
|
|
|
PendingType* TxnLog::changeIndexer(TypeId ty, std::optional<TableIndexer> indexer)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(get<TableTypeVar>(ty));
|
|
|
|
|
|
|
|
PendingType* newTy = queue(ty);
|
|
|
|
if (TableTypeVar* ttv = Luau::getMutable<TableTypeVar>(newTy))
|
|
|
|
{
|
|
|
|
ttv->indexer = indexer;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newTy;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<TypeLevel> TxnLog::getLevel(TypeId ty) const
|
|
|
|
{
|
|
|
|
if (FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty))
|
|
|
|
return ftv->level;
|
|
|
|
else if (TableTypeVar* ttv = getMutable<TableTypeVar>(ty); ttv && (ttv->state == TableState::Free || ttv->state == TableState::Generic))
|
|
|
|
return ttv->level;
|
|
|
|
else if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty))
|
|
|
|
return ftv->level;
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2022-02-03 23:09:37 +00:00
|
|
|
TypeId TxnLog::follow(TypeId ty) const
|
2022-01-06 22:10:07 +00:00
|
|
|
{
|
|
|
|
return Luau::follow(ty, [this](TypeId ty) {
|
|
|
|
PendingType* state = this->pending(ty);
|
|
|
|
|
|
|
|
if (state == nullptr)
|
|
|
|
return ty;
|
|
|
|
|
|
|
|
// Ugly: Fabricate a TypeId that doesn't adhere to most of the invariants
|
|
|
|
// that normally apply. This is safe because follow will only call get<>
|
|
|
|
// on the returned pointer.
|
|
|
|
return const_cast<const TypeVar*>(&state->pending);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
TypePackId TxnLog::follow(TypePackId tp) const
|
|
|
|
{
|
|
|
|
return Luau::follow(tp, [this](TypePackId tp) {
|
|
|
|
PendingTypePack* state = this->pending(tp);
|
|
|
|
|
|
|
|
if (state == nullptr)
|
|
|
|
return tp;
|
|
|
|
|
|
|
|
// Ugly: Fabricate a TypePackId that doesn't adhere to most of the
|
|
|
|
// invariants that normally apply. This is safe because follow will
|
|
|
|
// only call get<> on the returned pointer.
|
|
|
|
return const_cast<const TypePackVar*>(&state->pending);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
} // namespace Luau
|