Sync to upstream/release/513 (#340)

This commit is contained in:
Arseny Kapoulkine 2022-02-04 08:45:57 -08:00 committed by GitHub
parent c572f6944f
commit d58e70b8c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 12818 additions and 3212 deletions

View File

@ -285,12 +285,12 @@ struct TypesAreUnrelated
bool operator==(const TypesAreUnrelated& rhs) const;
};
using TypeErrorData = Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods,
DuplicateTypeDefinition, CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire,
IncorrectGenericParameterCount, SyntaxError, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError,
CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning,
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty,
TypesAreUnrelated>;
using TypeErrorData =
Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods, DuplicateTypeDefinition,
CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire, IncorrectGenericParameterCount, SyntaxError,
CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed,
ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning, DuplicateGenericParameter, CannotInferBinaryOperation,
MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty, TypesAreUnrelated>;
struct TypeError
{

View File

@ -4,7 +4,6 @@
#include "Luau/Variant.h"
#include "Luau/Symbol.h"
#include <map> // TODO: Kill with LuauLValueAsKey.
#include <memory>
#include <unordered_map>
@ -38,24 +37,13 @@ std::optional<LValue> tryGetLValue(const class AstExpr& expr);
// Utility function: breaks down an LValue to get at the Symbol, and reverses the vector of keys.
std::pair<Symbol, std::vector<std::string>> getFullName(const LValue& lvalue);
// Kill with LuauLValueAsKey.
std::string toString(const LValue& lvalue);
template<typename T>
const T* get(const LValue& lvalue)
{
return get_if<T>(&lvalue);
}
using NEW_RefinementMap = std::unordered_map<LValue, TypeId, LValueHasher>;
using DEPRECATED_RefinementMap = std::map<std::string, TypeId>;
// Transient. Kill with LuauLValueAsKey.
struct RefinementMap
{
NEW_RefinementMap NEW_refinements;
DEPRECATED_RefinementMap DEPRECATED_refinements;
};
using RefinementMap = std::unordered_map<LValue, TypeId, LValueHasher>;
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f);
void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty);

View File

@ -55,6 +55,8 @@
namespace Luau
{
struct TxnLog;
enum class TarjanResult
{
TooManyChildren,
@ -89,6 +91,10 @@ struct Tarjan
int childCount = 0;
// This should never be null; ensure you initialize it before calling
// substitution methods.
const TxnLog* log;
std::vector<TypeId> edgesTy;
std::vector<TypePackId> edgesTp;
std::vector<TarjanWorklistVertex> worklist;

View File

@ -72,6 +72,9 @@ struct PendingType
}
};
std::string toString(PendingType* pending);
std::string dump(PendingType* pending);
// Pending state for a TypePackVar. Generated by a TxnLog and committed via
// TxnLog::commit.
struct PendingTypePack
@ -85,6 +88,9 @@ struct PendingTypePack
}
};
std::string toString(PendingTypePack* pending);
std::string dump(PendingTypePack* pending);
template<typename T>
T* getMutable(PendingType* pending)
{
@ -237,7 +243,7 @@ struct TxnLog
// Follows a type, accounting for pending type states. The returned type may have
// pending state; you should use `pending` or `get` to find out.
TypeId follow(TypeId ty);
TypeId follow(TypeId ty) const;
// Follows a type pack, accounting for pending type states. The returned type pack
// may have pending state; you should use `pending` or `get` to find out.

View File

@ -262,7 +262,7 @@ public:
* {method: ({method: (<CYCLE>) -> a}) -> a}
*
*/
TypeId instantiate(const ScopePtr& scope, TypeId ty, Location location);
TypeId instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log = TxnLog::empty());
// Replace any free types or type packs by `any`.
// This is used when exporting types from modules, to make sure free types don't leak.
@ -308,9 +308,15 @@ private:
TypeId singletonType(bool value);
TypeId singletonType(std::string value);
TypeIdPredicate mkTruthyPredicate(bool sense);
// Returns nullopt if the predicate filters down the TypeId to 0 options.
std::optional<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
public:
std::optional<TypeId> pickTypesFromSense(TypeId type, bool sense);
private:
TypeId unionOfTypes(TypeId a, TypeId b, const Location& location, bool unifyFreeTypes = true);
// ex
@ -349,7 +355,6 @@ private:
void refineLValue(const LValue& lvalue, RefinementMap& refis, const ScopePtr& scope, TypeIdPredicate predicate);
std::optional<TypeId> resolveLValue(const ScopePtr& scope, const LValue& lvalue);
std::optional<TypeId> DEPRECATED_resolveLValue(const ScopePtr& scope, const LValue& lvalue);
std::optional<TypeId> resolveLValue(const RefinementMap& refis, const ScopePtr& scope, const LValue& lvalue);
void resolve(const PredicateVec& predicates, ErrorVec& errVec, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr = false);

View File

