luau/Analysis/src/TypeUtils.cpp

287 lines
8 KiB
C++
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeUtils.h"
2022-08-18 22:04:33 +01:00
#include "Luau/Normalize.h"
#include "Luau/Scope.h"
#include "Luau/ToString.h"
#include "Luau/TypeInfer.h"
2022-10-21 18:33:43 +01:00
#include <algorithm>
namespace Luau
{
2022-09-08 22:44:50 +01:00
std::optional<TypeId> findMetatableEntry(
NotNull<SingletonTypes> singletonTypes, ErrorVec& errors, TypeId type, const std::string& entry, Location location)
{
type = follow(type);
2022-09-08 22:44:50 +01:00
std::optional<TypeId> metatable = getMetatable(type, singletonTypes);
if (!metatable)
return std::nullopt;
TypeId unwrapped = follow(*metatable);
if (get<AnyTypeVar>(unwrapped))
2022-09-08 22:44:50 +01:00
return singletonTypes->anyType;
const TableTypeVar* mtt = getTableType(unwrapped);
if (!mtt)
{
2022-07-08 02:05:31 +01:00
errors.push_back(TypeError{location, GenericError{"Metatable was not a table"}});
return std::nullopt;
}
auto it = mtt->props.find(entry);
if (it != mtt->props.end())
return it->second.type;
else
return std::nullopt;
}
2022-09-08 22:44:50 +01:00
std::optional<TypeId> findTablePropertyRespectingMeta(
NotNull<SingletonTypes> singletonTypes, ErrorVec& errors, TypeId ty, const std::string& name, Location location)
{
if (get<AnyTypeVar>(ty))
return ty;
if (const TableTypeVar* tableType = getTableType(ty))
{
const auto& it = tableType->props.find(name);
if (it != tableType->props.end())
return it->second.type;
}
2022-09-08 22:44:50 +01:00
std::optional<TypeId> mtIndex = findMetatableEntry(singletonTypes, errors, ty, "__index", location);
2022-02-18 00:41:20 +00:00
int count = 0;
while (mtIndex)
{
TypeId index = follow(*mtIndex);
2022-02-18 00:41:20 +00:00
2022-05-20 00:46:52 +01:00
if (count >= 100)
return std::nullopt;
2022-02-18 00:41:20 +00:00
2022-05-20 00:46:52 +01:00
++count;
2022-02-18 00:41:20 +00:00
if (const auto& itt = getTableType(index))
{
const auto& fit = itt->props.find(name);
if (fit != itt->props.end())
return fit->second.type;
}
else if (const auto& itf = get<FunctionTypeVar>(index))
{
2022-06-17 01:54:42 +01:00
std::optional<TypeId> r = first(follow(itf->retTypes));
if (!r)
2022-09-08 22:44:50 +01:00
return singletonTypes->nilType;
else
return *r;
}
else if (get<AnyTypeVar>(index))
2022-09-08 22:44:50 +01:00
return singletonTypes->anyType;
else
errors.push_back(TypeError{location, GenericError{"__index should either be a function or table. Got " + toString(index)}});
2022-09-08 22:44:50 +01:00
mtIndex = findMetatableEntry(singletonTypes, errors, *mtIndex, "__index", location);
}
return std::nullopt;
}
2022-09-15 23:13:58 +01:00
std::pair<size_t, std::optional<size_t>> getParameterExtents(const TxnLog* log, TypePackId tp, bool includeHiddenVariadics)
2022-09-02 00:00:14 +01:00
{
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;
}
2022-10-13 23:59:53 +01:00
if (it.tail() && isVariadicTail(*it.tail(), *log, includeHiddenVariadics))
2022-09-02 00:00:14 +01:00
return {minCount, std::nullopt};
else
return {minCount, minCount + optionalCount};
}
2022-12-02 10:46:05 +00:00
TypePack extendTypePack(TypeArena& arena, NotNull<SingletonTypes> singletonTypes, TypePackId pack, size_t length)
2022-09-29 23:11:54 +01:00
{
2022-12-02 10:46:05 +00:00
TypePack result;
2022-09-29 23:11:54 +01:00
2022-12-02 10:46:05 +00:00
while (true)
2022-09-29 23:11:54 +01:00
{
2022-12-02 10:46:05 +00:00
pack = follow(pack);
if (const TypePack* p = get<TypePack>(pack))
{
size_t i = 0;
while (i < p->head.size() && result.head.size() < length)
{
result.head.push_back(p->head[i]);
++i;
}
if (result.head.size() == length)
{
if (i == p->head.size())
result.tail = p->tail;
else
{
TypePackId newTail = arena.addTypePack(TypePack{});
TypePack* newTailPack = getMutable<TypePack>(newTail);
newTailPack->head.insert(newTailPack->head.begin(), p->head.begin() + i, p->head.end());
newTailPack->tail = p->tail;
2022-09-29 23:11:54 +01:00
2022-12-02 10:46:05 +00:00
result.tail = newTail;
}
return result;
}
else if (p->tail)
{
pack = *p->tail;
continue;
}
else
{
// There just aren't enough types in this pack to satisfy the request.
return result;
}
}
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(pack))
{
while (result.head.size() < length)
result.head.push_back(vtp->ty);
result.tail = pack;
2022-09-29 23:11:54 +01:00
return result;
2022-12-02 10:46:05 +00:00
}
else if (FreeTypePack* ftp = getMutable<FreeTypePack>(pack))
{
// If we need to get concrete types out of a free pack, we choose to
// interpret this as proof that the pack must have at least 'length'
// elements. We mint fresh types for each element we're extracting
// and rebind the free pack to be a TypePack containing them. We
// also have to create a new tail.
2022-09-29 23:11:54 +01:00
2022-12-02 10:46:05 +00:00
TypePack newPack;
newPack.tail = arena.freshTypePack(ftp->scope);
2022-09-29 23:11:54 +01:00
2022-12-02 10:46:05 +00:00
while (result.head.size() < length)
{
newPack.head.push_back(arena.freshType(ftp->scope));
result.head.push_back(newPack.head.back());
}
2022-09-29 23:11:54 +01:00
2022-12-02 10:46:05 +00:00
asMutable(pack)->ty.emplace<TypePack>(std::move(newPack));
2022-09-29 23:11:54 +01:00
2022-12-02 10:46:05 +00:00
return result;
}
else if (const Unifiable::Error* etp = getMutable<Unifiable::Error>(pack))
{
while (result.head.size() < length)
result.head.push_back(singletonTypes->errorRecoveryType());
result.tail = pack;
return result;
}
else
{
// If the pack is blocked or generic, we can't extract.
// Return whatever we've got with this pack as the tail.
result.tail = pack;
return result;
}
}
2022-09-29 23:11:54 +01:00
}
2022-10-21 18:33:43 +01:00
std::vector<TypeId> reduceUnion(const std::vector<TypeId>& types)
{
std::vector<TypeId> result;
for (TypeId t : types)
{
t = follow(t);
if (get<NeverTypeVar>(t))
continue;
if (get<ErrorTypeVar>(t) || get<AnyTypeVar>(t))
return {t};
if (const UnionTypeVar* utv = get<UnionTypeVar>(t))
{
for (TypeId ty : utv)
{
ty = follow(ty);
if (get<NeverTypeVar>(ty))
continue;
if (get<ErrorTypeVar>(ty) || get<AnyTypeVar>(ty))
return {ty};
if (result.end() == std::find(result.begin(), result.end(), ty))
result.push_back(ty);
}
}
else if (std::find(result.begin(), result.end(), t) == result.end())
result.push_back(t);
}
return result;
}
static std::optional<TypeId> tryStripUnionFromNil(TypeArena& arena, TypeId ty)
{
if (const UnionTypeVar* utv = get<UnionTypeVar>(ty))
{
if (!std::any_of(begin(utv), end(utv), isNil))
return ty;
std::vector<TypeId> result;
for (TypeId option : utv)
{
if (!isNil(option))
result.push_back(option);
}
if (result.empty())
return std::nullopt;
return result.size() == 1 ? result[0] : arena.addType(UnionTypeVar{std::move(result)});
}
return std::nullopt;
}
TypeId stripNil(NotNull<SingletonTypes> singletonTypes, TypeArena& arena, TypeId ty)
{
ty = follow(ty);
if (get<UnionTypeVar>(ty))
{
std::optional<TypeId> cleaned = tryStripUnionFromNil(arena, ty);
// If there is no union option without 'nil'
if (!cleaned)
return singletonTypes->nilType;
return follow(*cleaned);
}
return follow(ty);
}
} // namespace Luau