2022-09-08 22:44:50 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
2022-06-17 01:54:42 +01:00
|
|
|
#include "Luau/TypeChecker2.h"
|
|
|
|
|
|
|
|
#include "Luau/Ast.h"
|
|
|
|
#include "Luau/AstQuery.h"
|
|
|
|
#include "Luau/Clone.h"
|
2023-02-24 18:24:22 +00:00
|
|
|
#include "Luau/Common.h"
|
2023-02-03 12:34:12 +00:00
|
|
|
#include "Luau/DcrLogger.h"
|
2023-01-20 12:02:39 +00:00
|
|
|
#include "Luau/Error.h"
|
2022-07-01 00:29:02 +01:00
|
|
|
#include "Luau/Instantiation.h"
|
2022-10-21 18:33:43 +01:00
|
|
|
#include "Luau/Metamethods.h"
|
2022-06-17 01:54:42 +01:00
|
|
|
#include "Luau/Normalize.h"
|
2022-08-18 22:04:33 +01:00
|
|
|
#include "Luau/ToString.h"
|
2022-07-01 00:29:02 +01:00
|
|
|
#include "Luau/TxnLog.h"
|
2023-01-03 17:33:19 +00:00
|
|
|
#include "Luau/Type.h"
|
2023-02-03 12:34:12 +00:00
|
|
|
#include "Luau/TypeReduction.h"
|
|
|
|
#include "Luau/TypeUtils.h"
|
2022-06-24 02:44:07 +01:00
|
|
|
#include "Luau/Unifier.h"
|
2022-09-08 22:44:50 +01:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
|
|
|
LUAU_FASTFLAG(DebugLuauDontReduceTypes)
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
LUAU_FASTFLAG(LuauNegatedClassTypes)
|
2022-06-17 01:54:42 +01:00
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2022-09-29 23:11:54 +01:00
|
|
|
// TypeInfer.h
|
|
|
|
// TODO move these
|
2022-10-13 23:59:53 +01:00
|
|
|
using PrintLineProc = void (*)(const std::string&);
|
2022-09-29 23:11:54 +01:00
|
|
|
extern PrintLineProc luauPrintLine;
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
/* Push a scope onto the end of a stack for the lifetime of the StackPusher instance.
|
|
|
|
* TypeChecker2 uses this to maintain knowledge about which scope encloses every
|
|
|
|
* given AstNode.
|
|
|
|
*/
|
|
|
|
struct StackPusher
|
|
|
|
{
|
|
|
|
std::vector<NotNull<Scope>>* stack;
|
|
|
|
NotNull<Scope> scope;
|
|
|
|
|
|
|
|
explicit StackPusher(std::vector<NotNull<Scope>>& stack, Scope* scope)
|
|
|
|
: stack(&stack)
|
|
|
|
, scope(scope)
|
|
|
|
{
|
|
|
|
stack.push_back(NotNull{scope});
|
|
|
|
}
|
|
|
|
|
|
|
|
~StackPusher()
|
|
|
|
{
|
|
|
|
if (stack)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(stack->back() == scope);
|
|
|
|
stack->pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
StackPusher(const StackPusher&) = delete;
|
|
|
|
StackPusher&& operator=(const StackPusher&) = delete;
|
|
|
|
|
|
|
|
StackPusher(StackPusher&& other)
|
|
|
|
: stack(std::exchange(other.stack, nullptr))
|
|
|
|
, scope(other.scope)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-21 18:33:43 +01:00
|
|
|
static std::optional<std::string> getIdentifierOfBaseVar(AstExpr* node)
|
|
|
|
{
|
|
|
|
if (AstExprGlobal* expr = node->as<AstExprGlobal>())
|
|
|
|
return expr->name.value;
|
|
|
|
|
|
|
|
if (AstExprLocal* expr = node->as<AstExprLocal>())
|
|
|
|
return expr->local->name.value;
|
|
|
|
|
|
|
|
if (AstExprIndexExpr* expr = node->as<AstExprIndexExpr>())
|
|
|
|
return getIdentifierOfBaseVar(expr->expr);
|
|
|
|
|
|
|
|
if (AstExprIndexName* expr = node->as<AstExprIndexName>())
|
|
|
|
return getIdentifierOfBaseVar(expr->expr);
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
struct TypeChecker2
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
NotNull<BuiltinTypes> builtinTypes;
|
2022-09-08 22:44:50 +01:00
|
|
|
DcrLogger* logger;
|
|
|
|
InternalErrorReporter ice; // FIXME accept a pointer from Frontend
|
2022-06-17 01:54:42 +01:00
|
|
|
const SourceModule* sourceModule;
|
|
|
|
Module* module;
|
2023-01-03 17:33:19 +00:00
|
|
|
TypeArena testArena;
|
2022-06-17 01:54:42 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
std::vector<NotNull<Scope>> stack;
|
|
|
|
|
2022-12-09 18:07:25 +00:00
|
|
|
UnifierSharedState sharedState{&ice};
|
2023-01-03 17:33:19 +00:00
|
|
|
Normalizer normalizer{&testArena, builtinTypes, NotNull{&sharedState}};
|
2022-12-09 18:07:25 +00:00
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
TypeChecker2(NotNull<BuiltinTypes> builtinTypes, DcrLogger* logger, const SourceModule* sourceModule, Module* module)
|
|
|
|
: builtinTypes(builtinTypes)
|
2022-09-08 22:44:50 +01:00
|
|
|
, logger(logger)
|
|
|
|
, sourceModule(sourceModule)
|
2022-06-17 01:54:42 +01:00
|
|
|
, module(module)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
std::optional<StackPusher> pushStack(AstNode* node)
|
|
|
|
{
|
|
|
|
if (Scope** scope = module->astScopes.find(node))
|
|
|
|
return StackPusher{stack, *scope};
|
|
|
|
else
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2022-06-17 01:54:42 +01:00
|
|
|
|
|
|
|
TypePackId lookupPack(AstExpr* expr)
|
|
|
|
{
|
2022-07-01 00:29:02 +01:00
|
|
|
// If a type isn't in the type graph, it probably means that a recursion limit was exceeded.
|
|
|
|
// We'll just return anyType in these cases. Typechecking against any is very fast and this
|
|
|
|
// allows us not to think about this very much in the actual typechecking logic.
|
2022-06-17 01:54:42 +01:00
|
|
|
TypePackId* tp = module->astTypePacks.find(expr);
|
2022-07-01 00:29:02 +01:00
|
|
|
if (tp)
|
|
|
|
return follow(*tp);
|
|
|
|
else
|
2023-01-03 17:33:19 +00:00
|
|
|
return builtinTypes->anyTypePack;
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TypeId lookupType(AstExpr* expr)
|
|
|
|
{
|
2022-07-01 00:29:02 +01:00
|
|
|
// If a type isn't in the type graph, it probably means that a recursion limit was exceeded.
|
|
|
|
// We'll just return anyType in these cases. Typechecking against any is very fast and this
|
|
|
|
// allows us not to think about this very much in the actual typechecking logic.
|
2022-06-17 01:54:42 +01:00
|
|
|
TypeId* ty = module->astTypes.find(expr);
|
2022-07-01 00:29:02 +01:00
|
|
|
if (ty)
|
|
|
|
return follow(*ty);
|
|
|
|
|
|
|
|
TypePackId* tp = module->astTypePacks.find(expr);
|
|
|
|
if (tp)
|
|
|
|
return flattenPack(*tp);
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
return builtinTypes->anyType;
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
|
2022-06-24 02:44:07 +01:00
|
|
|
TypeId lookupAnnotation(AstType* annotation)
|
|
|
|
{
|
2022-09-29 23:11:54 +01:00
|
|
|
if (FFlag::DebugLuauMagicTypes)
|
|
|
|
{
|
|
|
|
if (auto ref = annotation->as<AstTypeReference>(); ref && ref->name == "_luau_print" && ref->parameters.size > 0)
|
|
|
|
{
|
|
|
|
if (auto ann = ref->parameters.data[0].type)
|
|
|
|
{
|
|
|
|
TypeId argTy = lookupAnnotation(ref->parameters.data[0].type);
|
2022-10-13 23:59:53 +01:00
|
|
|
luauPrintLine(format(
|
|
|
|
"_luau_print (%d, %d): %s\n", annotation->location.begin.line, annotation->location.begin.column, toString(argTy).c_str()));
|
2022-09-29 23:11:54 +01:00
|
|
|
return follow(argTy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-24 02:44:07 +01:00
|
|
|
TypeId* ty = module->astResolvedTypes.find(annotation);
|
|
|
|
LUAU_ASSERT(ty);
|
|
|
|
return follow(*ty);
|
|
|
|
}
|
|
|
|
|
2022-08-04 22:27:28 +01:00
|
|
|
TypePackId lookupPackAnnotation(AstTypePack* annotation)
|
|
|
|
{
|
|
|
|
TypePackId* tp = module->astResolvedTypePacks.find(annotation);
|
|
|
|
LUAU_ASSERT(tp);
|
|
|
|
return follow(*tp);
|
|
|
|
}
|
|
|
|
|
2022-06-24 02:44:07 +01:00
|
|
|
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena)
|
|
|
|
{
|
2022-07-29 04:41:13 +01:00
|
|
|
if (exprs.size == 0)
|
|
|
|
return arena.addTypePack(TypePack{{}, std::nullopt});
|
|
|
|
|
2022-06-24 02:44:07 +01:00
|
|
|
std::vector<TypeId> head;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < exprs.size - 1; ++i)
|
|
|
|
{
|
|
|
|
head.push_back(lookupType(exprs.data[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TypePackId tail = lookupPack(exprs.data[exprs.size - 1]);
|
|
|
|
return arena.addTypePack(TypePack{head, tail});
|
|
|
|
}
|
|
|
|
|
2022-07-29 04:41:13 +01:00
|
|
|
Scope* findInnermostScope(Location location)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2022-07-29 04:41:13 +01:00
|
|
|
Scope* bestScope = module->getModuleScope().get();
|
|
|
|
Location bestLocation = module->scopes[0].first;
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-07-29 04:41:13 +01:00
|
|
|
for (size_t i = 0; i < module->scopes.size(); ++i)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2022-07-29 04:41:13 +01:00
|
|
|
auto& [scopeBounds, scope] = module->scopes[i];
|
2022-06-24 02:44:07 +01:00
|
|
|
if (scopeBounds.encloses(location))
|
|
|
|
{
|
|
|
|
if (scopeBounds.begin > bestLocation.begin || scopeBounds.end < bestLocation.end)
|
|
|
|
{
|
|
|
|
bestScope = scope.get();
|
|
|
|
bestLocation = scopeBounds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bestScope;
|
|
|
|
}
|
|
|
|
|
2023-01-20 12:02:39 +00:00
|
|
|
enum ValueContext
|
|
|
|
{
|
|
|
|
LValue,
|
|
|
|
RValue
|
|
|
|
};
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstStat* stat)
|
|
|
|
{
|
|
|
|
auto pusher = pushStack(stat);
|
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
if (auto s = stat->as<AstStatBlock>())
|
2022-08-18 22:04:33 +01:00
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatIf>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatWhile>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatRepeat>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatBreak>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatContinue>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatReturn>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatExpr>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatLocal>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatFor>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatForIn>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatAssign>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatCompoundAssign>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatFunction>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatLocalFunction>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatTypeAlias>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatDeclareFunction>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatDeclareClass>())
|
|
|
|
return visit(s);
|
|
|
|
else if (auto s = stat->as<AstStatError>())
|
|
|
|
return visit(s);
|
|
|
|
else
|
|
|
|
LUAU_ASSERT(!"TypeChecker2 encountered an unknown node type");
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatBlock* block)
|
|
|
|
{
|
|
|
|
auto StackPusher = pushStack(block);
|
|
|
|
|
|
|
|
for (AstStat* statement : block->body)
|
|
|
|
visit(statement);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatIf* ifStatement)
|
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(ifStatement->condition, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(ifStatement->thenbody);
|
|
|
|
if (ifStatement->elsebody)
|
|
|
|
visit(ifStatement->elsebody);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatWhile* whileStatement)
|
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(whileStatement->condition, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(whileStatement->body);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatRepeat* repeatStatement)
|
|
|
|
{
|
|
|
|
visit(repeatStatement->body);
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(repeatStatement->condition, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
2022-09-02 00:00:14 +01:00
|
|
|
void visit(AstStatBreak*) {}
|
2022-08-18 22:04:33 +01:00
|
|
|
|
2022-09-02 00:00:14 +01:00
|
|
|
void visit(AstStatContinue*) {}
|
2022-08-18 22:04:33 +01:00
|
|
|
|
|
|
|
void visit(AstStatReturn* ret)
|
|
|
|
{
|
|
|
|
Scope* scope = findInnermostScope(ret->location);
|
|
|
|
TypePackId expectedRetType = scope->returnType;
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
TypeArena* arena = &testArena;
|
2022-12-02 10:46:05 +00:00
|
|
|
TypePackId actualRetType = reconstructPack(ret->list, *arena);
|
2022-08-18 22:04:33 +01:00
|
|
|
|
2022-10-07 00:55:58 +01:00
|
|
|
Unifier u{NotNull{&normalizer}, Mode::Strict, stack.back(), ret->location, Covariant};
|
2022-08-18 22:04:33 +01:00
|
|
|
|
|
|
|
u.tryUnify(actualRetType, expectedRetType);
|
|
|
|
const bool ok = u.errors.empty() && u.log.empty();
|
|
|
|
|
|
|
|
if (!ok)
|
|
|
|
{
|
|
|
|
for (const TypeError& e : u.errors)
|
|
|
|
reportError(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (AstExpr* expr : ret->list)
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatExpr* expr)
|
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr->expr, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatLocal* local)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2022-09-29 23:11:54 +01:00
|
|
|
size_t count = std::max(local->values.size, local->vars.size);
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2022-09-29 23:11:54 +01:00
|
|
|
AstExpr* value = i < local->values.size ? local->values.data[i] : nullptr;
|
2023-02-24 18:24:22 +00:00
|
|
|
const bool isPack = value && (value->is<AstExprCall>() || value->is<AstExprVarargs>());
|
2022-08-18 22:04:33 +01:00
|
|
|
|
2022-09-29 23:11:54 +01:00
|
|
|
if (value)
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(value, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
if (i != local->values.size - 1 || !isPack)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2022-09-29 23:11:54 +01:00
|
|
|
AstLocal* var = i < local->vars.size ? local->vars.data[i] : nullptr;
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-09-29 23:11:54 +01:00
|
|
|
if (var && var->annotation)
|
|
|
|
{
|
2022-11-04 17:02:37 +00:00
|
|
|
TypeId annotationType = lookupAnnotation(var->annotation);
|
2022-09-29 23:11:54 +01:00
|
|
|
TypeId valueType = value ? lookupType(value) : nullptr;
|
2022-11-04 17:02:37 +00:00
|
|
|
if (valueType)
|
|
|
|
{
|
|
|
|
ErrorVec errors = tryUnify(stack.back(), value->location, valueType, annotationType);
|
|
|
|
if (!errors.empty())
|
|
|
|
reportErrors(std::move(errors));
|
|
|
|
}
|
2023-01-03 17:33:19 +00:00
|
|
|
|
|
|
|
visit(var->annotation);
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
|
|
|
}
|
2023-02-24 18:24:22 +00:00
|
|
|
else if (value)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2023-02-24 18:24:22 +00:00
|
|
|
TypePackId valuePack = lookupPack(value);
|
|
|
|
TypePack valueTypes;
|
|
|
|
if (i < local->vars.size)
|
|
|
|
valueTypes = extendTypePack(module->internalTypes, builtinTypes, valuePack, local->vars.size - i);
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
Location errorLocation;
|
2022-09-29 23:11:54 +01:00
|
|
|
for (size_t j = i; j < local->vars.size; ++j)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2023-02-24 18:24:22 +00:00
|
|
|
if (j - i >= valueTypes.head.size())
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2023-02-24 18:24:22 +00:00
|
|
|
errorLocation = local->vars.data[j]->location;
|
2022-09-29 23:11:54 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-01-27 21:28:45 +00:00
|
|
|
AstLocal* var = local->vars.data[j];
|
2022-09-29 23:11:54 +01:00
|
|
|
if (var->annotation)
|
|
|
|
{
|
|
|
|
TypeId varType = lookupAnnotation(var->annotation);
|
2023-02-24 18:24:22 +00:00
|
|
|
ErrorVec errors = tryUnify(stack.back(), value->location, valueTypes.head[j - i], varType);
|
2022-09-29 23:11:54 +01:00
|
|
|
if (!errors.empty())
|
|
|
|
reportErrors(std::move(errors));
|
2023-01-03 17:33:19 +00:00
|
|
|
|
|
|
|
visit(var->annotation);
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
2023-02-24 18:24:22 +00:00
|
|
|
}
|
2022-09-29 23:11:54 +01:00
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
if (valueTypes.head.size() < local->vars.size - i)
|
|
|
|
{
|
|
|
|
reportError(
|
|
|
|
CountMismatch{
|
|
|
|
// We subtract 1 here because the final AST
|
|
|
|
// expression is not worth one value. It is worth 0
|
|
|
|
// or more depending on valueTypes.head
|
|
|
|
local->values.size - 1 + valueTypes.head.size(),
|
|
|
|
std::nullopt,
|
|
|
|
local->vars.size,
|
|
|
|
local->values.data[local->values.size - 1]->is<AstExprCall>() ? CountMismatch::FunctionResult
|
|
|
|
: CountMismatch::ExprListResult,
|
|
|
|
},
|
|
|
|
errorLocation);
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatFor* forStatement)
|
|
|
|
{
|
2023-02-03 12:34:12 +00:00
|
|
|
NotNull<Scope> scope = stack.back();
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
if (forStatement->var->annotation)
|
2023-02-03 12:34:12 +00:00
|
|
|
{
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(forStatement->var->annotation);
|
2023-02-03 12:34:12 +00:00
|
|
|
reportErrors(tryUnify(scope, forStatement->var->location, builtinTypes->numberType, lookupAnnotation(forStatement->var->annotation)));
|
|
|
|
}
|
|
|
|
|
|
|
|
auto checkNumber = [this, scope](AstExpr* expr) {
|
|
|
|
if (!expr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
visit(expr, RValue);
|
|
|
|
reportErrors(tryUnify(scope, expr->location, lookupType(expr), builtinTypes->numberType));
|
|
|
|
};
|
|
|
|
|
|
|
|
checkNumber(forStatement->from);
|
|
|
|
checkNumber(forStatement->to);
|
|
|
|
checkNumber(forStatement->step);
|
2022-08-18 22:04:33 +01:00
|
|
|
|
|
|
|
visit(forStatement->body);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatForIn* forInStatement)
|
|
|
|
{
|
|
|
|
for (AstLocal* local : forInStatement->vars)
|
|
|
|
{
|
|
|
|
if (local->annotation)
|
|
|
|
visit(local->annotation);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (AstExpr* expr : forInStatement->values)
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr, RValue);
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(forInStatement->body);
|
2022-09-02 00:00:14 +01:00
|
|
|
|
|
|
|
// Rule out crazy stuff. Maybe possible if the file is not syntactically valid.
|
|
|
|
if (!forInStatement->vars.size || !forInStatement->values.size)
|
|
|
|
return;
|
|
|
|
|
|
|
|
NotNull<Scope> scope = stack.back();
|
2023-01-03 17:33:19 +00:00
|
|
|
TypeArena& arena = testArena;
|
2022-09-02 00:00:14 +01:00
|
|
|
|
|
|
|
std::vector<TypeId> variableTypes;
|
|
|
|
for (AstLocal* var : forInStatement->vars)
|
|
|
|
{
|
|
|
|
std::optional<TypeId> ty = scope->lookup(var);
|
|
|
|
LUAU_ASSERT(ty);
|
|
|
|
variableTypes.emplace_back(*ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ugh. There's nothing in the AST to hang a whole type pack on for the
|
|
|
|
// set of iteratees, so we have to piece it back together by hand.
|
|
|
|
std::vector<TypeId> valueTypes;
|
|
|
|
for (size_t i = 0; i < forInStatement->values.size - 1; ++i)
|
|
|
|
valueTypes.emplace_back(lookupType(forInStatement->values.data[i]));
|
|
|
|
TypePackId iteratorTail = lookupPack(forInStatement->values.data[forInStatement->values.size - 1]);
|
2022-09-15 23:13:58 +01:00
|
|
|
TypePackId iteratorPack = arena.addTypePack(valueTypes, iteratorTail);
|
2022-09-02 00:00:14 +01:00
|
|
|
|
|
|
|
// ... and then expand it out to 3 values (if possible)
|
2023-01-03 17:33:19 +00:00
|
|
|
TypePack iteratorTypes = extendTypePack(arena, builtinTypes, iteratorPack, 3);
|
2022-12-02 10:46:05 +00:00
|
|
|
if (iteratorTypes.head.empty())
|
2022-09-02 00:00:14 +01:00
|
|
|
{
|
|
|
|
reportError(GenericError{"for..in loops require at least one value to iterate over. Got zero"}, getLocation(forInStatement->values));
|
|
|
|
return;
|
|
|
|
}
|
2022-12-02 10:46:05 +00:00
|
|
|
TypeId iteratorTy = follow(iteratorTypes.head[0]);
|
2022-09-02 00:00:14 +01:00
|
|
|
|
2022-10-13 23:59:53 +01:00
|
|
|
auto checkFunction = [this, &arena, &scope, &forInStatement, &variableTypes](
|
2023-01-03 17:33:19 +00:00
|
|
|
const FunctionType* iterFtv, std::vector<TypeId> iterTys, bool isMm) {
|
2022-09-29 23:11:54 +01:00
|
|
|
if (iterTys.size() < 1 || iterTys.size() > 3)
|
|
|
|
{
|
|
|
|
if (isMm)
|
|
|
|
reportError(GenericError{"__iter metamethod must return (next[, table[, state]])"}, getLocation(forInStatement->values));
|
|
|
|
else
|
|
|
|
reportError(GenericError{"for..in loops must be passed (next[, table[, state]])"}, getLocation(forInStatement->values));
|
2022-09-02 00:00:14 +01:00
|
|
|
|
2022-09-29 23:11:54 +01:00
|
|
|
return;
|
|
|
|
}
|
2022-10-13 23:59:53 +01:00
|
|
|
|
2022-09-02 00:00:14 +01:00
|
|
|
// It is okay if there aren't enough iterators, but the iteratee must provide enough.
|
2023-01-03 17:33:19 +00:00
|
|
|
TypePack expectedVariableTypes = extendTypePack(arena, builtinTypes, iterFtv->retTypes, variableTypes.size());
|
2022-12-02 10:46:05 +00:00
|
|
|
if (expectedVariableTypes.head.size() < variableTypes.size())
|
2022-09-29 23:11:54 +01:00
|
|
|
{
|
|
|
|
if (isMm)
|
2022-10-13 23:59:53 +01:00
|
|
|
reportError(
|
|
|
|
GenericError{"__iter metamethod's next() function does not return enough values"}, getLocation(forInStatement->values));
|
2022-09-29 23:11:54 +01:00
|
|
|
else
|
|
|
|
reportError(GenericError{"next() does not return enough values"}, forInStatement->values.data[0]->location);
|
|
|
|
}
|
2022-09-02 00:00:14 +01:00
|
|
|
|
2022-12-02 10:46:05 +00:00
|
|
|
for (size_t i = 0; i < std::min(expectedVariableTypes.head.size(), variableTypes.size()); ++i)
|
|
|
|
reportErrors(tryUnify(scope, forInStatement->vars.data[i]->location, variableTypes[i], expectedVariableTypes.head[i]));
|
2022-10-13 23:59:53 +01:00
|
|
|
|
2022-09-02 00:00:14 +01:00
|
|
|
// nextFn is going to be invoked with (arrayTy, startIndexTy)
|
|
|
|
|
|
|
|
// It will be passed two arguments on every iteration save the
|
|
|
|
// first.
|
|
|
|
|
|
|
|
// It may be invoked with 0 or 1 argument on the first iteration.
|
|
|
|
// This depends on the types in iterateePack and therefore
|
|
|
|
// iteratorTypes.
|
|
|
|
|
|
|
|
// If iteratorTypes is too short to be a valid call to nextFn, we have to report a count mismatch error.
|
|
|
|
// If 2 is too short to be a valid call to nextFn, we have to report a count mismatch error.
|
|
|
|
// If 2 is too long to be a valid call to nextFn, we have to report a count mismatch error.
|
2022-09-29 23:11:54 +01:00
|
|
|
auto [minCount, maxCount] = getParameterExtents(TxnLog::empty(), iterFtv->argTypes, /*includeHiddenVariadics*/ true);
|
2022-09-02 00:00:14 +01:00
|
|
|
|
|
|
|
if (minCount > 2)
|
2022-09-15 23:13:58 +01:00
|
|
|
reportError(CountMismatch{2, std::nullopt, minCount, CountMismatch::Arg}, forInStatement->vars.data[0]->location);
|
2022-09-02 00:00:14 +01:00
|
|
|
if (maxCount && *maxCount < 2)
|
2022-09-15 23:13:58 +01:00
|
|
|
reportError(CountMismatch{2, std::nullopt, *maxCount, CountMismatch::Arg}, forInStatement->vars.data[0]->location);
|
2022-09-02 00:00:14 +01:00
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
TypePack flattenedArgTypes = extendTypePack(arena, builtinTypes, iterFtv->argTypes, 2);
|
2022-09-29 23:11:54 +01:00
|
|
|
size_t firstIterationArgCount = iterTys.empty() ? 0 : iterTys.size() - 1;
|
2022-12-02 10:46:05 +00:00
|
|
|
size_t actualArgCount = expectedVariableTypes.head.size();
|
2022-09-02 00:00:14 +01:00
|
|
|
|
|
|
|
if (firstIterationArgCount < minCount)
|
2022-09-15 23:13:58 +01:00
|
|
|
reportError(CountMismatch{2, std::nullopt, firstIterationArgCount, CountMismatch::Arg}, forInStatement->vars.data[0]->location);
|
2022-09-02 00:00:14 +01:00
|
|
|
else if (actualArgCount < minCount)
|
2022-09-15 23:13:58 +01:00
|
|
|
reportError(CountMismatch{2, std::nullopt, actualArgCount, CountMismatch::Arg}, forInStatement->vars.data[0]->location);
|
2022-09-02 00:00:14 +01:00
|
|
|
|
2022-12-02 10:46:05 +00:00
|
|
|
if (iterTys.size() >= 2 && flattenedArgTypes.head.size() > 0)
|
2022-09-02 00:00:14 +01:00
|
|
|
{
|
|
|
|
size_t valueIndex = forInStatement->values.size > 1 ? 1 : 0;
|
2022-12-02 10:46:05 +00:00
|
|
|
reportErrors(tryUnify(scope, forInStatement->values.data[valueIndex]->location, iterTys[1], flattenedArgTypes.head[0]));
|
2022-09-02 00:00:14 +01:00
|
|
|
}
|
|
|
|
|
2022-12-02 10:46:05 +00:00
|
|
|
if (iterTys.size() == 3 && flattenedArgTypes.head.size() > 1)
|
2022-09-02 00:00:14 +01:00
|
|
|
{
|
|
|
|
size_t valueIndex = forInStatement->values.size > 2 ? 2 : 0;
|
2022-12-02 10:46:05 +00:00
|
|
|
reportErrors(tryUnify(scope, forInStatement->values.data[valueIndex]->location, iterTys[2], flattenedArgTypes.head[1]));
|
2022-09-02 00:00:14 +01:00
|
|
|
}
|
2022-09-29 23:11:54 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the first iterator argument is a function
|
|
|
|
* * There must be 1 to 3 iterator arguments. Name them (nextTy,
|
|
|
|
* arrayTy, startIndexTy)
|
|
|
|
* * The return type of nextTy() must correspond to the variables'
|
|
|
|
* types and counts. HOWEVER the first iterator will never be nil.
|
|
|
|
* * The first return value of nextTy must be compatible with
|
|
|
|
* startIndexTy.
|
|
|
|
* * The first argument to nextTy() must be compatible with arrayTy if
|
|
|
|
* present. nil if not.
|
|
|
|
* * The second argument to nextTy() must be compatible with
|
|
|
|
* startIndexTy if it is present. Else, it must be compatible with
|
|
|
|
* nil.
|
|
|
|
* * nextTy() must be callable with only 2 arguments.
|
|
|
|
*/
|
2023-01-03 17:33:19 +00:00
|
|
|
if (const FunctionType* nextFn = get<FunctionType>(iteratorTy))
|
2022-09-29 23:11:54 +01:00
|
|
|
{
|
2022-12-02 10:46:05 +00:00
|
|
|
checkFunction(nextFn, iteratorTypes.head, false);
|
2022-09-02 00:00:14 +01:00
|
|
|
}
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (const TableType* ttv = get<TableType>(iteratorTy))
|
2022-09-02 00:00:14 +01:00
|
|
|
{
|
|
|
|
if ((forInStatement->vars.size == 1 || forInStatement->vars.size == 2) && ttv->indexer)
|
|
|
|
{
|
|
|
|
reportErrors(tryUnify(scope, forInStatement->vars.data[0]->location, variableTypes[0], ttv->indexer->indexType));
|
|
|
|
if (variableTypes.size() == 2)
|
|
|
|
reportErrors(tryUnify(scope, forInStatement->vars.data[1]->location, variableTypes[1], ttv->indexer->indexResultType));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
reportError(GenericError{"Cannot iterate over a table without indexer"}, forInStatement->values.data[0]->location);
|
|
|
|
}
|
2023-02-03 12:34:12 +00:00
|
|
|
else if (get<AnyType>(iteratorTy) || get<ErrorType>(iteratorTy) || get<NeverType>(iteratorTy))
|
2022-09-02 00:00:14 +01:00
|
|
|
{
|
|
|
|
// nothing
|
|
|
|
}
|
2023-03-24 17:34:14 +00:00
|
|
|
else if (isOptional(iteratorTy))
|
|
|
|
{
|
|
|
|
reportError(OptionalValueAccess{iteratorTy}, forInStatement->values.data[0]->location);
|
|
|
|
}
|
2022-10-13 23:59:53 +01:00
|
|
|
else if (std::optional<TypeId> iterMmTy =
|
2023-01-03 17:33:19 +00:00
|
|
|
findMetatableEntry(builtinTypes, module->errors, iteratorTy, "__iter", forInStatement->values.data[0]->location))
|
2022-09-29 23:11:54 +01:00
|
|
|
{
|
|
|
|
Instantiation instantiation{TxnLog::empty(), &arena, TypeLevel{}, scope};
|
|
|
|
|
|
|
|
if (std::optional<TypeId> instantiatedIterMmTy = instantiation.substitute(*iterMmTy))
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
if (const FunctionType* iterMmFtv = get<FunctionType>(*instantiatedIterMmTy))
|
2022-09-29 23:11:54 +01:00
|
|
|
{
|
|
|
|
TypePackId argPack = arena.addTypePack({iteratorTy});
|
|
|
|
reportErrors(tryUnify(scope, forInStatement->values.data[0]->location, argPack, iterMmFtv->argTypes));
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
TypePack mmIteratorTypes = extendTypePack(arena, builtinTypes, iterMmFtv->retTypes, 3);
|
2022-09-29 23:11:54 +01:00
|
|
|
|
2022-12-02 10:46:05 +00:00
|
|
|
if (mmIteratorTypes.head.size() == 0)
|
2022-09-29 23:11:54 +01:00
|
|
|
{
|
|
|
|
reportError(GenericError{"__iter must return at least one value"}, forInStatement->values.data[0]->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-02 10:46:05 +00:00
|
|
|
TypeId nextFn = follow(mmIteratorTypes.head[0]);
|
2022-09-29 23:11:54 +01:00
|
|
|
|
|
|
|
if (std::optional<TypeId> instantiatedNextFn = instantiation.substitute(nextFn))
|
|
|
|
{
|
2022-12-02 10:46:05 +00:00
|
|
|
std::vector<TypeId> instantiatedIteratorTypes = mmIteratorTypes.head;
|
2022-09-29 23:11:54 +01:00
|
|
|
instantiatedIteratorTypes[0] = *instantiatedNextFn;
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
if (const FunctionType* nextFtv = get<FunctionType>(*instantiatedNextFn))
|
2022-09-29 23:11:54 +01:00
|
|
|
{
|
|
|
|
checkFunction(nextFtv, instantiatedIteratorTypes, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(CannotCallNonFunction{*instantiatedNextFn}, forInStatement->values.data[0]->location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: This will not tell the user that this is because the
|
|
|
|
// metamethod isn't callable. This is not ideal, and we should
|
|
|
|
// improve this error message.
|
2022-10-13 23:59:53 +01:00
|
|
|
|
2022-09-29 23:11:54 +01:00
|
|
|
// TODO: This will also not handle intersections of functions or
|
|
|
|
// callable tables (which are supported by the runtime).
|
|
|
|
reportError(CannotCallNonFunction{*iterMmTy}, forInStatement->values.data[0]->location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(UnificationTooComplex{}, forInStatement->values.data[0]->location);
|
|
|
|
}
|
|
|
|
}
|
2022-09-02 00:00:14 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(CannotCallNonFunction{iteratorTy}, forInStatement->values.data[0]->location);
|
|
|
|
}
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstStatAssign* assign)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
|
|
|
size_t count = std::min(assign->vars.size, assign->values.size);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
AstExpr* lhs = assign->vars.data[i];
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(lhs, LValue);
|
2022-07-01 00:29:02 +01:00
|
|
|
TypeId lhsType = lookupType(lhs);
|
2022-06-17 01:54:42 +01:00
|
|
|
|
|
|
|
AstExpr* rhs = assign->values.data[i];
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(rhs, RValue);
|
2022-07-01 00:29:02 +01:00
|
|
|
TypeId rhsType = lookupType(rhs);
|
2022-06-17 01:54:42 +01:00
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
if (get<NeverType>(lhsType))
|
|
|
|
continue;
|
|
|
|
|
2022-11-10 22:04:44 +00:00
|
|
|
if (!isSubtype(rhsType, lhsType, stack.back()))
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2022-07-01 00:29:02 +01:00
|
|
|
reportError(TypeMismatch{lhsType, rhsType}, rhs->location);
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
}
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
2022-06-17 01:54:42 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstStatCompoundAssign* stat)
|
|
|
|
{
|
2023-01-06 16:07:19 +00:00
|
|
|
AstExprBinary fake{stat->location, stat->op, stat->var, stat->value};
|
|
|
|
TypeId resultTy = visit(&fake, stat);
|
|
|
|
TypeId varTy = lookupType(stat->var);
|
|
|
|
|
|
|
|
reportErrors(tryUnify(stack.back(), stat->location, resultTy, varTy));
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstStatFunction* stat)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(stat->name, LValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(stat->func);
|
|
|
|
}
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstStatLocalFunction* stat)
|
|
|
|
{
|
|
|
|
visit(stat->func);
|
|
|
|
}
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(const AstTypeList* typeList)
|
|
|
|
{
|
|
|
|
for (AstType* ty : typeList->types)
|
|
|
|
visit(ty);
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
if (typeList->tailType)
|
|
|
|
visit(typeList->tailType);
|
|
|
|
}
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstStatTypeAlias* stat)
|
|
|
|
{
|
2023-02-17 14:53:37 +00:00
|
|
|
visitGenerics(stat->generics, stat->genericPacks);
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(stat->type);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstTypeList types)
|
|
|
|
{
|
|
|
|
for (AstType* type : types.types)
|
|
|
|
visit(type);
|
|
|
|
if (types.tailType)
|
|
|
|
visit(types.tailType);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatDeclareFunction* stat)
|
|
|
|
{
|
2023-02-17 14:53:37 +00:00
|
|
|
visitGenerics(stat->generics, stat->genericPacks);
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(stat->params);
|
|
|
|
visit(stat->retTypes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatDeclareGlobal* stat)
|
|
|
|
{
|
|
|
|
visit(stat->type);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatDeclareClass* stat)
|
|
|
|
{
|
|
|
|
for (const AstDeclaredClassProp& prop : stat->props)
|
|
|
|
visit(prop.ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstStatError* stat)
|
|
|
|
{
|
|
|
|
for (AstExpr* expr : stat->expressions)
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
|
|
|
|
for (AstStat* s : stat->statements)
|
|
|
|
visit(s);
|
|
|
|
}
|
|
|
|
|
2023-01-20 12:02:39 +00:00
|
|
|
void visit(AstExpr* expr, ValueContext context)
|
2022-08-18 22:04:33 +01:00
|
|
|
{
|
|
|
|
auto StackPusher = pushStack(expr);
|
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
if (auto e = expr->as<AstExprGroup>())
|
2023-01-20 12:02:39 +00:00
|
|
|
return visit(e, context);
|
2022-08-18 22:04:33 +01:00
|
|
|
else if (auto e = expr->as<AstExprConstantNil>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprConstantBool>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprConstantNumber>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprConstantString>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprLocal>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprGlobal>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprVarargs>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprCall>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprIndexName>())
|
2023-01-20 12:02:39 +00:00
|
|
|
return visit(e, context);
|
2022-08-18 22:04:33 +01:00
|
|
|
else if (auto e = expr->as<AstExprIndexExpr>())
|
2023-01-20 12:02:39 +00:00
|
|
|
return visit(e, context);
|
2022-08-18 22:04:33 +01:00
|
|
|
else if (auto e = expr->as<AstExprFunction>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprTable>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprUnary>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprBinary>())
|
2023-01-06 16:07:19 +00:00
|
|
|
{
|
|
|
|
visit(e);
|
|
|
|
return;
|
|
|
|
}
|
2022-08-18 22:04:33 +01:00
|
|
|
else if (auto e = expr->as<AstExprTypeAssertion>())
|
|
|
|
return visit(e);
|
|
|
|
else if (auto e = expr->as<AstExprIfElse>())
|
|
|
|
return visit(e);
|
2023-01-27 21:28:45 +00:00
|
|
|
else if (auto e = expr->as<AstExprInterpString>())
|
|
|
|
return visit(e);
|
2022-08-18 22:04:33 +01:00
|
|
|
else if (auto e = expr->as<AstExprError>())
|
|
|
|
return visit(e);
|
|
|
|
else
|
|
|
|
LUAU_ASSERT(!"TypeChecker2 encountered an unknown expression type");
|
|
|
|
}
|
|
|
|
|
2023-01-20 12:02:39 +00:00
|
|
|
void visit(AstExprGroup* expr, ValueContext context)
|
2022-08-18 22:04:33 +01:00
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr->expr, context);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstExprConstantNil* expr)
|
|
|
|
{
|
2023-02-03 12:34:12 +00:00
|
|
|
NotNull<Scope> scope = stack.back();
|
|
|
|
TypeId actualType = lookupType(expr);
|
|
|
|
TypeId expectedType = builtinTypes->nilType;
|
|
|
|
LUAU_ASSERT(isSubtype(actualType, expectedType, scope));
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstExprConstantBool* expr)
|
|
|
|
{
|
2023-02-03 12:34:12 +00:00
|
|
|
NotNull<Scope> scope = stack.back();
|
|
|
|
TypeId actualType = lookupType(expr);
|
|
|
|
TypeId expectedType = builtinTypes->booleanType;
|
|
|
|
LUAU_ASSERT(isSubtype(actualType, expectedType, scope));
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
void visit(AstExprConstantNumber* expr)
|
2022-08-18 22:04:33 +01:00
|
|
|
{
|
2023-02-03 12:34:12 +00:00
|
|
|
NotNull<Scope> scope = stack.back();
|
|
|
|
TypeId actualType = lookupType(expr);
|
|
|
|
TypeId expectedType = builtinTypes->numberType;
|
|
|
|
LUAU_ASSERT(isSubtype(actualType, expectedType, scope));
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
void visit(AstExprConstantString* expr)
|
2022-08-18 22:04:33 +01:00
|
|
|
{
|
2023-02-03 12:34:12 +00:00
|
|
|
NotNull<Scope> scope = stack.back();
|
|
|
|
TypeId actualType = lookupType(expr);
|
|
|
|
TypeId expectedType = builtinTypes->stringType;
|
|
|
|
LUAU_ASSERT(isSubtype(actualType, expectedType, scope));
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstExprLocal* expr)
|
|
|
|
{
|
|
|
|
// TODO!
|
|
|
|
}
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstExprGlobal* expr)
|
|
|
|
{
|
|
|
|
// TODO!
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstExprVarargs* expr)
|
|
|
|
{
|
|
|
|
// TODO!
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
ErrorVec visitOverload(AstExprCall* call, NotNull<const FunctionType> overloadFunctionType, const std::vector<Location>& argLocs,
|
|
|
|
TypePackId expectedArgTypes, TypePackId expectedRetType)
|
|
|
|
{
|
|
|
|
ErrorVec overloadErrors =
|
|
|
|
tryUnify(stack.back(), call->location, overloadFunctionType->retTypes, expectedRetType, CountMismatch::FunctionResult);
|
|
|
|
|
|
|
|
size_t argIndex = 0;
|
|
|
|
auto inferredArgIt = begin(overloadFunctionType->argTypes);
|
|
|
|
auto expectedArgIt = begin(expectedArgTypes);
|
|
|
|
while (inferredArgIt != end(overloadFunctionType->argTypes) && expectedArgIt != end(expectedArgTypes))
|
|
|
|
{
|
|
|
|
Location argLoc = (argIndex >= argLocs.size()) ? argLocs.back() : argLocs[argIndex];
|
|
|
|
ErrorVec argErrors = tryUnify(stack.back(), argLoc, *expectedArgIt, *inferredArgIt);
|
|
|
|
for (TypeError e : argErrors)
|
|
|
|
overloadErrors.emplace_back(e);
|
|
|
|
|
|
|
|
++argIndex;
|
|
|
|
++inferredArgIt;
|
|
|
|
++expectedArgIt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// piggyback on the unifier for arity checking, but we can't do this for checking the actual arguments since the locations would be bad
|
|
|
|
ErrorVec argumentErrors = tryUnify(stack.back(), call->location, expectedArgTypes, overloadFunctionType->argTypes);
|
|
|
|
for (TypeError e : argumentErrors)
|
|
|
|
if (get<CountMismatch>(e) != nullptr)
|
|
|
|
overloadErrors.emplace_back(std::move(e));
|
|
|
|
|
|
|
|
return overloadErrors;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reportOverloadResolutionErrors(AstExprCall* call, std::vector<TypeId> overloads, TypePackId expectedArgTypes,
|
2023-03-10 19:20:04 +00:00
|
|
|
const std::vector<TypeId>& overloadsThatMatchArgCount, std::vector<std::pair<ErrorVec, TypeId>> overloadsErrors)
|
2023-02-24 18:24:22 +00:00
|
|
|
{
|
|
|
|
if (overloads.size() == 1)
|
|
|
|
{
|
|
|
|
reportErrors(std::get<0>(overloadsErrors.front()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<TypeId> overloadTypes = overloadsThatMatchArgCount;
|
|
|
|
if (overloadsThatMatchArgCount.size() == 0)
|
|
|
|
{
|
|
|
|
reportError(GenericError{"No overload for function accepts " + std::to_string(size(expectedArgTypes)) + " arguments."}, call->location);
|
|
|
|
// If no overloads match argument count, just list all overloads.
|
|
|
|
overloadTypes = overloads;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Report errors of the first argument-count-matching, but failing overload
|
|
|
|
TypeId overload = overloadsThatMatchArgCount[0];
|
|
|
|
|
|
|
|
// Remove the overload we are reporting errors about from the list of alternatives
|
|
|
|
overloadTypes.erase(std::remove(overloadTypes.begin(), overloadTypes.end(), overload), overloadTypes.end());
|
|
|
|
|
|
|
|
const FunctionType* ftv = get<FunctionType>(overload);
|
|
|
|
LUAU_ASSERT(ftv); // overload must be a function type here
|
|
|
|
|
2023-03-10 19:20:04 +00:00
|
|
|
auto error = std::find_if(overloadsErrors.begin(), overloadsErrors.end(), [overload](const std::pair<ErrorVec, TypeId>& e) {
|
|
|
|
return overload == e.second;
|
2023-02-24 18:24:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
LUAU_ASSERT(error != overloadsErrors.end());
|
|
|
|
reportErrors(std::get<0>(*error));
|
|
|
|
|
|
|
|
// If only one overload matched, we don't need this error because we provided the previous errors.
|
|
|
|
if (overloadsThatMatchArgCount.size() == 1)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string s;
|
|
|
|
for (size_t i = 0; i < overloadTypes.size(); ++i)
|
|
|
|
{
|
|
|
|
TypeId overload = follow(overloadTypes[i]);
|
|
|
|
|
|
|
|
if (i > 0)
|
|
|
|
s += "; ";
|
|
|
|
|
|
|
|
if (i > 0 && i == overloadTypes.size() - 1)
|
|
|
|
s += "and ";
|
|
|
|
|
|
|
|
s += toString(overload);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (overloadsThatMatchArgCount.size() == 0)
|
|
|
|
reportError(ExtraInformation{"Available overloads: " + s}, call->func->location);
|
|
|
|
else
|
|
|
|
reportError(ExtraInformation{"Other overloads are also not viable: " + s}, call->func->location);
|
|
|
|
}
|
|
|
|
|
2023-03-03 13:45:38 +00:00
|
|
|
// Note: this is intentionally separated from `visit(AstExprCall*)` for stack allocation purposes.
|
|
|
|
void visitCall(AstExprCall* call)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
TypeArena* arena = &testArena;
|
2022-12-02 10:46:05 +00:00
|
|
|
Instantiation instantiation{TxnLog::empty(), arena, TypeLevel{}, stack.back()};
|
2022-07-01 00:29:02 +01:00
|
|
|
|
2022-06-17 01:54:42 +01:00
|
|
|
TypePackId expectedRetType = lookupPack(call);
|
|
|
|
TypeId functionType = lookupType(call->func);
|
2022-10-21 18:33:43 +01:00
|
|
|
TypeId testFunctionType = functionType;
|
|
|
|
TypePack args;
|
2023-01-20 12:02:39 +00:00
|
|
|
std::vector<Location> argLocs;
|
|
|
|
argLocs.reserve(call->args.size + 1);
|
2022-06-17 01:54:42 +01:00
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
if (get<AnyType>(functionType) || get<ErrorType>(functionType) || get<NeverType>(functionType))
|
2022-09-02 00:00:14 +01:00
|
|
|
return;
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (std::optional<TypeId> callMm = findMetatableEntry(builtinTypes, module->errors, functionType, "__call", call->func->location))
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
if (get<FunctionType>(follow(*callMm)))
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
if (std::optional<TypeId> instantiatedCallMm = instantiation.substitute(*callMm))
|
|
|
|
{
|
|
|
|
args.head.push_back(functionType);
|
2023-01-20 12:02:39 +00:00
|
|
|
argLocs.push_back(call->func->location);
|
2022-10-21 18:33:43 +01:00
|
|
|
testFunctionType = follow(*instantiatedCallMm);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(UnificationTooComplex{}, call->func->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: This doesn't flag the __call metamethod as the problem
|
|
|
|
// very clearly.
|
|
|
|
reportError(CannotCallNonFunction{*callMm}, call->func->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (get<FunctionType>(functionType))
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
if (std::optional<TypeId> instantiatedFunctionType = instantiation.substitute(functionType))
|
|
|
|
{
|
|
|
|
testFunctionType = *instantiatedFunctionType;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(UnificationTooComplex{}, call->func->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-02-24 18:24:22 +00:00
|
|
|
else if (auto itv = get<IntersectionType>(functionType))
|
|
|
|
{
|
|
|
|
// We do nothing here because we'll flatten the intersection later, but we don't want to report it as a non-function.
|
|
|
|
}
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (auto utv = get<UnionType>(functionType))
|
2022-12-02 10:46:05 +00:00
|
|
|
{
|
|
|
|
// Sometimes it's okay to call a union of functions, but only if all of the functions are the same.
|
2023-03-24 17:34:14 +00:00
|
|
|
// Another scenario we might run into it is if the union has a nil member. In this case, we want to throw an error
|
|
|
|
if (isOptional(functionType))
|
|
|
|
{
|
|
|
|
reportError(OptionalValueAccess{functionType}, call->location);
|
|
|
|
return;
|
|
|
|
}
|
2022-12-02 10:46:05 +00:00
|
|
|
std::optional<TypeId> fst;
|
|
|
|
for (TypeId ty : utv)
|
|
|
|
{
|
|
|
|
if (!fst)
|
|
|
|
fst = follow(ty);
|
|
|
|
else if (fst != follow(ty))
|
|
|
|
{
|
|
|
|
reportError(CannotCallNonFunction{functionType}, call->func->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fst)
|
2023-01-03 17:33:19 +00:00
|
|
|
ice.ice("UnionType had no elements, so fst is nullopt?");
|
2022-12-02 10:46:05 +00:00
|
|
|
|
|
|
|
if (std::optional<TypeId> instantiatedFunctionType = instantiation.substitute(*fst))
|
|
|
|
{
|
|
|
|
testFunctionType = *instantiatedFunctionType;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(UnificationTooComplex{}, call->func->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2022-10-21 18:33:43 +01:00
|
|
|
else
|
2022-09-02 00:00:14 +01:00
|
|
|
{
|
|
|
|
reportError(CannotCallNonFunction{functionType}, call->func->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-18 18:45:14 +00:00
|
|
|
if (call->self)
|
|
|
|
{
|
|
|
|
AstExprIndexName* indexExpr = call->func->as<AstExprIndexName>();
|
|
|
|
if (!indexExpr)
|
|
|
|
ice.ice("method call expression has no 'self'");
|
|
|
|
|
2022-12-02 10:46:05 +00:00
|
|
|
args.head.push_back(lookupType(indexExpr->expr));
|
2023-01-20 12:02:39 +00:00
|
|
|
argLocs.push_back(indexExpr->expr->location);
|
2022-12-02 10:46:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < call->args.size; ++i)
|
|
|
|
{
|
|
|
|
AstExpr* arg = call->args.data[i];
|
2023-01-20 12:02:39 +00:00
|
|
|
argLocs.push_back(arg->location);
|
2022-12-02 10:46:05 +00:00
|
|
|
TypeId* argTy = module->astTypes.find(arg);
|
|
|
|
if (argTy)
|
|
|
|
args.head.push_back(*argTy);
|
|
|
|
else if (i == call->args.size - 1)
|
|
|
|
{
|
|
|
|
TypePackId* argTail = module->astTypePacks.find(arg);
|
|
|
|
if (argTail)
|
|
|
|
args.tail = *argTail;
|
|
|
|
else
|
2023-01-03 17:33:19 +00:00
|
|
|
args.tail = builtinTypes->anyTypePack;
|
2022-12-02 10:46:05 +00:00
|
|
|
}
|
|
|
|
else
|
2023-01-03 17:33:19 +00:00
|
|
|
args.head.push_back(builtinTypes->anyType);
|
2022-11-18 18:45:14 +00:00
|
|
|
}
|
|
|
|
|
2023-01-20 12:02:39 +00:00
|
|
|
TypePackId expectedArgTypes = arena->addTypePack(args);
|
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
std::vector<TypeId> overloads = flattenIntersection(testFunctionType);
|
2023-03-10 19:20:04 +00:00
|
|
|
std::vector<std::pair<ErrorVec, TypeId>> overloadsErrors;
|
2023-02-24 18:24:22 +00:00
|
|
|
overloadsErrors.reserve(overloads.size());
|
2022-09-02 00:00:14 +01:00
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
std::vector<TypeId> overloadsThatMatchArgCount;
|
|
|
|
|
|
|
|
for (TypeId overload : overloads)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2023-02-24 18:24:22 +00:00
|
|
|
overload = follow(overload);
|
2023-01-20 12:02:39 +00:00
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
const FunctionType* overloadFn = get<FunctionType>(overload);
|
|
|
|
if (!overloadFn)
|
|
|
|
{
|
|
|
|
reportError(CannotCallNonFunction{overload}, call->func->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We may have to instantiate the overload in order for it to typecheck.
|
|
|
|
if (std::optional<TypeId> instantiatedFunctionType = instantiation.substitute(overload))
|
|
|
|
{
|
|
|
|
overloadFn = get<FunctionType>(*instantiatedFunctionType);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-10 19:20:04 +00:00
|
|
|
overloadsErrors.emplace_back(std::vector{TypeError{call->func->location, UnificationTooComplex{}}}, overload);
|
2023-02-24 18:24:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-01-20 12:02:39 +00:00
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
ErrorVec overloadErrors = visitOverload(call, NotNull{overloadFn}, argLocs, expectedArgTypes, expectedRetType);
|
|
|
|
if (overloadErrors.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool argMismatch = false;
|
|
|
|
for (auto error : overloadErrors)
|
|
|
|
{
|
|
|
|
CountMismatch* cm = get<CountMismatch>(error);
|
|
|
|
if (!cm)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (cm->context == CountMismatch::Arg)
|
|
|
|
{
|
|
|
|
argMismatch = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-01-20 12:02:39 +00:00
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
if (!argMismatch)
|
|
|
|
overloadsThatMatchArgCount.push_back(overload);
|
|
|
|
|
2023-03-10 19:20:04 +00:00
|
|
|
overloadsErrors.emplace_back(std::move(overloadErrors), overload);
|
2023-02-24 18:24:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reportOverloadResolutionErrors(call, overloads, expectedArgTypes, overloadsThatMatchArgCount, overloadsErrors);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
2023-03-03 13:45:38 +00:00
|
|
|
void visit(AstExprCall* call)
|
|
|
|
{
|
|
|
|
visit(call->func, RValue);
|
|
|
|
|
|
|
|
for (AstExpr* arg : call->args)
|
|
|
|
visit(arg, RValue);
|
|
|
|
|
|
|
|
visitCall(call);
|
|
|
|
}
|
|
|
|
|
2023-03-10 19:20:04 +00:00
|
|
|
std::optional<TypeId> tryStripUnionFromNil(TypeId ty)
|
|
|
|
{
|
|
|
|
if (const UnionType* utv = get<UnionType>(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] : module->internalTypes.addType(UnionType{std::move(result)});
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeId stripFromNilAndReport(TypeId ty, const Location& location)
|
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
|
|
|
|
if (auto utv = get<UnionType>(ty))
|
|
|
|
{
|
|
|
|
if (!std::any_of(begin(utv), end(utv), isNil))
|
|
|
|
return ty;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::optional<TypeId> strippedUnion = tryStripUnionFromNil(ty))
|
|
|
|
{
|
|
|
|
reportError(OptionalValueAccess{ty}, location);
|
|
|
|
return follow(*strippedUnion);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ty;
|
|
|
|
}
|
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
void visitExprName(AstExpr* expr, Location location, const std::string& propName, ValueContext context)
|
2022-08-18 22:04:33 +01:00
|
|
|
{
|
2023-02-24 18:24:22 +00:00
|
|
|
visit(expr, RValue);
|
2022-12-09 18:07:25 +00:00
|
|
|
|
2023-03-10 19:20:04 +00:00
|
|
|
TypeId leftType = stripFromNilAndReport(lookupType(expr), location);
|
2023-04-14 13:05:27 +01:00
|
|
|
checkIndexTypeFromType(leftType, propName, location, context);
|
2023-02-24 18:24:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstExprIndexName* indexName, ValueContext context)
|
|
|
|
{
|
|
|
|
visitExprName(indexName->expr, indexName->location, indexName->index.value, context);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
2022-06-17 01:54:42 +01:00
|
|
|
|
2023-01-20 12:02:39 +00:00
|
|
|
void visit(AstExprIndexExpr* indexExpr, ValueContext context)
|
2022-08-18 22:04:33 +01:00
|
|
|
{
|
2023-02-24 18:24:22 +00:00
|
|
|
if (auto str = indexExpr->index->as<AstExprConstantString>())
|
|
|
|
{
|
|
|
|
const std::string stringValue(str->value.data, str->value.size);
|
|
|
|
visitExprName(indexExpr->expr, indexExpr->location, stringValue, context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
// TODO!
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(indexExpr->expr, LValue);
|
|
|
|
visit(indexExpr->index, RValue);
|
2023-02-24 18:24:22 +00:00
|
|
|
|
|
|
|
NotNull<Scope> scope = stack.back();
|
|
|
|
|
|
|
|
TypeId exprType = lookupType(indexExpr->expr);
|
|
|
|
TypeId indexType = lookupType(indexExpr->index);
|
|
|
|
|
|
|
|
if (auto tt = get<TableType>(exprType))
|
|
|
|
{
|
|
|
|
if (tt->indexer)
|
|
|
|
reportErrors(tryUnify(scope, indexExpr->index->location, indexType, tt->indexer->indexType));
|
|
|
|
else
|
|
|
|
reportError(CannotExtendTable{exprType, CannotExtendTable::Indexer, "indexer??"}, indexExpr->location);
|
|
|
|
}
|
2023-03-24 17:34:14 +00:00
|
|
|
else if (get<UnionType>(exprType) && isOptional(exprType))
|
|
|
|
reportError(OptionalValueAccess{exprType}, indexExpr->location);
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstExprFunction* fn)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2022-08-18 22:04:33 +01:00
|
|
|
auto StackPusher = pushStack(fn);
|
|
|
|
|
2023-02-17 14:53:37 +00:00
|
|
|
visitGenerics(fn->generics, fn->genericPacks);
|
|
|
|
|
2022-06-24 02:44:07 +01:00
|
|
|
TypeId inferredFnTy = lookupType(fn);
|
2023-01-03 17:33:19 +00:00
|
|
|
const FunctionType* inferredFtv = get<FunctionType>(inferredFnTy);
|
2022-06-24 02:44:07 +01:00
|
|
|
LUAU_ASSERT(inferredFtv);
|
|
|
|
|
2023-02-17 14:53:37 +00:00
|
|
|
// There is no way to write an annotation for the self argument, so we
|
|
|
|
// cannot do anything to check it.
|
2022-06-24 02:44:07 +01:00
|
|
|
auto argIt = begin(inferredFtv->argTypes);
|
2023-02-17 14:53:37 +00:00
|
|
|
if (fn->self)
|
|
|
|
++argIt;
|
|
|
|
|
2022-06-24 02:44:07 +01:00
|
|
|
for (const auto& arg : fn->args)
|
|
|
|
{
|
|
|
|
if (argIt == end(inferredFtv->argTypes))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (arg->annotation)
|
|
|
|
{
|
|
|
|
TypeId inferredArgTy = *argIt;
|
|
|
|
TypeId annotatedArgTy = lookupAnnotation(arg->annotation);
|
|
|
|
|
2023-03-03 13:45:38 +00:00
|
|
|
if (!isSubtype(inferredArgTy, annotatedArgTy, stack.back()))
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2023-03-03 13:45:38 +00:00
|
|
|
reportError(TypeMismatch{inferredArgTy, annotatedArgTy}, arg->location);
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
++argIt;
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(fn->body);
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstExprTable* expr)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2022-08-18 22:04:33 +01:00
|
|
|
// TODO!
|
|
|
|
for (const AstExprTable::Item& item : expr->items)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2022-08-18 22:04:33 +01:00
|
|
|
if (item.key)
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(item.key, LValue);
|
|
|
|
visit(item.value, RValue);
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstExprUnary* expr)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr->expr, RValue);
|
2022-10-27 23:22:49 +01:00
|
|
|
|
|
|
|
NotNull<Scope> scope = stack.back();
|
|
|
|
TypeId operandType = lookupType(expr->expr);
|
2023-02-17 14:53:37 +00:00
|
|
|
TypeId resultType = lookupType(expr);
|
2022-10-27 23:22:49 +01:00
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
if (get<AnyType>(operandType) || get<ErrorType>(operandType) || get<NeverType>(operandType))
|
2022-10-27 23:22:49 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (auto it = kUnaryOpMetamethods.find(expr->op); it != kUnaryOpMetamethods.end())
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
std::optional<TypeId> mm = findMetatableEntry(builtinTypes, module->errors, operandType, it->second, expr->location);
|
2022-10-27 23:22:49 +01:00
|
|
|
if (mm)
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
if (const FunctionType* ftv = get<FunctionType>(follow(*mm)))
|
2022-10-27 23:22:49 +01:00
|
|
|
{
|
|
|
|
if (std::optional<TypeId> ret = first(ftv->retTypes))
|
|
|
|
{
|
|
|
|
if (expr->op == AstExprUnary::Op::Len)
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
reportErrors(tryUnify(scope, expr->location, follow(*ret), builtinTypes->numberType));
|
2022-10-27 23:22:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(GenericError{format("Metamethod '%s' must return a value", it->second)}, expr->location);
|
|
|
|
}
|
2023-02-17 14:53:37 +00:00
|
|
|
|
|
|
|
std::optional<TypeId> firstArg = first(ftv->argTypes);
|
|
|
|
if (!firstArg)
|
|
|
|
{
|
|
|
|
reportError(GenericError{"__unm metamethod must accept one argument"}, expr->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypePackId expectedArgs = testArena.addTypePack({operandType});
|
|
|
|
TypePackId expectedRet = testArena.addTypePack({resultType});
|
|
|
|
|
|
|
|
TypeId expectedFunction = testArena.addType(FunctionType{expectedArgs, expectedRet});
|
|
|
|
|
|
|
|
ErrorVec errors = tryUnify(scope, expr->location, *mm, expectedFunction);
|
|
|
|
if (!errors.empty())
|
|
|
|
{
|
|
|
|
reportError(TypeMismatch{*firstArg, operandType}, expr->location);
|
|
|
|
return;
|
|
|
|
}
|
2022-10-27 23:22:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (expr->op == AstExprUnary::Op::Len)
|
|
|
|
{
|
|
|
|
DenseHashSet<TypeId> seen{nullptr};
|
|
|
|
int recursionCount = 0;
|
|
|
|
|
2023-03-24 17:34:14 +00:00
|
|
|
|
2022-10-27 23:22:49 +01:00
|
|
|
if (!hasLength(operandType, seen, &recursionCount))
|
|
|
|
{
|
2023-03-24 17:34:14 +00:00
|
|
|
if (isOptional(operandType))
|
|
|
|
reportError(OptionalValueAccess{operandType}, expr->location);
|
|
|
|
else
|
|
|
|
reportError(NotATable{operandType}, expr->location);
|
2022-10-27 23:22:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (expr->op == AstExprUnary::Op::Minus)
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
reportErrors(tryUnify(scope, expr->location, operandType, builtinTypes->numberType));
|
2022-10-27 23:22:49 +01:00
|
|
|
}
|
|
|
|
else if (expr->op == AstExprUnary::Op::Not)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(!"Unhandled unary operator");
|
|
|
|
}
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
TypeId visit(AstExprBinary* expr, AstNode* overrideKey = nullptr)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr->left, LValue);
|
|
|
|
visit(expr->right, LValue);
|
2022-10-21 18:33:43 +01:00
|
|
|
|
|
|
|
NotNull<Scope> scope = stack.back();
|
|
|
|
|
|
|
|
bool isEquality = expr->op == AstExprBinary::Op::CompareEq || expr->op == AstExprBinary::Op::CompareNe;
|
|
|
|
bool isComparison = expr->op >= AstExprBinary::Op::CompareEq && expr->op <= AstExprBinary::Op::CompareGe;
|
|
|
|
bool isLogical = expr->op == AstExprBinary::Op::And || expr->op == AstExprBinary::Op::Or;
|
|
|
|
|
|
|
|
TypeId leftType = lookupType(expr->left);
|
|
|
|
TypeId rightType = lookupType(expr->right);
|
|
|
|
|
|
|
|
if (expr->op == AstExprBinary::Op::Or)
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
leftType = stripNil(builtinTypes, testArena, leftType);
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isStringOperation = isString(leftType) && isString(rightType);
|
|
|
|
|
2023-01-06 16:07:19 +00:00
|
|
|
if (get<AnyType>(leftType) || get<ErrorType>(leftType))
|
|
|
|
return leftType;
|
|
|
|
else if (get<AnyType>(rightType) || get<ErrorType>(rightType))
|
|
|
|
return rightType;
|
2022-10-21 18:33:43 +01:00
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
if ((get<BlockedType>(leftType) || get<FreeType>(leftType)) && !isEquality && !isLogical)
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
auto name = getIdentifierOfBaseVar(expr->left);
|
|
|
|
reportError(CannotInferBinaryOperation{expr->op, name,
|
|
|
|
isComparison ? CannotInferBinaryOperation::OpKind::Comparison : CannotInferBinaryOperation::OpKind::Operation},
|
|
|
|
expr->location);
|
2023-01-06 16:07:19 +00:00
|
|
|
return leftType;
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (auto it = kBinaryOpMetamethods.find(expr->op); it != kBinaryOpMetamethods.end())
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
std::optional<TypeId> leftMt = getMetatable(leftType, builtinTypes);
|
|
|
|
std::optional<TypeId> rightMt = getMetatable(rightType, builtinTypes);
|
2022-10-21 18:33:43 +01:00
|
|
|
bool matches = leftMt == rightMt;
|
|
|
|
if (isEquality && !matches)
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
auto testUnion = [&matches, builtinTypes = this->builtinTypes](const UnionType* utv, std::optional<TypeId> otherMt) {
|
2022-10-21 18:33:43 +01:00
|
|
|
for (TypeId option : utv)
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
if (getMetatable(follow(option), builtinTypes) == otherMt)
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
matches = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
if (const UnionType* utv = get<UnionType>(leftType); utv && rightMt)
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
testUnion(utv, rightMt);
|
|
|
|
}
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
if (const UnionType* utv = get<UnionType>(rightType); utv && leftMt && !matches)
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
testUnion(utv, leftMt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!matches && isComparison)
|
|
|
|
{
|
|
|
|
reportError(GenericError{format("Types %s and %s cannot be compared with %s because they do not have the same metatable",
|
|
|
|
toString(leftType).c_str(), toString(rightType).c_str(), toString(expr->op).c_str())},
|
|
|
|
expr->location);
|
|
|
|
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->errorRecoveryType();
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<TypeId> mm;
|
2023-01-03 17:33:19 +00:00
|
|
|
if (std::optional<TypeId> leftMm = findMetatableEntry(builtinTypes, module->errors, leftType, it->second, expr->left->location))
|
2022-10-21 18:33:43 +01:00
|
|
|
mm = leftMm;
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (std::optional<TypeId> rightMm = findMetatableEntry(builtinTypes, module->errors, rightType, it->second, expr->right->location))
|
2022-12-09 18:07:25 +00:00
|
|
|
{
|
2022-10-21 18:33:43 +01:00
|
|
|
mm = rightMm;
|
2022-12-09 18:07:25 +00:00
|
|
|
std::swap(leftType, rightType);
|
|
|
|
}
|
2022-10-21 18:33:43 +01:00
|
|
|
|
|
|
|
if (mm)
|
|
|
|
{
|
2023-02-03 12:34:12 +00:00
|
|
|
AstNode* key = expr;
|
2023-01-06 16:07:19 +00:00
|
|
|
if (overrideKey != nullptr)
|
|
|
|
key = overrideKey;
|
|
|
|
|
|
|
|
TypeId instantiatedMm = module->astOverloadResolvedTypes[key];
|
2022-12-09 18:07:25 +00:00
|
|
|
if (!instantiatedMm)
|
|
|
|
reportError(CodeTooComplex{}, expr->location);
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (const FunctionType* ftv = get<FunctionType>(follow(instantiatedMm)))
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
TypePackId expectedArgs;
|
|
|
|
// For >= and > we invoke __lt and __le respectively with
|
|
|
|
// swapped argument ordering.
|
|
|
|
if (expr->op == AstExprBinary::Op::CompareGe || expr->op == AstExprBinary::Op::CompareGt)
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
expectedArgs = testArena.addTypePack({rightType, leftType});
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
expectedArgs = testArena.addTypePack({leftType, rightType});
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
|
2023-01-06 16:07:19 +00:00
|
|
|
TypePackId expectedRets;
|
2022-10-21 18:33:43 +01:00
|
|
|
if (expr->op == AstExprBinary::CompareEq || expr->op == AstExprBinary::CompareNe || expr->op == AstExprBinary::CompareGe ||
|
|
|
|
expr->op == AstExprBinary::CompareGt || expr->op == AstExprBinary::Op::CompareLe || expr->op == AstExprBinary::Op::CompareLt)
|
|
|
|
{
|
2023-01-06 16:07:19 +00:00
|
|
|
expectedRets = testArena.addTypePack({builtinTypes->booleanType});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
expectedRets = testArena.addTypePack({testArena.freshType(scope, TypeLevel{})});
|
|
|
|
}
|
|
|
|
|
|
|
|
TypeId expectedTy = testArena.addType(FunctionType(expectedArgs, expectedRets));
|
|
|
|
|
|
|
|
reportErrors(tryUnify(scope, expr->location, follow(*mm), expectedTy));
|
|
|
|
|
|
|
|
std::optional<TypeId> ret = first(ftv->retTypes);
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
if (isComparison)
|
|
|
|
{
|
|
|
|
if (!isBoolean(follow(*ret)))
|
|
|
|
{
|
|
|
|
reportError(GenericError{format("Metamethod '%s' must return a boolean", it->second)}, expr->location);
|
|
|
|
}
|
|
|
|
|
|
|
|
return builtinTypes->booleanType;
|
|
|
|
}
|
|
|
|
else
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
2023-01-06 16:07:19 +00:00
|
|
|
return follow(*ret);
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-06 16:07:19 +00:00
|
|
|
else
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
2023-01-06 16:07:19 +00:00
|
|
|
if (isComparison)
|
|
|
|
{
|
|
|
|
reportError(GenericError{format("Metamethod '%s' must return a boolean", it->second)}, expr->location);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(GenericError{format("Metamethod '%s' must return a value", it->second)}, expr->location);
|
|
|
|
}
|
|
|
|
|
|
|
|
return builtinTypes->errorRecoveryType();
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(CannotCallNonFunction{*mm}, expr->location);
|
|
|
|
}
|
|
|
|
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->errorRecoveryType();
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
// If this is a string comparison, or a concatenation of strings, we
|
|
|
|
// want to fall through to primitive behavior.
|
|
|
|
else if (!isEquality && !(isStringOperation && (expr->op == AstExprBinary::Op::Concat || isComparison)))
|
|
|
|
{
|
2023-01-06 16:07:19 +00:00
|
|
|
if ((leftMt && !isString(leftType)) || (rightMt && !isString(rightType)))
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
if (isComparison)
|
|
|
|
{
|
|
|
|
reportError(GenericError{format(
|
|
|
|
"Types '%s' and '%s' cannot be compared with %s because neither type's metatable has a '%s' metamethod",
|
|
|
|
toString(leftType).c_str(), toString(rightType).c_str(), toString(expr->op).c_str(), it->second)},
|
|
|
|
expr->location);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(GenericError{format(
|
|
|
|
"Operator %s is not applicable for '%s' and '%s' because neither type's metatable has a '%s' metamethod",
|
|
|
|
toString(expr->op).c_str(), toString(leftType).c_str(), toString(rightType).c_str(), it->second)},
|
|
|
|
expr->location);
|
|
|
|
}
|
|
|
|
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->errorRecoveryType();
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (!leftMt && !rightMt && (get<TableType>(leftType) || get<TableType>(rightType)))
|
2022-10-21 18:33:43 +01:00
|
|
|
{
|
|
|
|
if (isComparison)
|
|
|
|
{
|
|
|
|
reportError(GenericError{format("Types '%s' and '%s' cannot be compared with %s because neither type has a metatable",
|
|
|
|
toString(leftType).c_str(), toString(rightType).c_str(), toString(expr->op).c_str())},
|
|
|
|
expr->location);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(GenericError{format("Operator %s is not applicable for '%s' and '%s' because neither type has a metatable",
|
|
|
|
toString(expr->op).c_str(), toString(leftType).c_str(), toString(rightType).c_str())},
|
|
|
|
expr->location);
|
|
|
|
}
|
|
|
|
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->errorRecoveryType();
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (expr->op)
|
|
|
|
{
|
|
|
|
case AstExprBinary::Op::Add:
|
|
|
|
case AstExprBinary::Op::Sub:
|
|
|
|
case AstExprBinary::Op::Mul:
|
|
|
|
case AstExprBinary::Op::Div:
|
|
|
|
case AstExprBinary::Op::Pow:
|
|
|
|
case AstExprBinary::Op::Mod:
|
2023-01-03 17:33:19 +00:00
|
|
|
reportErrors(tryUnify(scope, expr->left->location, leftType, builtinTypes->numberType));
|
|
|
|
reportErrors(tryUnify(scope, expr->right->location, rightType, builtinTypes->numberType));
|
2022-10-21 18:33:43 +01:00
|
|
|
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->numberType;
|
2022-10-21 18:33:43 +01:00
|
|
|
case AstExprBinary::Op::Concat:
|
2023-01-03 17:33:19 +00:00
|
|
|
reportErrors(tryUnify(scope, expr->left->location, leftType, builtinTypes->stringType));
|
|
|
|
reportErrors(tryUnify(scope, expr->right->location, rightType, builtinTypes->stringType));
|
2022-10-21 18:33:43 +01:00
|
|
|
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->stringType;
|
2022-10-21 18:33:43 +01:00
|
|
|
case AstExprBinary::Op::CompareGe:
|
|
|
|
case AstExprBinary::Op::CompareGt:
|
|
|
|
case AstExprBinary::Op::CompareLe:
|
|
|
|
case AstExprBinary::Op::CompareLt:
|
|
|
|
if (isNumber(leftType))
|
2023-01-06 16:07:19 +00:00
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
reportErrors(tryUnify(scope, expr->right->location, rightType, builtinTypes->numberType));
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->numberType;
|
|
|
|
}
|
2022-10-21 18:33:43 +01:00
|
|
|
else if (isString(leftType))
|
2023-01-06 16:07:19 +00:00
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
reportErrors(tryUnify(scope, expr->right->location, rightType, builtinTypes->stringType));
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->stringType;
|
|
|
|
}
|
2022-10-21 18:33:43 +01:00
|
|
|
else
|
2023-01-06 16:07:19 +00:00
|
|
|
{
|
2022-10-21 18:33:43 +01:00
|
|
|
reportError(GenericError{format("Types '%s' and '%s' cannot be compared with relational operator %s", toString(leftType).c_str(),
|
|
|
|
toString(rightType).c_str(), toString(expr->op).c_str())},
|
|
|
|
expr->location);
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->errorRecoveryType();
|
|
|
|
}
|
2022-10-21 18:33:43 +01:00
|
|
|
case AstExprBinary::Op::And:
|
|
|
|
case AstExprBinary::Op::Or:
|
|
|
|
case AstExprBinary::Op::CompareEq:
|
|
|
|
case AstExprBinary::Op::CompareNe:
|
2023-01-06 16:07:19 +00:00
|
|
|
// Ugly case: we don't care about this possibility, because a
|
|
|
|
// compound assignment will never exist with one of these operators.
|
|
|
|
return builtinTypes->anyType;
|
2022-10-21 18:33:43 +01:00
|
|
|
default:
|
|
|
|
// Unhandled AstExprBinary::Op possibility.
|
|
|
|
LUAU_ASSERT(false);
|
2023-01-06 16:07:19 +00:00
|
|
|
return builtinTypes->errorRecoveryType();
|
2022-10-21 18:33:43 +01:00
|
|
|
}
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstExprTypeAssertion* expr)
|
2022-08-11 21:42:54 +01:00
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr->expr, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(expr->annotation);
|
|
|
|
|
2022-08-11 21:42:54 +01:00
|
|
|
TypeId annotationType = lookupAnnotation(expr->annotation);
|
|
|
|
TypeId computedType = lookupType(expr->expr);
|
|
|
|
|
|
|
|
// Note: As an optimization, we try 'number <: number | string' first, as that is the more likely case.
|
2022-11-10 22:04:44 +00:00
|
|
|
if (isSubtype(annotationType, computedType, stack.back()))
|
2022-08-18 22:04:33 +01:00
|
|
|
return;
|
2022-08-11 21:42:54 +01:00
|
|
|
|
2022-11-10 22:04:44 +00:00
|
|
|
if (isSubtype(computedType, annotationType, stack.back()))
|
2022-08-18 22:04:33 +01:00
|
|
|
return;
|
2022-08-11 21:42:54 +01:00
|
|
|
|
|
|
|
reportError(TypesAreUnrelated{computedType, annotationType}, expr->location);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstExprIfElse* expr)
|
|
|
|
{
|
|
|
|
// TODO!
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(expr->condition, RValue);
|
|
|
|
visit(expr->trueExpr, RValue);
|
|
|
|
visit(expr->falseExpr, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
2023-01-27 21:28:45 +00:00
|
|
|
void visit(AstExprInterpString* interpString)
|
|
|
|
{
|
|
|
|
for (AstExpr* expr : interpString->expressions)
|
|
|
|
visit(expr, RValue);
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstExprError* expr)
|
|
|
|
{
|
|
|
|
// TODO!
|
|
|
|
for (AstExpr* e : expr->expressions)
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(e, RValue);
|
2022-08-11 21:42:54 +01:00
|
|
|
}
|
|
|
|
|
2022-07-01 00:29:02 +01:00
|
|
|
/** Extract a TypeId for the first type of the provided pack.
|
|
|
|
*
|
|
|
|
* Note that this may require modifying some types. I hope this doesn't cause problems!
|
|
|
|
*/
|
|
|
|
TypeId flattenPack(TypePackId pack)
|
|
|
|
{
|
|
|
|
pack = follow(pack);
|
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
if (auto fst = first(pack, /*ignoreHiddenVariadics*/ false))
|
|
|
|
return *fst;
|
2022-07-01 00:29:02 +01:00
|
|
|
else if (auto ftp = get<FreeTypePack>(pack))
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
TypeId result = testArena.addType(FreeType{ftp->scope});
|
|
|
|
TypePackId freeTail = testArena.addTypePack(FreeTypePack{ftp->scope});
|
2022-07-01 00:29:02 +01:00
|
|
|
|
|
|
|
TypePack& resultPack = asMutable(pack)->ty.emplace<TypePack>();
|
|
|
|
resultPack.head.assign(1, result);
|
|
|
|
resultPack.tail = freeTail;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else if (get<Unifiable::Error>(pack))
|
2023-01-03 17:33:19 +00:00
|
|
|
return builtinTypes->errorRecoveryType();
|
2023-02-03 12:34:12 +00:00
|
|
|
else if (finite(pack) && size(pack) == 0)
|
|
|
|
return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil`
|
2022-07-01 00:29:02 +01:00
|
|
|
else
|
|
|
|
ice.ice("flattenPack got a weird pack!");
|
|
|
|
}
|
|
|
|
|
2023-02-17 14:53:37 +00:00
|
|
|
void visitGenerics(AstArray<AstGenericType> generics, AstArray<AstGenericTypePack> genericPacks)
|
|
|
|
{
|
|
|
|
DenseHashSet<AstName> seen{AstName{}};
|
|
|
|
|
|
|
|
for (const auto& g : generics)
|
|
|
|
{
|
|
|
|
if (seen.contains(g.name))
|
|
|
|
reportError(DuplicateGenericParameter{g.name.value}, g.location);
|
|
|
|
else
|
|
|
|
seen.insert(g.name);
|
|
|
|
|
|
|
|
if (g.defaultValue)
|
|
|
|
visit(g.defaultValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& g : genericPacks)
|
|
|
|
{
|
|
|
|
if (seen.contains(g.name))
|
|
|
|
reportError(DuplicateGenericParameter{g.name.value}, g.location);
|
|
|
|
else
|
|
|
|
seen.insert(g.name);
|
|
|
|
|
|
|
|
if (g.defaultValue)
|
|
|
|
visit(g.defaultValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstType* ty)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2022-08-18 22:04:33 +01:00
|
|
|
if (auto t = ty->as<AstTypeReference>())
|
|
|
|
return visit(t);
|
|
|
|
else if (auto t = ty->as<AstTypeTable>())
|
|
|
|
return visit(t);
|
|
|
|
else if (auto t = ty->as<AstTypeFunction>())
|
|
|
|
return visit(t);
|
|
|
|
else if (auto t = ty->as<AstTypeTypeof>())
|
|
|
|
return visit(t);
|
|
|
|
else if (auto t = ty->as<AstTypeUnion>())
|
|
|
|
return visit(t);
|
|
|
|
else if (auto t = ty->as<AstTypeIntersection>())
|
|
|
|
return visit(t);
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstTypeReference* ty)
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
// No further validation is necessary in this case. The main logic for
|
|
|
|
// _luau_print is contained in lookupAnnotation.
|
|
|
|
if (FFlag::DebugLuauMagicTypes && ty->name == "_luau_print" && ty->parameters.size > 0)
|
|
|
|
return;
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
for (const AstTypeOrPack& param : ty->parameters)
|
|
|
|
{
|
|
|
|
if (param.type)
|
|
|
|
visit(param.type);
|
|
|
|
else
|
|
|
|
visit(param.typePack);
|
|
|
|
}
|
|
|
|
|
2022-07-29 04:41:13 +01:00
|
|
|
Scope* scope = findInnermostScope(ty->location);
|
2022-08-04 22:27:28 +01:00
|
|
|
LUAU_ASSERT(scope);
|
2022-06-24 02:44:07 +01:00
|
|
|
|
2022-10-27 23:22:49 +01:00
|
|
|
std::optional<TypeFun> alias =
|
|
|
|
(ty->prefix) ? scope->lookupImportedType(ty->prefix->value, ty->name.value) : scope->lookupType(ty->name.value);
|
2022-08-04 22:27:28 +01:00
|
|
|
|
|
|
|
if (alias.has_value())
|
|
|
|
{
|
|
|
|
size_t typesRequired = alias->typeParams.size();
|
|
|
|
size_t packsRequired = alias->typePackParams.size();
|
|
|
|
|
|
|
|
bool hasDefaultTypes = std::any_of(alias->typeParams.begin(), alias->typeParams.end(), [](auto&& el) {
|
|
|
|
return el.defaultValue.has_value();
|
|
|
|
});
|
|
|
|
|
|
|
|
bool hasDefaultPacks = std::any_of(alias->typePackParams.begin(), alias->typePackParams.end(), [](auto&& el) {
|
|
|
|
return el.defaultValue.has_value();
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!ty->hasParameterList)
|
|
|
|
{
|
|
|
|
if ((!alias->typeParams.empty() && !hasDefaultTypes) || (!alias->typePackParams.empty() && !hasDefaultPacks))
|
|
|
|
{
|
|
|
|
reportError(GenericError{"Type parameter list is required"}, ty->location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t typesProvided = 0;
|
|
|
|
size_t extraTypes = 0;
|
|
|
|
size_t packsProvided = 0;
|
|
|
|
|
|
|
|
for (const AstTypeOrPack& p : ty->parameters)
|
|
|
|
{
|
|
|
|
if (p.type)
|
|
|
|
{
|
|
|
|
if (packsProvided != 0)
|
|
|
|
{
|
|
|
|
reportError(GenericError{"Type parameters must come before type pack parameters"}, ty->location);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typesProvided < typesRequired)
|
|
|
|
{
|
|
|
|
typesProvided += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
extraTypes += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (p.typePack)
|
|
|
|
{
|
|
|
|
TypePackId tp = lookupPackAnnotation(p.typePack);
|
|
|
|
|
|
|
|
if (typesProvided < typesRequired && size(tp) == 1 && finite(tp) && first(tp))
|
|
|
|
{
|
|
|
|
typesProvided += 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
packsProvided += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (extraTypes != 0 && packsProvided == 0)
|
|
|
|
{
|
|
|
|
packsProvided += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = typesProvided; i < typesRequired; ++i)
|
|
|
|
{
|
|
|
|
if (alias->typeParams[i].defaultValue)
|
|
|
|
{
|
|
|
|
typesProvided += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-03 13:45:38 +00:00
|
|
|
for (size_t i = packsProvided; i < packsRequired; ++i)
|
2022-08-04 22:27:28 +01:00
|
|
|
{
|
|
|
|
if (alias->typePackParams[i].defaultValue)
|
|
|
|
{
|
|
|
|
packsProvided += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (extraTypes == 0 && packsProvided + 1 == packsRequired)
|
|
|
|
{
|
|
|
|
packsProvided += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typesProvided != typesRequired || packsProvided != packsRequired)
|
|
|
|
{
|
|
|
|
reportError(IncorrectGenericParameterCount{
|
|
|
|
/* name */ ty->name.value,
|
|
|
|
/* typeFun */ *alias,
|
|
|
|
/* actualParameters */ typesProvided,
|
|
|
|
/* actualPackParameters */ packsProvided,
|
|
|
|
},
|
|
|
|
ty->location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-08-11 21:42:54 +01:00
|
|
|
if (scope->lookupPack(ty->name.value))
|
2022-08-04 22:27:28 +01:00
|
|
|
{
|
|
|
|
reportError(
|
|
|
|
SwappedGenericTypeParameter{
|
|
|
|
ty->name.value,
|
|
|
|
SwappedGenericTypeParameter::Kind::Type,
|
|
|
|
},
|
|
|
|
ty->location);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-10 19:20:04 +00:00
|
|
|
std::string symbol = "";
|
|
|
|
if (ty->prefix)
|
|
|
|
{
|
|
|
|
symbol += (*(ty->prefix)).value;
|
|
|
|
symbol += ".";
|
|
|
|
}
|
|
|
|
symbol += ty->name.value;
|
|
|
|
|
|
|
|
reportError(UnknownSymbol{symbol, UnknownSymbol::Context::Type}, ty->location);
|
2022-08-04 22:27:28 +01:00
|
|
|
}
|
|
|
|
}
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstTypeTable* table)
|
|
|
|
{
|
|
|
|
// TODO!
|
|
|
|
|
|
|
|
for (const AstTableProp& prop : table->props)
|
|
|
|
visit(prop.type);
|
|
|
|
|
|
|
|
if (table->indexer)
|
|
|
|
{
|
|
|
|
visit(table->indexer->indexType);
|
|
|
|
visit(table->indexer->resultType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstTypeFunction* ty)
|
|
|
|
{
|
2023-02-17 14:53:37 +00:00
|
|
|
visitGenerics(ty->generics, ty->genericPacks);
|
2022-08-18 22:04:33 +01:00
|
|
|
visit(ty->argTypes);
|
|
|
|
visit(ty->returnTypes);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstTypeTypeof* ty)
|
|
|
|
{
|
2023-01-20 12:02:39 +00:00
|
|
|
visit(ty->expr, RValue);
|
2022-08-18 22:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstTypeUnion* ty)
|
|
|
|
{
|
|
|
|
// TODO!
|
|
|
|
for (AstType* type : ty->types)
|
|
|
|
visit(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstTypeIntersection* ty)
|
|
|
|
{
|
|
|
|
// TODO!
|
|
|
|
for (AstType* type : ty->types)
|
|
|
|
visit(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstTypePack* pack)
|
|
|
|
{
|
|
|
|
if (auto p = pack->as<AstTypePackExplicit>())
|
|
|
|
return visit(p);
|
|
|
|
else if (auto p = pack->as<AstTypePackVariadic>())
|
|
|
|
return visit(p);
|
|
|
|
else if (auto p = pack->as<AstTypePackGeneric>())
|
|
|
|
return visit(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void visit(AstTypePackExplicit* tp)
|
|
|
|
{
|
|
|
|
// TODO!
|
|
|
|
for (AstType* type : tp->typeList.types)
|
|
|
|
visit(type);
|
2022-08-04 22:27:28 +01:00
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
if (tp->typeList.tailType)
|
|
|
|
visit(tp->typeList.tailType);
|
2022-08-04 22:27:28 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstTypePackVariadic* tp)
|
2022-08-04 22:27:28 +01:00
|
|
|
{
|
2022-08-18 22:04:33 +01:00
|
|
|
// TODO!
|
|
|
|
visit(tp->variadicType);
|
2022-08-04 22:27:28 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:04:33 +01:00
|
|
|
void visit(AstTypePackGeneric* tp)
|
2022-08-04 22:27:28 +01:00
|
|
|
{
|
|
|
|
Scope* scope = findInnermostScope(tp->location);
|
|
|
|
LUAU_ASSERT(scope);
|
|
|
|
|
2022-08-11 21:42:54 +01:00
|
|
|
std::optional<TypePackId> alias = scope->lookupPack(tp->genericName.value);
|
2022-08-04 22:27:28 +01:00
|
|
|
if (!alias.has_value())
|
2022-06-24 02:44:07 +01:00
|
|
|
{
|
2022-08-11 21:42:54 +01:00
|
|
|
if (scope->lookupType(tp->genericName.value))
|
2022-08-04 22:27:28 +01:00
|
|
|
{
|
|
|
|
reportError(
|
|
|
|
SwappedGenericTypeParameter{
|
|
|
|
tp->genericName.value,
|
|
|
|
SwappedGenericTypeParameter::Kind::Pack,
|
|
|
|
},
|
|
|
|
tp->location);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
|
|
|
|
}
|
2022-06-24 02:44:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-03 12:34:12 +00:00
|
|
|
void reduceTypes()
|
|
|
|
{
|
|
|
|
if (FFlag::DebugLuauDontReduceTypes)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (auto [_, scope] : module->scopes)
|
|
|
|
{
|
|
|
|
for (auto& [_, b] : scope->bindings)
|
|
|
|
{
|
|
|
|
if (auto reduced = module->reduction->reduce(b.typeId))
|
|
|
|
b.typeId = *reduced;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto reduced = module->reduction->reduce(scope->returnType))
|
|
|
|
scope->returnType = *reduced;
|
|
|
|
|
|
|
|
if (scope->varargPack)
|
|
|
|
{
|
|
|
|
if (auto reduced = module->reduction->reduce(*scope->varargPack))
|
|
|
|
scope->varargPack = *reduced;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto reduceMap = [this](auto& map) {
|
|
|
|
for (auto& [_, tf] : map)
|
|
|
|
{
|
|
|
|
if (auto reduced = module->reduction->reduce(tf))
|
|
|
|
tf = *reduced;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
reduceMap(scope->exportedTypeBindings);
|
|
|
|
reduceMap(scope->privateTypeBindings);
|
|
|
|
reduceMap(scope->privateTypePackBindings);
|
|
|
|
for (auto& [_, space] : scope->importedTypeBindings)
|
|
|
|
reduceMap(space);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto reduceOrError = [this](auto& map) {
|
|
|
|
for (auto [ast, t] : map)
|
|
|
|
{
|
|
|
|
if (!t)
|
|
|
|
continue; // Reminder: this implies that the recursion limit was exceeded.
|
|
|
|
else if (auto reduced = module->reduction->reduce(t))
|
|
|
|
map[ast] = *reduced;
|
|
|
|
else
|
|
|
|
reportError(NormalizationTooComplex{}, ast->location);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
module->astOriginalResolvedTypes = module->astResolvedTypes;
|
|
|
|
|
|
|
|
// Both [`Module::returnType`] and [`Module::exportedTypeBindings`] are empty here, and
|
|
|
|
// is populated by [`Module::clonePublicInterface`] in the future, so by that point these
|
|
|
|
// two aforementioned fields will only contain types that are irreducible.
|
|
|
|
reduceOrError(module->astTypes);
|
|
|
|
reduceOrError(module->astTypePacks);
|
|
|
|
reduceOrError(module->astExpectedTypes);
|
|
|
|
reduceOrError(module->astOriginalCallTypes);
|
|
|
|
reduceOrError(module->astOverloadResolvedTypes);
|
|
|
|
reduceOrError(module->astResolvedTypes);
|
|
|
|
reduceOrError(module->astResolvedTypePacks);
|
|
|
|
}
|
|
|
|
|
2022-11-10 22:04:44 +00:00
|
|
|
template<typename TID>
|
|
|
|
bool isSubtype(TID subTy, TID superTy, NotNull<Scope> scope)
|
|
|
|
{
|
|
|
|
TypeArena arena;
|
|
|
|
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, Location{}, Covariant};
|
|
|
|
u.useScopes = true;
|
|
|
|
|
|
|
|
u.tryUnify(subTy, superTy);
|
|
|
|
const bool ok = u.errors.empty() && u.log.empty();
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2022-09-02 00:00:14 +01:00
|
|
|
template<typename TID>
|
2023-01-20 12:02:39 +00:00
|
|
|
ErrorVec tryUnify(NotNull<Scope> scope, const Location& location, TID subTy, TID superTy, CountMismatch::Context context = CountMismatch::Arg)
|
2022-09-02 00:00:14 +01:00
|
|
|
{
|
2022-10-07 00:55:58 +01:00
|
|
|
Unifier u{NotNull{&normalizer}, Mode::Strict, scope, location, Covariant};
|
2023-01-20 12:02:39 +00:00
|
|
|
u.ctx = context;
|
2022-11-10 22:04:44 +00:00
|
|
|
u.useScopes = true;
|
2022-09-02 00:00:14 +01:00
|
|
|
u.tryUnify(subTy, superTy);
|
|
|
|
|
|
|
|
return std::move(u.errors);
|
|
|
|
}
|
|
|
|
|
|
|
|
void reportError(TypeErrorData data, const Location& location)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
|
|
|
module->errors.emplace_back(location, sourceModule->name, std::move(data));
|
2022-09-08 22:44:50 +01:00
|
|
|
|
2023-03-03 13:45:38 +00:00
|
|
|
if (logger)
|
2022-09-08 22:44:50 +01:00
|
|
|
logger->captureTypeCheckError(module->errors.back());
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
2022-07-01 00:29:02 +01:00
|
|
|
|
|
|
|
void reportError(TypeError e)
|
|
|
|
{
|
2022-09-02 00:00:14 +01:00
|
|
|
reportError(std::move(e.data), e.location);
|
|
|
|
}
|
|
|
|
|
|
|
|
void reportErrors(ErrorVec errors)
|
|
|
|
{
|
|
|
|
for (TypeError e : errors)
|
|
|
|
reportError(std::move(e));
|
2022-07-01 00:29:02 +01:00
|
|
|
}
|
2022-08-11 21:42:54 +01:00
|
|
|
|
2023-04-14 13:05:27 +01:00
|
|
|
// If the provided type does not have the named property, report an error.
|
|
|
|
void checkIndexTypeFromType(TypeId tableTy, const std::string& prop, const Location& location, ValueContext context)
|
2022-08-11 21:42:54 +01:00
|
|
|
{
|
2023-04-14 13:05:27 +01:00
|
|
|
const NormalizedType* norm = normalizer.normalize(tableTy);
|
|
|
|
if (!norm)
|
|
|
|
{
|
|
|
|
reportError(NormalizationTooComplex{}, location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-09 18:07:25 +00:00
|
|
|
bool foundOneProp = false;
|
|
|
|
std::vector<TypeId> typesMissingTheProp;
|
|
|
|
|
|
|
|
auto fetch = [&](TypeId ty) {
|
|
|
|
if (!normalizer.isInhabited(ty))
|
|
|
|
return;
|
|
|
|
|
2023-04-14 13:05:27 +01:00
|
|
|
std::unordered_set<TypeId> seen;
|
|
|
|
bool found = hasIndexTypeFromType(ty, prop, location, seen);
|
2022-12-09 18:07:25 +00:00
|
|
|
foundOneProp |= found;
|
|
|
|
if (!found)
|
|
|
|
typesMissingTheProp.push_back(ty);
|
|
|
|
};
|
|
|
|
|
2023-04-14 13:05:27 +01:00
|
|
|
fetch(norm->tops);
|
|
|
|
fetch(norm->booleans);
|
2023-01-03 17:33:19 +00:00
|
|
|
|
|
|
|
if (FFlag::LuauNegatedClassTypes)
|
|
|
|
{
|
2023-04-14 13:05:27 +01:00
|
|
|
for (const auto& [ty, _negations] : norm->classes.classes)
|
2023-01-03 17:33:19 +00:00
|
|
|
{
|
|
|
|
fetch(ty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-14 13:05:27 +01:00
|
|
|
for (TypeId ty : norm->DEPRECATED_classes)
|
2023-01-03 17:33:19 +00:00
|
|
|
fetch(ty);
|
|
|
|
}
|
2023-04-14 13:05:27 +01:00
|
|
|
fetch(norm->errors);
|
|
|
|
fetch(norm->nils);
|
|
|
|
fetch(norm->numbers);
|
|
|
|
if (!norm->strings.isNever())
|
2023-01-03 17:33:19 +00:00
|
|
|
fetch(builtinTypes->stringType);
|
2023-04-14 13:05:27 +01:00
|
|
|
fetch(norm->threads);
|
|
|
|
for (TypeId ty : norm->tables)
|
2022-12-09 18:07:25 +00:00
|
|
|
fetch(ty);
|
2023-04-14 13:05:27 +01:00
|
|
|
if (norm->functions.isTop)
|
2023-01-03 17:33:19 +00:00
|
|
|
fetch(builtinTypes->functionType);
|
2023-04-14 13:05:27 +01:00
|
|
|
else if (!norm->functions.isNever())
|
2022-12-09 18:07:25 +00:00
|
|
|
{
|
2023-04-14 13:05:27 +01:00
|
|
|
if (norm->functions.parts.size() == 1)
|
|
|
|
fetch(norm->functions.parts.front());
|
2022-12-09 18:07:25 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<TypeId> parts;
|
2023-04-14 13:05:27 +01:00
|
|
|
parts.insert(parts.end(), norm->functions.parts.begin(), norm->functions.parts.end());
|
2023-01-03 17:33:19 +00:00
|
|
|
fetch(testArena.addType(IntersectionType{std::move(parts)}));
|
2022-12-09 18:07:25 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-14 13:05:27 +01:00
|
|
|
for (const auto& [tyvar, intersect] : norm->tyvars)
|
2022-12-09 18:07:25 +00:00
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
if (get<NeverType>(intersect->tops))
|
2022-12-09 18:07:25 +00:00
|
|
|
{
|
|
|
|
TypeId ty = normalizer.typeFromNormal(*intersect);
|
2023-01-03 17:33:19 +00:00
|
|
|
fetch(testArena.addType(IntersectionType{{tyvar, ty}}));
|
2022-12-09 18:07:25 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
fetch(tyvar);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!typesMissingTheProp.empty())
|
|
|
|
{
|
|
|
|
if (foundOneProp)
|
2023-01-20 12:02:39 +00:00
|
|
|
reportError(MissingUnionProperty{tableTy, typesMissingTheProp, prop}, location);
|
2023-03-10 19:20:04 +00:00
|
|
|
// For class LValues, we don't want to report an extension error,
|
|
|
|
// because classes come into being with full knowledge of their
|
|
|
|
// shape. We instead want to report the unknown property error of
|
|
|
|
// the `else` branch.
|
|
|
|
else if (context == LValue && !get<ClassType>(tableTy))
|
2023-01-20 12:02:39 +00:00
|
|
|
reportError(CannotExtendTable{tableTy, CannotExtendTable::Property, prop}, location);
|
2022-12-09 18:07:25 +00:00
|
|
|
else
|
2023-01-20 12:02:39 +00:00
|
|
|
reportError(UnknownProperty{tableTy, prop}, location);
|
2022-12-09 18:07:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-14 13:05:27 +01:00
|
|
|
bool hasIndexTypeFromType(TypeId ty, const std::string& prop, const Location& location, std::unordered_set<TypeId>& seen)
|
2022-12-09 18:07:25 +00:00
|
|
|
{
|
2023-04-14 13:05:27 +01:00
|
|
|
// If we have already encountered this type, we must assume that some
|
|
|
|
// other codepath will do the right thing and signal false if the
|
|
|
|
// property is not present.
|
|
|
|
const bool isUnseen = seen.insert(ty).second;
|
|
|
|
if (!isUnseen)
|
|
|
|
return true;
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
if (get<ErrorType>(ty) || get<AnyType>(ty) || get<NeverType>(ty))
|
2022-12-09 18:07:25 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (isString(ty))
|
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
std::optional<TypeId> mtIndex = Luau::findMetatableEntry(builtinTypes, module->errors, builtinTypes->stringType, "__index", location);
|
2022-12-09 18:07:25 +00:00
|
|
|
LUAU_ASSERT(mtIndex);
|
|
|
|
ty = *mtIndex;
|
|
|
|
}
|
|
|
|
|
2023-02-24 18:24:22 +00:00
|
|
|
if (auto tt = getTableType(ty))
|
|
|
|
{
|
|
|
|
if (findTablePropertyRespectingMeta(builtinTypes, module->errors, ty, prop, location))
|
|
|
|
return true;
|
|
|
|
|
2023-03-03 13:45:38 +00:00
|
|
|
else if (tt->indexer && isPrim(tt->indexer->indexType, PrimitiveType::String))
|
|
|
|
return true;
|
2023-02-24 18:24:22 +00:00
|
|
|
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (const ClassType* cls = get<ClassType>(ty))
|
2022-12-09 18:07:25 +00:00
|
|
|
return bool(lookupClassProp(cls, prop));
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (const UnionType* utv = get<UnionType>(ty))
|
2023-04-14 13:05:27 +01:00
|
|
|
return std::all_of(begin(utv), end(utv), [&](TypeId part) {
|
|
|
|
return hasIndexTypeFromType(part, prop, location, seen);
|
|
|
|
});
|
2023-01-03 17:33:19 +00:00
|
|
|
else if (const IntersectionType* itv = get<IntersectionType>(ty))
|
2022-12-09 18:07:25 +00:00
|
|
|
return std::any_of(begin(itv), end(itv), [&](TypeId part) {
|
2023-04-14 13:05:27 +01:00
|
|
|
return hasIndexTypeFromType(part, prop, location, seen);
|
2022-12-09 18:07:25 +00:00
|
|
|
});
|
|
|
|
else
|
|
|
|
return false;
|
2022-08-11 21:42:54 +01:00
|
|
|
}
|
2022-06-17 01:54:42 +01:00
|
|
|
};
|
|
|
|
|
2023-01-03 17:33:19 +00:00
|
|
|
void check(NotNull<BuiltinTypes> builtinTypes, DcrLogger* logger, const SourceModule& sourceModule, Module* module)
|
2022-06-17 01:54:42 +01:00
|
|
|
{
|
2023-01-03 17:33:19 +00:00
|
|
|
TypeChecker2 typeChecker{builtinTypes, logger, &sourceModule, module};
|
2023-02-03 12:34:12 +00:00
|
|
|
typeChecker.reduceTypes();
|
2022-08-18 22:04:33 +01:00
|
|
|
typeChecker.visit(sourceModule.root);
|
2023-01-03 17:33:19 +00:00
|
|
|
|
|
|
|
unfreeze(module->interfaceTypes);
|
|
|
|
copyErrors(module->errors, module->interfaceTypes);
|
|
|
|
freeze(module->interfaceTypes);
|
2022-06-17 01:54:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Luau
|