@ -6,8 +6,6 @@
#include <vector>
#include <memory>
LUAU_FASTFLAG(LuauTypedAllocatorZeroStart)
namespace Luau
{
@ -22,10 +20,7 @@ class TypedAllocator
public:
TypedAllocator()
{
if (FFlag::LuauTypedAllocatorZeroStart)
currentBlockSize = kBlockSize;
else
appendBlock();
currentBlockSize = kBlockSize;
}
~TypedAllocator()
@ -64,18 +59,12 @@ public:
bool empty() const
{
if (FFlag::LuauTypedAllocatorZeroStart)
return stuff.empty();
else
return stuff.size() == 1 && currentBlockSize == 0;
return stuff.empty();
}
size_t size() const
{
if (FFlag::LuauTypedAllocatorZeroStart)
return stuff.empty() ? 0 : kBlockSize * (stuff.size() - 1) + currentBlockSize;
else
return kBlockSize * (stuff.size() - 1) + currentBlockSize;
return stuff.empty() ? 0 : kBlockSize * (stuff.size() - 1) + currentBlockSize;
}
void clear()
@ -84,10 +73,7 @@ public:
unfreeze();
free();
if (FFlag::LuauTypedAllocatorZeroStart)
currentBlockSize = kBlockSize;
else
appendBlock();
currentBlockSize = kBlockSize;
}
void freeze()

View File

@ -51,6 +51,10 @@ struct Unifier
private:
void tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall = false, bool isIntersection = false);
void tryUnifyUnionWithType(TypeId subTy, const UnionTypeVar* uv, TypeId superTy);
void tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTypeVar* uv, bool cacheEnabled, bool isFunctionCall);
void tryUnifyTypeWithIntersection(TypeId subTy, TypeId superTy, const IntersectionTypeVar* uv);
void tryUnifyIntersectionWithType(TypeId subTy, const IntersectionTypeVar* uv, TypeId superTy, bool cacheEnabled, bool isFunctionCall);
void tryUnifyPrimitives(TypeId subTy, TypeId superTy);
void tryUnifySingletons(TypeId subTy, TypeId superTy);
void tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCall = false);

View File

