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/TypeUtils.h"
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
#include "Luau/Normalize.h"
|
2021-11-05 02:07:18 +00:00
|
|
|
#include "Luau/Scope.h"
|
2021-10-29 21:25:12 +01:00
|
|
|
#include "Luau/ToString.h"
|
|
|
|
#include "Luau/TypeInfer.h"
|
|
|
|
|
2022-09-15 23:13:58 +01:00
|
|
|
LUAU_FASTFLAG(LuauFunctionArgMismatchDetails)
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
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)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
|
|
|
type = follow(type);
|
|
|
|
|
2022-09-08 22:44:50 +01:00
|
|
|
std::optional<TypeId> metatable = getMetatable(type, singletonTypes);
|
2021-10-29 21:25:12 +01:00
|
|
|
if (!metatable)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
TypeId unwrapped = follow(*metatable);
|
|
|
|
|
|
|
|
if (get<AnyTypeVar>(unwrapped))
|
2022-09-08 22:44:50 +01:00
|
|
|
return singletonTypes->anyType;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
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"}});
|
2021-10-29 21:25:12 +01:00
|
|
|
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)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
|
|
|
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;
|
2021-10-29 21:25:12 +01:00
|
|
|
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
|
|
|
|
2021-10-29 21:25:12 +01: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));
|
2021-10-29 21:25:12 +01:00
|
|
|
if (!r)
|
2022-09-08 22:44:50 +01:00
|
|
|
return singletonTypes->nilType;
|
2021-10-29 21:25:12 +01:00
|
|
|
else
|
|
|
|
return *r;
|
|
|
|
}
|
|
|
|
else if (get<AnyTypeVar>(index))
|
2022-09-08 22:44:50 +01:00
|
|
|
return singletonTypes->anyType;
|
2021-10-29 21:25:12 +01:00
|
|
|
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);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2022-09-08 22:44:50 +01:00
|
|
|
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, NotNull<SingletonTypes> singletonTypes,
|
|
|
|
TypeId type, const std::string& prop, const Location& location, bool addErrors, InternalErrorReporter& handle)
|
2022-08-18 22:04:33 +01:00
|
|
|
{
|
|
|
|
type = follow(type);
|
|
|
|
|
|
|
|
if (get<ErrorTypeVar>(type) || get<AnyTypeVar>(type) || get<NeverTypeVar>(type))
|
|
|
|
return type;
|
|
|
|
|
|
|
|
if (auto f = get<FreeTypeVar>(type))
|
|
|
|
*asMutable(type) = TableTypeVar{TableState::Free, f->level};
|
|
|
|
|
|
|
|
if (isString(type))
|
|
|
|
{
|
2022-09-08 22:44:50 +01:00
|
|
|
std::optional<TypeId> mtIndex = Luau::findMetatableEntry(singletonTypes, errors, singletonTypes->stringType, "__index", location);
|
2022-08-18 22:04:33 +01:00
|
|
|
LUAU_ASSERT(mtIndex);
|
|
|
|
type = *mtIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getTableType(type))
|
|
|
|
{
|
2022-09-08 22:44:50 +01:00
|
|
|
return findTablePropertyRespectingMeta(singletonTypes, errors, type, prop, location);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
else if (const ClassTypeVar* cls = get<ClassTypeVar>(type))
|
|
|
|
{
|
|
|
|
if (const Property* p = lookupClassProp(cls, prop))
|
|
|
|
return p->type;
|
|
|
|
}
|
|
|
|
else if (const UnionTypeVar* utv = get<UnionTypeVar>(type))
|
|
|
|
{
|
|
|
|
std::vector<TypeId> goodOptions;
|
|
|
|
std::vector<TypeId> badOptions;
|
|
|
|
|
|
|
|
for (TypeId t : utv)
|
|
|
|
{
|
|
|
|
// TODO: we should probably limit recursion here?
|
|
|
|
// RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit);
|
|
|
|
|
|
|
|
// Not needed when we normalize types.
|
|
|
|
if (get<AnyTypeVar>(follow(t)))
|
|
|
|
return t;
|
|
|
|
|
2022-09-08 22:44:50 +01:00
|
|
|
if (std::optional<TypeId> ty =
|
|
|
|
getIndexTypeFromType(scope, errors, arena, singletonTypes, t, prop, location, /* addErrors= */ false, handle))
|
2022-08-18 22:04:33 +01:00
|
|
|
goodOptions.push_back(*ty);
|
|
|
|
else
|
|
|
|
badOptions.push_back(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!badOptions.empty())
|
|
|
|
{
|
|
|
|
if (addErrors)
|
|
|
|
{
|
|
|
|
if (goodOptions.empty())
|
|
|
|
errors.push_back(TypeError{location, UnknownProperty{type, prop}});
|
|
|
|
else
|
|
|
|
errors.push_back(TypeError{location, MissingUnionProperty{type, badOptions, prop}});
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (goodOptions.empty())
|
2022-09-08 22:44:50 +01:00
|
|
|
return singletonTypes->neverType;
|
2022-08-18 22:04:33 +01:00
|
|
|
|
|
|
|
if (goodOptions.size() == 1)
|
|
|
|
return goodOptions[0];
|
|
|
|
|
|
|
|
// TODO: inefficient.
|
|
|
|
TypeId result = arena->addType(UnionTypeVar{std::move(goodOptions)});
|
2022-09-08 22:44:50 +01:00
|
|
|
auto [ty, ok] = normalize(result, NotNull{scope.get()}, *arena, singletonTypes, handle);
|
2022-08-18 22:04:33 +01:00
|
|
|
if (!ok && addErrors)
|
|
|
|
errors.push_back(TypeError{location, NormalizationTooComplex{}});
|
2022-09-08 22:44:50 +01:00
|
|
|
return ok ? ty : singletonTypes->anyType;
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
else if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(type))
|
|
|
|
{
|
|
|
|
std::vector<TypeId> parts;
|
|
|
|
|
|
|
|
for (TypeId t : itv->parts)
|
|
|
|
{
|
|
|
|
// TODO: we should probably limit recursion here?
|
|
|
|
// RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit);
|
|
|
|
|
2022-09-08 22:44:50 +01:00
|
|
|
if (std::optional<TypeId> ty =
|
|
|
|
getIndexTypeFromType(scope, errors, arena, singletonTypes, t, prop, location, /* addErrors= */ false, handle))
|
2022-08-18 22:04:33 +01:00
|
|
|
parts.push_back(*ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no parts of the intersection had the property we looked up for, it never existed at all.
|
|
|
|
if (parts.empty())
|
|
|
|
{
|
|
|
|
if (addErrors)
|
|
|
|
errors.push_back(TypeError{location, UnknownProperty{type, prop}});
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parts.size() == 1)
|
|
|
|
return parts[0];
|
|
|
|
|
|
|
|
return arena->addType(IntersectionTypeVar{std::move(parts)}); // Not at all correct.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addErrors)
|
|
|
|
errors.push_back(TypeError{location, UnknownProperty{type, prop}});
|
|
|
|
|
|
|
|
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-09-15 23:13:58 +01:00
|
|
|
if (it.tail() && (!FFlag::LuauFunctionArgMismatchDetails || isVariadicTail(*it.tail(), *log, includeHiddenVariadics)))
|
2022-09-02 00:00:14 +01:00
|
|
|
return {minCount, std::nullopt};
|
|
|
|
else
|
|
|
|
return {minCount, minCount + optionalCount};
|
|
|
|
}
|
|
|
|
|
2022-09-29 23:11:54 +01:00
|
|
|
std::vector<TypeId> flatten(TypeArena& arena, NotNull<SingletonTypes> singletonTypes, TypePackId pack, size_t length)
|
|
|
|
{
|
|
|
|
std::vector<TypeId> result;
|
|
|
|
|
|
|
|
auto it = begin(pack);
|
|
|
|
auto endIt = end(pack);
|
|
|
|
|
|
|
|
while (it != endIt)
|
|
|
|
{
|
|
|
|
result.push_back(*it);
|
|
|
|
|
|
|
|
if (result.size() >= length)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!it.tail())
|
|
|
|
return result;
|
|
|
|
|
|
|
|
TypePackId tail = *it.tail();
|
|
|
|
if (get<TypePack>(tail))
|
|
|
|
LUAU_ASSERT(0);
|
|
|
|
else if (auto vtp = get<VariadicTypePack>(tail))
|
|
|
|
{
|
|
|
|
while (result.size() < length)
|
|
|
|
result.push_back(vtp->ty);
|
|
|
|
}
|
|
|
|
else if (get<FreeTypePack>(tail) || get<GenericTypePack>(tail))
|
|
|
|
{
|
|
|
|
while (result.size() < length)
|
|
|
|
result.push_back(arena.addType(FreeTypeVar{nullptr}));
|
|
|
|
}
|
|
|
|
else if (auto etp = get<Unifiable::Error>(tail))
|
|
|
|
{
|
|
|
|
while (result.size() < length)
|
|
|
|
result.push_back(singletonTypes->errorRecoveryType());
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
} // namespace Luau
|