@ -8,6 +8,8 @@
#include <algorithm>
LUAU_FASTFLAG(LuauAssertStripsFalsyTypes)
/** FIXME: Many of these type definitions are not quite completely accurate.
*
* Some of them require richer generics than we have. For instance, we do not yet have a way to talk
@ -391,12 +393,41 @@ static std::optional<ExprResult<TypePackId>> magicFunctionAssert(
{
auto [paramPack, predicates] = exprResult;
if (expr.args.size < 1)
if (FFlag::LuauAssertStripsFalsyTypes)
{
TypeArena& arena = typechecker.currentModule->internalTypes;
auto [head, tail] = flatten(paramPack);
if (head.empty() && tail)
{
std::optional<TypeId> fst = first(*tail);
if (!fst)
return ExprResult<TypePackId>{paramPack};
head.push_back(*fst);
}
typechecker.reportErrors(typechecker.resolve(predicates, scope, true));
if (head.size() > 0)
{
std::optional<TypeId> newhead = typechecker.pickTypesFromSense(head[0], true);
if (!newhead)
head = {typechecker.nilType};
else
head[0] = *newhead;
}
return ExprResult<TypePackId>{arena.addTypePack(TypePack{std::move(head), tail})};
}
else
{
if (expr.args.size < 1)
return ExprResult<TypePackId>{paramPack};
typechecker.reportErrors(typechecker.resolve(predicates, scope, true));
return ExprResult<TypePackId>{paramPack};
typechecker.reportErrors(typechecker.resolve(predicates, scope, true));
return ExprResult<TypePackId>{paramPack};
}
}
static std::optional<ExprResult<TypePackId>> magicFunctionPack(

View File

@ -5,8 +5,6 @@
#include <vector>
LUAU_FASTFLAG(LuauLValueAsKey)
namespace Luau
{
@ -94,17 +92,7 @@ std::pair<Symbol, std::vector<std::string>> getFullName(const LValue& lvalue)
return {*symbol, std::vector<std::string>(keys.rbegin(), keys.rend())};
}
// Kill with LuauLValueAsKey.
std::string toString(const LValue& lvalue)
{
auto [symbol, keys] = getFullName(lvalue);
std::string s = toString(symbol);
for (std::string key : keys)
s += "." + key;
return s;
}
static void merge(NEW_RefinementMap& l, const NEW_RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
{
for (const auto& [k, a] : r)
{
@ -115,45 +103,9 @@ static void merge(NEW_RefinementMap& l, const NEW_RefinementMap& r, std::functio
}
}
static void merge(DEPRECATED_RefinementMap& l, const DEPRECATED_RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
{
auto itL = l.begin();
auto itR = r.begin();
while (itL != l.end() && itR != r.end())
{
const auto& [k, a] = *itR;
if (itL->first == k)
{
l[k] = f(itL->second, a);
++itL;
++itR;
}
else if (itL->first < k)
++itL;
else
{
l[k] = a;
++itR;
}
}
l.insert(itR, r.end());
}
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f)
{
if (FFlag::LuauLValueAsKey)
return merge(l.NEW_refinements, r.NEW_refinements, f);
else
return merge(l.DEPRECATED_refinements, r.DEPRECATED_refinements, f);
}
void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty)
{
if (FFlag::LuauLValueAsKey)
refis.NEW_refinements[lvalue] = ty;
else
refis.DEPRECATED_refinements[toString(lvalue)] = ty;
refis[lvalue] = ty;
}
} // namespace Luau

View File

@ -12,8 +12,6 @@
#include <math.h>
#include <limits.h>
LUAU_FASTFLAGVARIABLE(LuauLintTableCreateTable, false)
namespace Luau
{
@ -2155,7 +2153,7 @@ private:
"table.move uses index 0 but arrays are 1-based; did you mean 1 instead?");
}
if (FFlag::LuauLintTableCreateTable && func->index == "create" && node->args.size == 2)
if (func->index == "create" && node->args.size == 2)
{
// table.create(n, {...})
if (args[1]->is<AstExprTable>())

View File

@ -2,6 +2,7 @@
#include "Luau/Substitution.h"
#include "Luau/Common.h"
#include "Luau/TxnLog.h"
#include <algorithm>
#include <stdexcept>
@ -13,17 +14,17 @@ namespace Luau
void Tarjan::visitChildren(TypeId ty, int index)
{
ty = follow(ty);
ty = log->follow(ty);
if (ignoreChildren(ty))
return;
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
if (const FunctionTypeVar* ftv = log->getMutable<FunctionTypeVar>(ty))
{
visitChild(ftv->argTypes);
visitChild(ftv->retType);
}
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
else if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
{
LUAU_ASSERT(!ttv->boundTo);
for (const auto& [name, prop] : ttv->props)
@ -40,17 +41,17 @@ void Tarjan::visitChildren(TypeId ty, int index)
for (TypePackId itp : ttv->instantiatedTypePackParams)
visitChild(itp);
}
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
else if (const MetatableTypeVar* mtv = log->getMutable<MetatableTypeVar>(ty))
{
visitChild(mtv->table);
visitChild(mtv->metatable);
}
else if (const UnionTypeVar* utv = get<UnionTypeVar>(ty))
else if (const UnionTypeVar* utv = log->getMutable<UnionTypeVar>(ty))
{
for (TypeId opt : utv->options)
visitChild(opt);
}
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(ty))
else if (const IntersectionTypeVar* itv = log->getMutable<IntersectionTypeVar>(ty))
{
for (TypeId part : itv->parts)
visitChild(part);
@ -59,19 +60,19 @@ void Tarjan::visitChildren(TypeId ty, int index)
void Tarjan::visitChildren(TypePackId tp, int index)
{
tp = follow(tp);
tp = log->follow(tp);
if (ignoreChildren(tp))
return;
if (const TypePack* tpp = get<TypePack>(tp))
if (const TypePack* tpp = log->getMutable<TypePack>(tp))
{
for (TypeId tv : tpp->head)
visitChild(tv);
if (tpp->tail)
visitChild(*tpp->tail);
}
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
else if (const VariadicTypePack* vtp = log->getMutable<VariadicTypePack>(tp))
{
visitChild(vtp->ty);
}
@ -79,7 +80,7 @@ void Tarjan::visitChildren(TypePackId tp, int index)
std::pair<int, bool> Tarjan::indexify(TypeId ty)
{
ty = follow(ty);
ty = log->follow(ty);
bool fresh = !typeToIndex.contains(ty);
int& index = typeToIndex[ty];
@ -97,7 +98,7 @@ std::pair<int, bool> Tarjan::indexify(TypeId ty)
std::pair<int, bool> Tarjan::indexify(TypePackId tp)
{
tp = follow(tp);
tp = log->follow(tp);
bool fresh = !packToIndex.contains(tp);
int& index = packToIndex[tp];
@ -115,7 +116,7 @@ std::pair<int, bool> Tarjan::indexify(TypePackId tp)
void Tarjan::visitChild(TypeId ty)
{
ty = follow(ty);
ty = log->follow(ty);
edgesTy.push_back(ty);
edgesTp.push_back(nullptr);
@ -123,7 +124,7 @@ void Tarjan::visitChild(TypeId ty)
void Tarjan::visitChild(TypePackId tp)
{
tp = follow(tp);
tp = log->follow(tp);
edgesTy.push_back(nullptr);
edgesTp.push_back(tp);
@ -243,7 +244,7 @@ void Tarjan::clear()
TarjanResult Tarjan::visitRoot(TypeId ty)
{
childCount = 0;
ty = follow(ty);
ty = log->follow(ty);
clear();
auto [index, fresh] = indexify(ty);
@ -254,7 +255,7 @@ TarjanResult Tarjan::visitRoot(TypeId ty)
TarjanResult Tarjan::visitRoot(TypePackId tp)
{
childCount = 0;
tp = follow(tp);
tp = log->follow(tp);
clear();
auto [index, fresh] = indexify(tp);
@ -325,7 +326,7 @@ TarjanResult FindDirty::findDirty(TypePackId tp)
std::optional<TypeId> Substitution::substitute(TypeId ty)
{
ty = follow(ty);
ty = log->follow(ty);
newTypes.clear();
newPacks.clear();
@ -345,7 +346,7 @@ std::optional<TypeId> Substitution::substitute(TypeId ty)
std::optional<TypePackId> Substitution::substitute(TypePackId tp)
{
tp = follow(tp);
tp = log->follow(tp);
newTypes.clear();
newPacks.clear();
@ -365,11 +366,11 @@ std::optional<TypePackId> Substitution::substitute(TypePackId tp)
TypeId Substitution::clone(TypeId ty)
{
ty = follow(ty);
ty = log->follow(ty);
TypeId result = ty;
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
if (const FunctionTypeVar* ftv = log->getMutable<FunctionTypeVar>(ty))
{
FunctionTypeVar clone = FunctionTypeVar{ftv->level, ftv->argTypes, ftv->retType, ftv->definition, ftv->hasSelf};
clone.generics = ftv->generics;
@ -379,7 +380,7 @@ TypeId Substitution::clone(TypeId ty)
clone.argNames = ftv->argNames;
result = addType(std::move(clone));
}
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
else if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
{
LUAU_ASSERT(!ttv->boundTo);
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, ttv->level, ttv->state};
@ -392,19 +393,19 @@ TypeId Substitution::clone(TypeId ty)
clone.tags = ttv->tags;
result = addType(std::move(clone));
}
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
else if (const MetatableTypeVar* mtv = log->getMutable<MetatableTypeVar>(ty))
{
MetatableTypeVar clone = MetatableTypeVar{mtv->table, mtv->metatable};
clone.syntheticName = mtv->syntheticName;
result = addType(std::move(clone));
}
else if (const UnionTypeVar* utv = get<UnionTypeVar>(ty))
else if (const UnionTypeVar* utv = log->getMutable<UnionTypeVar>(ty))
{
UnionTypeVar clone;
clone.options = utv->options;
result = addType(std::move(clone));
}
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(ty))
else if (const IntersectionTypeVar* itv = log->getMutable<IntersectionTypeVar>(ty))
{
IntersectionTypeVar clone;
clone.parts = itv->parts;
@ -417,15 +418,15 @@ TypeId Substitution::clone(TypeId ty)
TypePackId Substitution::clone(TypePackId tp)
{
tp = follow(tp);
if (const TypePack* tpp = get<TypePack>(tp))
tp = log->follow(tp);
if (const TypePack* tpp = log->getMutable<TypePack>(tp))
{
TypePack clone;
clone.head = tpp->head;
clone.tail = tpp->tail;
return addTypePack(std::move(clone));
}
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
else if (const VariadicTypePack* vtp = log->getMutable<VariadicTypePack>(tp))
{
VariadicTypePack clone;
clone.ty = vtp->ty;
@ -437,7 +438,7 @@ TypePackId Substitution::clone(TypePackId tp)
void Substitution::foundDirty(TypeId ty)
{
ty = follow(ty);
ty = log->follow(ty);
if (isDirty(ty))
newTypes[ty] = clean(ty);
else
@ -446,7 +447,7 @@ void Substitution::foundDirty(TypeId ty)
void Substitution::foundDirty(TypePackId tp)
{
tp = follow(tp);
tp = log->follow(tp);
if (isDirty(tp))
newPacks[tp] = clean(tp);
else
@ -455,7 +456,7 @@ void Substitution::foundDirty(TypePackId tp)
TypeId Substitution::replace(TypeId ty)
{
ty = follow(ty);
ty = log->follow(ty);
if (TypeId* prevTy = newTypes.find(ty))
return *prevTy;
else
@ -464,7 +465,7 @@ TypeId Substitution::replace(TypeId ty)
TypePackId Substitution::replace(TypePackId tp)
{
tp = follow(tp);
tp = log->follow(tp);
if (TypePackId* prevTp = newPacks.find(tp))
return *prevTp;
else
@ -473,7 +474,7 @@ TypePackId Substitution::replace(TypePackId tp)
void Substitution::replaceChildren(TypeId ty)
{
ty = follow(ty);
ty = log->follow(ty);
if (ignoreChildren(ty))
return;
@ -519,7 +520,7 @@ void Substitution::replaceChildren(TypeId ty)
void Substitution::replaceChildren(TypePackId tp)
{
tp = follow(tp);
tp = log->follow(tp);
if (ignoreChildren(tp))
return;

View File

@ -1,6 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TxnLog.h"
#include "Luau/ToString.h"
#include "Luau/TypePack.h"
#include <algorithm>
@ -80,6 +81,56 @@ void DEPRECATED_TxnLog::popSeen(TypeId lhs, TypeId rhs)
sharedSeen->pop_back();
}
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;
}
static const TxnLog emptyLog;
const TxnLog* TxnLog::empty()
@ -199,8 +250,6 @@ PendingTypePack* TxnLog::queue(TypePackId tp)
PendingType* TxnLog::pending(TypeId ty) const
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
for (const TxnLog* current = this; current; current = current->parent)
{
if (auto it = current->typeVarChanges.find(ty); it != current->typeVarChanges.end())
@ -212,8 +261,6 @@ PendingType* TxnLog::pending(TypeId ty) const
PendingTypePack* TxnLog::pending(TypePackId tp) const
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
for (const TxnLog* current = this; current; current = current->parent)
{
if (auto it = current->typePackChanges.find(tp); it != current->typePackChanges.end())
@ -225,8 +272,6 @@ PendingTypePack* TxnLog::pending(TypePackId tp) const
PendingType* TxnLog::replace(TypeId ty, TypeVar replacement)
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
PendingType* newTy = queue(ty);
newTy->pending = replacement;
return newTy;
@ -234,8 +279,6 @@ PendingType* TxnLog::replace(TypeId ty, TypeVar replacement)
PendingTypePack* TxnLog::replace(TypePackId tp, TypePackVar replacement)
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
PendingTypePack* newTp = queue(tp);
newTp->pending = replacement;
return newTp;
@ -243,7 +286,6 @@ PendingTypePack* TxnLog::replace(TypePackId tp, TypePackVar replacement)
PendingType* TxnLog::bindTable(TypeId ty, std::optional<TypeId> newBoundTo)
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
LUAU_ASSERT(get<TableTypeVar>(ty));
PendingType* newTy = queue(ty);
@ -255,7 +297,6 @@ PendingType* TxnLog::bindTable(TypeId ty, std::optional<TypeId> newBoundTo)
PendingType* TxnLog::changeLevel(TypeId ty, TypeLevel newLevel)
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
LUAU_ASSERT(get<FreeTypeVar>(ty) || get<TableTypeVar>(ty) || get<FunctionTypeVar>(ty));
PendingType* newTy = queue(ty);
@ -278,7 +319,6 @@ PendingType* TxnLog::changeLevel(TypeId ty, TypeLevel newLevel)
PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel)
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
LUAU_ASSERT(get<FreeTypePack>(tp));
PendingTypePack* newTp = queue(tp);
@ -292,7 +332,6 @@ PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel)
PendingType* TxnLog::changeIndexer(TypeId ty, std::optional<TableIndexer> indexer)
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
LUAU_ASSERT(get<TableTypeVar>(ty));
PendingType* newTy = queue(ty);
@ -306,8 +345,6 @@ PendingType* TxnLog::changeIndexer(TypeId ty, std::optional<TableIndexer> indexe
std::optional<TypeLevel> TxnLog::getLevel(TypeId ty) const
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
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))
@ -318,10 +355,8 @@ std::optional<TypeLevel> TxnLog::getLevel(TypeId ty) const
return std::nullopt;
}
TypeId TxnLog::follow(TypeId ty)
TypeId TxnLog::follow(TypeId ty) const
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
return Luau::follow(ty, [this](TypeId ty) {
PendingType* state = this->pending(ty);
@ -337,8 +372,6 @@ TypeId TxnLog::follow(TypeId ty)
TypePackId TxnLog::follow(TypePackId tp) const
{
LUAU_ASSERT(FFlag::LuauUseCommittingTxnLog);
return Luau::follow(tp, [this](TypePackId tp) {
PendingTypePack* state = this->pending(tp);

View File

@ -32,6 +32,7 @@ LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
LUAU_FASTFLAGVARIABLE(LuauIfElseBranchTypeUnion, false)
LUAU_FASTFLAGVARIABLE(LuauIfElseExpectedType2, false)
LUAU_FASTFLAGVARIABLE(LuauLengthOnCompositeType, false)
LUAU_FASTFLAGVARIABLE(LuauNoSealedTypeMod, false)
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
LUAU_FASTFLAGVARIABLE(LuauSealExports, false)
LUAU_FASTFLAGVARIABLE(LuauSingletonTypes, false)
@ -40,13 +41,12 @@ LUAU_FASTFLAGVARIABLE(LuauTypeAliasDefaults, false)
LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
LUAU_FASTFLAGVARIABLE(LuauLValueAsKey, false)
LUAU_FASTFLAGVARIABLE(LuauRefiLookupFromIndexExpr, false)
LUAU_FASTFLAGVARIABLE(LuauPerModuleUnificationCache, false)
LUAU_FASTFLAGVARIABLE(LuauProperTypeLevels, false)
LUAU_FASTFLAGVARIABLE(LuauAscribeCorrectLevelToInferredProperitesOfFreeTables, false)
LUAU_FASTFLAGVARIABLE(LuauBidirectionalAsExpr, false)
LUAU_FASTFLAG(LuauUnionTagMatchFix)
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
LUAU_FASTFLAGVARIABLE(LuauAssertStripsFalsyTypes, false)
namespace Luau
{
@ -1117,7 +1117,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
ty = follow(ty);
if (tableSelf && !selfTy->persistent)
if (tableSelf && (FFlag::LuauNoSealedTypeMod ? tableSelf->state != TableState::Sealed : !selfTy->persistent))
tableSelf->props[indexName->index.value] = {ty, /* deprecated */ false, {}, indexName->indexLocation};
const FunctionTypeVar* funTy = get<FunctionTypeVar>(ty);
@ -1130,7 +1130,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
checkFunctionBody(funScope, ty, *function.func);
if (tableSelf && !selfTy->persistent)
if (tableSelf && (FFlag::LuauNoSealedTypeMod ? tableSelf->state != TableState::Sealed : !selfTy->persistent))
tableSelf->props[indexName->index.value] = {
follow(quantify(funScope, ty, indexName->indexLocation)), /* deprecated */ false, {}, indexName->indexLocation};
}
@ -1657,7 +1657,7 @@ std::optional<TypeId> TypeChecker::getIndexTypeFromType(
RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit);
// Not needed when we normalize types.
if (FFlag::LuauLValueAsKey && get<AnyTypeVar>(follow(t)))
if (get<AnyTypeVar>(follow(t)))
return t;
if (std::optional<TypeId> ty = getIndexTypeFromType(scope, t, name, location, false))
@ -1802,12 +1802,9 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprIn
{
TypeId ty = checkLValue(scope, expr);
if (FFlag::LuauRefiLookupFromIndexExpr)
{
if (std::optional<LValue> lvalue = tryGetLValue(expr))
if (std::optional<TypeId> refiTy = resolveLValue(scope, *lvalue))
return {*refiTy, {TruthyPredicate{std::move(*lvalue), expr.location}}};
}
if (std::optional<LValue> lvalue = tryGetLValue(expr))
if (std::optional<TypeId> refiTy = resolveLValue(scope, *lvalue))
return {*refiTy, {TruthyPredicate{std::move(*lvalue), expr.location}}};
return {ty};
}
@ -2471,33 +2468,28 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprBi
{
if (expr.op == AstExprBinary::And)
{
ExprResult<TypeId> lhs = checkExpr(scope, *expr.left);
auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left);
// We can't just report errors here.
// This function can be called from AstStatLocal or from AstStatIf, or even from AstExprBinary (and others).
// For now, ignore the errors returned by the predicate resolver.
// We may need an extra property for each predicate set that indicates it has been resolved.
// Requires a slight modification to the data structure.
ScopePtr innerScope = childScope(scope, expr.location);
resolve(lhs.predicates, innerScope, true);
resolve(lhsPredicates, innerScope, true);
ExprResult<TypeId> rhs = checkExpr(innerScope, *expr.right);
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right);
return {checkBinaryOperation(FFlag::LuauDiscriminableUnions ? scope : innerScope, expr, lhs.type, rhs.type),
{AndPredicate{std::move(lhs.predicates), std::move(rhs.predicates)}}};
return {checkBinaryOperation(FFlag::LuauDiscriminableUnions ? scope : innerScope, expr, lhsTy, rhsTy),
{AndPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
}
else if (expr.op == AstExprBinary::Or)
{
ExprResult<TypeId> lhs = checkExpr(scope, *expr.left);
auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left);
ScopePtr innerScope = childScope(scope, expr.location);
resolve(lhs.predicates, innerScope, false);
resolve(lhsPredicates, innerScope, false);
ExprResult<TypeId> rhs = checkExpr(innerScope, *expr.right);
auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right);
// Because of C++, I'm not sure if lhs.predicates was not moved out by the time we call checkBinaryOperation.
TypeId result = checkBinaryOperation(FFlag::LuauDiscriminableUnions ? scope : innerScope, expr, lhs.type, rhs.type, lhs.predicates);
return {result, {OrPredicate{std::move(lhs.predicates), std::move(rhs.predicates)}}};
// Because of C++, I'm not sure if lhsPredicates was not moved out by the time we call checkBinaryOperation.
TypeId result = checkBinaryOperation(FFlag::LuauDiscriminableUnions ? scope : innerScope, expr, lhsTy, rhsTy, lhsPredicates);
return {result, {OrPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}};
}
else if (expr.op == AstExprBinary::CompareEq || expr.op == AstExprBinary::CompareNe)
{
@ -2535,27 +2527,15 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprTy
TypeId annotationType = resolveType(scope, *expr.annotation);
ExprResult<TypeId> result = checkExpr(scope, *expr.expr, annotationType);
if (FFlag::LuauBidirectionalAsExpr)
{
// Note: As an optimization, we try 'number <: number | string' first, as that is the more likely case.
if (canUnify(annotationType, result.type, expr.location).empty())
return {annotationType, std::move(result.predicates)};
if (canUnify(result.type, annotationType, expr.location).empty())
return {annotationType, std::move(result.predicates)};
reportError(expr.location, TypesAreUnrelated{result.type, annotationType});
return {errorRecoveryType(annotationType), std::move(result.predicates)};
}
else
{
ErrorVec errorVec = canUnify(annotationType, result.type, expr.location);
reportErrors(errorVec);
if (!errorVec.empty())
annotationType = errorRecoveryType(annotationType);
// Note: As an optimization, we try 'number <: number | string' first, as that is the more likely case.
if (canUnify(annotationType, result.type, expr.location).empty())
return {annotationType, std::move(result.predicates)};
}
if (canUnify(result.type, annotationType, expr.location).empty())
return {annotationType, std::move(result.predicates)};
reportError(expr.location, TypesAreUnrelated{result.type, annotationType});
return {errorRecoveryType(annotationType), std::move(result.predicates)};
}
ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprError& expr)
@ -4295,7 +4275,7 @@ void TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId s
child.tryUnify(subTy, superTy, /*isFunctionCall*/ false);
if (!child.errors.empty())
{
TypeId instantiated = instantiate(scope, subTy, state.location);
TypeId instantiated = instantiate(scope, subTy, state.location, &child.log);
if (subTy == instantiated)
{
// Instantiating the argument made no difference, so just report any child errors
@ -4330,7 +4310,7 @@ void TypeChecker::unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId s
bool Instantiation::isDirty(TypeId ty)
{
if (get<FunctionTypeVar>(ty))
if (log->getMutable<FunctionTypeVar>(ty))
return true;
else
return false;
@ -4343,7 +4323,7 @@ bool Instantiation::isDirty(TypePackId tp)
bool Instantiation::ignoreChildren(TypeId ty)
{
if (get<FunctionTypeVar>(ty))
if (log->getMutable<FunctionTypeVar>(ty))
return true;
else
return false;
@ -4351,7 +4331,7 @@ bool Instantiation::ignoreChildren(TypeId ty)
TypeId Instantiation::clean(TypeId ty)
{
const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty);
const FunctionTypeVar* ftv = log->getMutable<FunctionTypeVar>(ty);
LUAU_ASSERT(ftv);
FunctionTypeVar clone = FunctionTypeVar{level, ftv->argTypes, ftv->retType, ftv->definition, ftv->hasSelf};
@ -4362,6 +4342,7 @@ TypeId Instantiation::clean(TypeId ty)
// Annoyingly, we have to do this even if there are no generics,
// to replace any generic tables.
replaceGenerics.log = log;
replaceGenerics.level = level;
replaceGenerics.currentModule = currentModule;
replaceGenerics.generics.assign(ftv->generics.begin(), ftv->generics.end());
@ -4383,7 +4364,7 @@ TypePackId Instantiation::clean(TypePackId tp)
bool ReplaceGenerics::ignoreChildren(TypeId ty)
{
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
if (const FunctionTypeVar* ftv = log->getMutable<FunctionTypeVar>(ty))
// We aren't recursing in the case of a generic function which
// binds the same generics. This can happen if, for example, there's recursive types.
// If T = <a>(a,T)->T then instantiating T should produce T' = (X,T)->T not T' = (X,T')->T'.
@ -4396,9 +4377,9 @@ bool ReplaceGenerics::ignoreChildren(TypeId ty)
bool ReplaceGenerics::isDirty(TypeId ty)
{
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
return ttv->state == TableState::Generic;
else if (get<GenericTypeVar>(ty))
else if (log->getMutable<GenericTypeVar>(ty))
return std::find(generics.begin(), generics.end(), ty) != generics.end();
else
return false;
@ -4406,7 +4387,7 @@ bool ReplaceGenerics::isDirty(TypeId ty)
bool ReplaceGenerics::isDirty(TypePackId tp)
{
if (get<GenericTypePack>(tp))
if (log->getMutable<GenericTypePack>(tp))
return std::find(genericPacks.begin(), genericPacks.end(), tp) != genericPacks.end();
else
return false;
@ -4415,7 +4396,7 @@ bool ReplaceGenerics::isDirty(TypePackId tp)
TypeId ReplaceGenerics::clean(TypeId ty)
{
LUAU_ASSERT(isDirty(ty));
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
{
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, level, TableState::Free};
clone.methodDefinitionLocations = ttv->methodDefinitionLocations;
@ -4434,9 +4415,9 @@ TypePackId ReplaceGenerics::clean(TypePackId tp)
bool Quantification::isDirty(TypeId ty)
{
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
return level.subsumes(ttv->level) && ((ttv->state == TableState::Free) || (ttv->state == TableState::Unsealed));
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
else if (const FreeTypeVar* ftv = log->getMutable<FreeTypeVar>(ty))
return level.subsumes(ftv->level);
else
return false;
@ -4444,7 +4425,7 @@ bool Quantification::isDirty(TypeId ty)
bool Quantification::isDirty(TypePackId tp)
{
if (const FreeTypePack* ftv = get<FreeTypePack>(tp))
if (const FreeTypePack* ftv = log->getMutable<FreeTypePack>(tp))
return level.subsumes(ftv->level);
else
return false;
@ -4453,7 +4434,7 @@ bool Quantification::isDirty(TypePackId tp)
TypeId Quantification::clean(TypeId ty)
{
LUAU_ASSERT(isDirty(ty));
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
{
TableState state = (ttv->state == TableState::Unsealed ? TableState::Sealed : TableState::Generic);
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, level, state};
@ -4479,9 +4460,9 @@ TypePackId Quantification::clean(TypePackId tp)
bool Anyification::isDirty(TypeId ty)
{
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
return (ttv->state == TableState::Free || (FFlag::LuauSealExports && ttv->state == TableState::Unsealed));
else if (get<FreeTypeVar>(ty))
else if (log->getMutable<FreeTypeVar>(ty))
return true;
else
return false;
@ -4489,7 +4470,7 @@ bool Anyification::isDirty(TypeId ty)
bool Anyification::isDirty(TypePackId tp)
{
if (get<FreeTypePack>(tp))
if (log->getMutable<FreeTypePack>(tp))
return true;
else
return false;
@ -4498,7 +4479,7 @@ bool Anyification::isDirty(TypePackId tp)
TypeId Anyification::clean(TypeId ty)
{
LUAU_ASSERT(isDirty(ty));
if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
{
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, ttv->level, TableState::Sealed};
clone.methodDefinitionLocations = ttv->methodDefinitionLocations;
@ -4535,6 +4516,7 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
return ty;
}
quantification.log = TxnLog::empty();
quantification.level = scope->level;
quantification.generics.clear();
quantification.genericPacks.clear();
@ -4558,8 +4540,11 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
return *qty;
}
TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location location)
TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log)
{
LUAU_ASSERT(log != nullptr);
instantiation.log = FFlag::LuauUseCommittingTxnLog ? log : TxnLog::empty();
instantiation.level = scope->level;
instantiation.currentModule = currentModule;
std::optional<TypeId> instantiated = instantiation.substitute(ty);
@ -4574,6 +4559,7 @@ TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location locat
TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location)
{
anyification.log = TxnLog::empty();
anyification.anyType = anyType;
anyification.anyTypePack = anyTypePack;
anyification.currentModule = currentModule;
@ -4589,6 +4575,7 @@ TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location)
TypePackId TypeChecker::anyify(const ScopePtr& scope, TypePackId ty, Location location)
{
anyification.log = TxnLog::empty();
anyification.anyType = anyType;
anyification.anyTypePack = anyTypePack;
anyification.currentModule = currentModule;
@ -4660,7 +4647,7 @@ void TypeChecker::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& d
}
};
if (auto ttv = getTableType(follow(utk->table)))
if (auto ttv = getTableType(FFlag::LuauUnionTagMatchFix ? utk->table : follow(utk->table)))
accumulate(ttv->props);
else if (auto ctv = get<ClassTypeVar>(follow(utk->table)))
{
@ -4775,6 +4762,29 @@ TypePackId TypeChecker::errorRecoveryTypePack(TypePackId guess)
return getSingletonTypes().errorRecoveryTypePack(guess);
}
TypeIdPredicate TypeChecker::mkTruthyPredicate(bool sense) {
return [this, sense](TypeId ty) -> std::optional<TypeId> {
// any/error/free gets a special pass unconditionally because they can't be decided.
if (get<AnyTypeVar>(ty) || get<ErrorTypeVar>(ty) || get<FreeTypeVar>(ty))
return ty;
// maps boolean primitive to the corresponding singleton equal to sense
if (isPrim(ty, PrimitiveTypeVar::Boolean))
return singletonType(sense);
// if we have boolean singleton, eliminate it if the sense doesn't match with that singleton
if (auto boolean = get<BooleanSingleton>(get<SingletonTypeVar>(ty)))
return boolean->value == sense ? std::optional<TypeId>(ty) : std::nullopt;
// if we have nil, eliminate it if sense is true, otherwise take it
if (isNil(ty))
return sense ? std::nullopt : std::optional<TypeId>(ty);
// at this point, anything else is kept if sense is true, or eliminated otherwise
return sense ? std::optional<TypeId>(ty) : std::nullopt;
};
}
std::optional<TypeId> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate)
{
std::vector<TypeId> types = Luau::filterMap(type, predicate);
@ -4783,6 +4793,11 @@ std::optional<TypeId> TypeChecker::filterMap(TypeId type, TypeIdPredicate predic
return std::nullopt;
}
std::optional<TypeId> TypeChecker::pickTypesFromSense(TypeId type, bool sense)
{
return filterMap(type, mkTruthyPredicate(sense));
}
TypeId TypeChecker::addTV(TypeVar&& tv)
{
return currentModule->internalTypes.addType(std::move(tv));
@ -4962,6 +4977,7 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
if (notEnoughParameters && hasDefaultParameters)
{
// 'applyTypeFunction' is used to substitute default types that reference previous generic types
applyTypeFunction.log = TxnLog::empty();
applyTypeFunction.typeArguments.clear();
applyTypeFunction.typePackArguments.clear();
applyTypeFunction.currentModule = currentModule;
@ -5293,6 +5309,7 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf,
for (size_t i = 0; i < tf.typePackParams.size(); ++