2022-06-03 23:15:45 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
|
|
|
|
#include "Luau/ConstraintGraphBuilder.h"
|
2022-08-11 22:01:33 +01:00
|
|
|
#include "Luau/Ast.h"
|
2022-08-18 22:32:08 +01:00
|
|
|
#include "Luau/Common.h"
|
2022-08-11 22:01:33 +01:00
|
|
|
#include "Luau/Constraint.h"
|
2022-09-02 00:14:03 +01:00
|
|
|
#include "Luau/ModuleResolver.h"
|
2022-07-01 00:52:43 +01:00
|
|
|
#include "Luau/RecursionCounter.h"
|
|
|
|
#include "Luau/ToString.h"
|
2022-09-08 23:14:25 +01:00
|
|
|
#include "Luau/DcrLogger.h"
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
LUAU_FASTINT(LuauCheckRecursionLimit);
|
2022-09-08 23:14:25 +01:00
|
|
|
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-06-24 02:56:00 +01:00
|
|
|
#include "Luau/Scope.h"
|
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
static std::optional<AstExpr*> matchRequire(const AstExprCall& call)
|
|
|
|
{
|
|
|
|
const char* require = "require";
|
|
|
|
|
|
|
|
if (call.args.size != 1)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
const AstExprGlobal* funcAsGlobal = call.func->as<AstExprGlobal>();
|
|
|
|
if (!funcAsGlobal || funcAsGlobal->name != require)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
if (call.args.size != 1)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
return call.args.data[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
ConstraintGraphBuilder::ConstraintGraphBuilder(const ModuleName& moduleName, ModulePtr module, TypeArena* arena,
|
2022-09-08 23:14:25 +01:00
|
|
|
NotNull<ModuleResolver> moduleResolver, NotNull<SingletonTypes> singletonTypes, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope, DcrLogger* logger)
|
2022-07-01 00:52:43 +01:00
|
|
|
: moduleName(moduleName)
|
2022-08-11 22:01:33 +01:00
|
|
|
, module(module)
|
2022-09-08 23:14:25 +01:00
|
|
|
, singletonTypes(singletonTypes)
|
2022-06-03 23:15:45 +01:00
|
|
|
, arena(arena)
|
|
|
|
, rootScope(nullptr)
|
2022-09-02 00:14:03 +01:00
|
|
|
, moduleResolver(moduleResolver)
|
2022-07-01 00:52:43 +01:00
|
|
|
, ice(ice)
|
|
|
|
, globalScope(globalScope)
|
2022-09-08 23:14:25 +01:00
|
|
|
, logger(logger)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-09-08 23:14:25 +01:00
|
|
|
if (FFlag::DebugLuauLogSolverToJson)
|
|
|
|
LUAU_ASSERT(logger);
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
LUAU_ASSERT(module);
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::freshType(const ScopePtr& scope)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
return arena->addType(FreeTypeVar{scope.get()});
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId ConstraintGraphBuilder::freshTypePack(const ScopePtr& scope)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
FreeTypePack f{scope.get()};
|
2022-06-03 23:15:45 +01:00
|
|
|
return arena->addTypePack(TypePackVar{std::move(f)});
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr ConstraintGraphBuilder::childScope(AstNode* node, const ScopePtr& parent)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
auto scope = std::make_shared<Scope>(parent);
|
2022-08-18 22:32:08 +01:00
|
|
|
scopes.emplace_back(node->location, scope);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
scope->returnType = parent->returnType;
|
2022-09-08 23:14:25 +01:00
|
|
|
scope->varargPack = parent->varargPack;
|
2022-08-18 22:32:08 +01:00
|
|
|
|
|
|
|
parent->children.push_back(NotNull{scope.get()});
|
|
|
|
module->astScopes[node] = scope.get();
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
return scope;
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, const Location& location, ConstraintV cv)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-09-02 00:14:03 +01:00
|
|
|
scope->constraints.emplace_back(new Constraint{NotNull{scope.get()}, location, std::move(cv)});
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
|
|
|
scope->constraints.emplace_back(std::move(c));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConstraintGraphBuilder::visit(AstStatBlock* block)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(scopes.empty());
|
|
|
|
LUAU_ASSERT(rootScope == nullptr);
|
2022-08-11 22:01:33 +01:00
|
|
|
ScopePtr scope = std::make_shared<Scope>(globalScope);
|
2022-07-29 05:24:07 +01:00
|
|
|
rootScope = scope.get();
|
|
|
|
scopes.emplace_back(block->location, scope);
|
2022-08-18 22:32:08 +01:00
|
|
|
module->astScopes[block] = NotNull{scope.get()};
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
rootScope->returnType = freshTypePack(scope);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
prepopulateGlobalScope(scope, block);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
visitBlockWithoutChildScope(scope, block);
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
RecursionCounter counter{&recursionCount};
|
|
|
|
|
|
|
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
|
|
|
{
|
|
|
|
reportCodeTooComplex(block->location);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
std::unordered_map<Name, Location> aliasDefinitionLocations;
|
|
|
|
|
|
|
|
// In order to enable mutually-recursive type aliases, we need to
|
|
|
|
// populate the type bindings before we actually check any of the
|
|
|
|
// alias statements. Since we're not ready to actually resolve
|
|
|
|
// any of the annotations, we just use a fresh type for now.
|
|
|
|
for (AstStat* stat : block->body)
|
|
|
|
{
|
|
|
|
if (auto alias = stat->as<AstStatTypeAlias>())
|
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
if (scope->privateTypeBindings.count(alias->name.value) != 0)
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
auto it = aliasDefinitionLocations.find(alias->name.value);
|
|
|
|
LUAU_ASSERT(it != aliasDefinitionLocations.end());
|
|
|
|
reportError(alias->location, DuplicateTypeDefinition{alias->name.value, it->second});
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasGenerics = alias->generics.size > 0 || alias->genericPacks.size > 0;
|
|
|
|
|
|
|
|
ScopePtr defnScope = scope;
|
|
|
|
if (hasGenerics)
|
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
defnScope = childScope(alias, scope);
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TypeId initialType = freshType(scope);
|
|
|
|
TypeFun initialFun = TypeFun{initialType};
|
|
|
|
|
|
|
|
for (const auto& [name, gen] : createGenerics(defnScope, alias->generics))
|
|
|
|
{
|
|
|
|
initialFun.typeParams.push_back(gen);
|
2022-08-11 22:01:33 +01:00
|
|
|
defnScope->privateTypeBindings[name] = TypeFun{gen.ty};
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& [name, genPack] : createGenericPacks(defnScope, alias->genericPacks))
|
|
|
|
{
|
|
|
|
initialFun.typePackParams.push_back(genPack);
|
2022-08-11 22:01:33 +01:00
|
|
|
defnScope->privateTypePackBindings[name] = genPack.tp;
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
|
2022-08-11 22:01:33 +01:00
|
|
|
scope->privateTypeBindings[alias->name.value] = std::move(initialFun);
|
2022-08-04 23:35:33 +01:00
|
|
|
astTypeAliasDefiningScopes[alias] = defnScope;
|
|
|
|
aliasDefinitionLocations[alias->name.value] = alias->location;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
for (AstStat* stat : block->body)
|
|
|
|
visit(scope, stat);
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
|
|
|
if (auto s = stat->as<AstStatBlock>())
|
|
|
|
visit(scope, s);
|
|
|
|
else if (auto s = stat->as<AstStatLocal>())
|
|
|
|
visit(scope, s);
|
2022-07-14 23:52:26 +01:00
|
|
|
else if (auto s = stat->as<AstStatFor>())
|
|
|
|
visit(scope, s);
|
2022-09-02 00:14:03 +01:00
|
|
|
else if (auto s = stat->as<AstStatForIn>())
|
|
|
|
visit(scope, s);
|
2022-08-11 22:01:33 +01:00
|
|
|
else if (auto s = stat->as<AstStatWhile>())
|
|
|
|
visit(scope, s);
|
2022-08-18 22:32:08 +01:00
|
|
|
else if (auto s = stat->as<AstStatRepeat>())
|
|
|
|
visit(scope, s);
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto f = stat->as<AstStatFunction>())
|
|
|
|
visit(scope, f);
|
2022-06-03 23:15:45 +01:00
|
|
|
else if (auto f = stat->as<AstStatLocalFunction>())
|
|
|
|
visit(scope, f);
|
|
|
|
else if (auto r = stat->as<AstStatReturn>())
|
|
|
|
visit(scope, r);
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto a = stat->as<AstStatAssign>())
|
|
|
|
visit(scope, a);
|
2022-08-18 22:32:08 +01:00
|
|
|
else if (auto a = stat->as<AstStatCompoundAssign>())
|
|
|
|
visit(scope, a);
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto e = stat->as<AstStatExpr>())
|
|
|
|
checkPack(scope, e->expr);
|
|
|
|
else if (auto i = stat->as<AstStatIf>())
|
|
|
|
visit(scope, i);
|
2022-06-24 02:56:00 +01:00
|
|
|
else if (auto a = stat->as<AstStatTypeAlias>())
|
|
|
|
visit(scope, a);
|
2022-08-04 23:35:33 +01:00
|
|
|
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
|
|
|
visit(scope, s);
|
|
|
|
else if (auto s = stat->as<AstStatDeclareClass>())
|
|
|
|
visit(scope, s);
|
|
|
|
else if (auto s = stat->as<AstStatDeclareFunction>())
|
|
|
|
visit(scope, s);
|
2022-06-03 23:15:45 +01:00
|
|
|
else
|
|
|
|
LUAU_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
|
|
|
std::vector<TypeId> varTypes;
|
|
|
|
|
|
|
|
for (AstLocal* local : local->vars)
|
|
|
|
{
|
|
|
|
TypeId ty = freshType(scope);
|
2022-07-29 05:24:07 +01:00
|
|
|
Location location = local->location;
|
2022-06-24 02:56:00 +01:00
|
|
|
|
|
|
|
if (local->annotation)
|
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
location = local->annotation->location;
|
2022-08-04 23:35:33 +01:00
|
|
|
TypeId annotation = resolveType(scope, local->annotation, /* topLevel */ true);
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, location, SubtypeConstraint{ty, annotation});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
varTypes.push_back(ty);
|
2022-07-29 05:24:07 +01:00
|
|
|
scope->bindings[local] = Binding{ty, location};
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
for (size_t i = 0; i < local->values.size; ++i)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-08-25 22:53:50 +01:00
|
|
|
AstExpr* value = local->values.data[i];
|
|
|
|
if (value->is<AstExprConstantNil>())
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
|
|
|
// HACK: we leave nil-initialized things floating under the assumption that they will later be populated.
|
|
|
|
// See the test TypeInfer/infer_locals_with_nil_value.
|
|
|
|
// Better flow awareness should make this obsolete.
|
|
|
|
}
|
|
|
|
else if (i == local->values.size - 1)
|
|
|
|
{
|
2022-08-25 22:53:50 +01:00
|
|
|
TypePackId exprPack = checkPack(scope, value);
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
if (i < local->vars.size)
|
|
|
|
{
|
|
|
|
std::vector<TypeId> tailValues{varTypes.begin() + i, varTypes.end()};
|
|
|
|
TypePackId tailPack = arena->addTypePack(std::move(tailValues));
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, local->location, PackSubtypeConstraint{exprPack, tailPack});
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-08-25 22:53:50 +01:00
|
|
|
TypeId exprType = check(scope, value);
|
2022-06-17 02:05:14 +01:00
|
|
|
if (i < varTypes.size())
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, local->location, SubtypeConstraint{varTypes[i], exprType});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (local->values.size > 0)
|
|
|
|
{
|
|
|
|
// To correctly handle 'require', we need to import the exported type bindings into the variable 'namespace'.
|
|
|
|
for (size_t i = 0; i < local->values.size && i < local->vars.size; ++i)
|
|
|
|
{
|
|
|
|
const AstExprCall* call = local->values.data[i]->as<AstExprCall>();
|
|
|
|
if (!call)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (auto maybeRequire = matchRequire(*call))
|
|
|
|
{
|
|
|
|
AstExpr* require = *maybeRequire;
|
|
|
|
|
|
|
|
if (auto moduleInfo = moduleResolver->resolveModuleInfo(moduleName, *require))
|
|
|
|
{
|
|
|
|
const Name name{local->vars.data[i]->name.value};
|
|
|
|
|
|
|
|
if (ModulePtr module = moduleResolver->getModule(moduleInfo->name))
|
|
|
|
scope->importedTypeBindings[name] = module->getModuleScope()->exportedTypeBindings;
|
|
|
|
}
|
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
|
2022-07-14 23:52:26 +01:00
|
|
|
{
|
2022-08-04 23:35:33 +01:00
|
|
|
auto checkNumber = [&](AstExpr* expr) {
|
2022-07-14 23:52:26 +01:00
|
|
|
if (!expr)
|
|
|
|
return;
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2022-07-14 23:52:26 +01:00
|
|
|
TypeId t = check(scope, expr);
|
2022-09-08 23:14:25 +01:00
|
|
|
addConstraint(scope, expr->location, SubtypeConstraint{t, singletonTypes->numberType});
|
2022-07-14 23:52:26 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
checkNumber(for_->from);
|
|
|
|
checkNumber(for_->to);
|
|
|
|
checkNumber(for_->step);
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr forScope = childScope(for_, scope);
|
2022-09-08 23:14:25 +01:00
|
|
|
forScope->bindings[for_->var] = Binding{singletonTypes->numberType, for_->var->location};
|
2022-07-14 23:52:26 +01:00
|
|
|
|
|
|
|
visit(forScope, for_->body);
|
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatForIn* forIn)
|
|
|
|
{
|
|
|
|
ScopePtr loopScope = childScope(forIn, scope);
|
|
|
|
|
|
|
|
TypePackId iterator = checkPack(scope, forIn->values);
|
|
|
|
|
|
|
|
std::vector<TypeId> variableTypes;
|
|
|
|
variableTypes.reserve(forIn->vars.size);
|
|
|
|
for (AstLocal* var : forIn->vars)
|
|
|
|
{
|
|
|
|
TypeId ty = freshType(loopScope);
|
|
|
|
loopScope->bindings[var] = Binding{ty, var->location};
|
|
|
|
variableTypes.push_back(ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// It is always ok to provide too few variables, so we give this pack a free tail.
|
|
|
|
TypePackId variablePack = arena->addTypePack(std::move(variableTypes), arena->addTypePack(FreeTypePack{loopScope.get()}));
|
|
|
|
|
|
|
|
addConstraint(loopScope, getLocation(forIn->values), IterableConstraint{iterator, variablePack});
|
|
|
|
|
|
|
|
visit(loopScope, forIn->body);
|
|
|
|
}
|
|
|
|
|
2022-08-11 22:01:33 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatWhile* while_)
|
|
|
|
{
|
|
|
|
check(scope, while_->condition);
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr whileScope = childScope(while_, scope);
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
visit(whileScope, while_->body);
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatRepeat* repeat)
|
|
|
|
{
|
|
|
|
ScopePtr repeatScope = childScope(repeat, scope);
|
|
|
|
|
|
|
|
visit(repeatScope, repeat->body);
|
|
|
|
|
|
|
|
// The condition does indeed have access to bindings from within the body of
|
|
|
|
// the loop.
|
|
|
|
check(repeatScope, repeat->condition);
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void addConstraints(Constraint* constraint, NotNull<Scope> scope)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
|
|
|
scope->constraints.reserve(scope->constraints.size() + scope->constraints.size());
|
|
|
|
|
|
|
|
for (const auto& c : scope->constraints)
|
2022-06-17 02:05:14 +01:00
|
|
|
constraint->dependencies.push_back(NotNull{c.get()});
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
for (const auto& c : scope->unqueuedConstraints)
|
|
|
|
constraint->dependencies.push_back(NotNull{c.get()});
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
for (NotNull<Scope> childScope : scope->children)
|
2022-06-03 23:15:45 +01:00
|
|
|
addConstraints(constraint, childScope);
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
|
|
|
// Local
|
|
|
|
// Global
|
|
|
|
// Dotted path
|
|
|
|
// Self?
|
|
|
|
|
|
|
|
TypeId functionType = nullptr;
|
|
|
|
auto ty = scope->lookup(function->name);
|
2022-07-01 00:52:43 +01:00
|
|
|
LUAU_ASSERT(!ty.has_value()); // The parser ensures that every local function has a distinct Symbol for its name.
|
|
|
|
|
|
|
|
functionType = arena->addType(BlockedTypeVar{});
|
2022-07-29 05:24:07 +01:00
|
|
|
scope->bindings[function->name] = Binding{functionType, function->name->location};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
FunctionSignature sig = checkFunctionSignature(scope, function->func);
|
2022-07-29 05:24:07 +01:00
|
|
|
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->func->location};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
checkFunctionBody(sig.bodyScope, function->func);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
|
2022-09-02 00:14:03 +01:00
|
|
|
std::unique_ptr<Constraint> c =
|
|
|
|
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
|
2022-07-29 05:24:07 +01:00
|
|
|
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
addConstraint(scope, std::move(c));
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* function)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
|
|
|
// Name could be AstStatLocal, AstStatGlobal, AstStatIndexName.
|
|
|
|
// With or without self
|
|
|
|
|
|
|
|
TypeId functionType = nullptr;
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
FunctionSignature sig = checkFunctionSignature(scope, function->func);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-06-17 02:05:14 +01:00
|
|
|
std::optional<TypeId> existingFunctionTy = scope->lookup(localName->local);
|
|
|
|
if (existingFunctionTy)
|
|
|
|
{
|
|
|
|
// Duplicate definition
|
|
|
|
functionType = *existingFunctionTy;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
functionType = arena->addType(BlockedTypeVar{});
|
2022-07-29 05:24:07 +01:00
|
|
|
scope->bindings[localName->local] = Binding{functionType, localName->location};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
2022-07-29 05:24:07 +01:00
|
|
|
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else if (AstExprGlobal* globalName = function->name->as<AstExprGlobal>())
|
|
|
|
{
|
|
|
|
std::optional<TypeId> existingFunctionTy = scope->lookup(globalName->name);
|
|
|
|
if (existingFunctionTy)
|
|
|
|
{
|
|
|
|
// Duplicate definition
|
|
|
|
functionType = *existingFunctionTy;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
functionType = arena->addType(BlockedTypeVar{});
|
2022-07-29 05:24:07 +01:00
|
|
|
rootScope->bindings[globalName->name] = Binding{functionType, globalName->location};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
2022-07-29 05:24:07 +01:00
|
|
|
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else if (AstExprIndexName* indexName = function->name->as<AstExprIndexName>())
|
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
TypeId containingTableType = check(scope, indexName->expr);
|
|
|
|
|
|
|
|
functionType = arena->addType(BlockedTypeVar{});
|
|
|
|
TypeId prospectiveTableType =
|
|
|
|
arena->addType(TableTypeVar{}); // TODO look into stack utilization. This is probably ok because it scales with AST depth.
|
|
|
|
NotNull<TableTypeVar> prospectiveTable{getMutable<TableTypeVar>(prospectiveTableType)};
|
|
|
|
|
|
|
|
Property& prop = prospectiveTable->props[indexName->index.value];
|
|
|
|
prop.type = functionType;
|
|
|
|
prop.location = function->name->location;
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, indexName->location, SubtypeConstraint{containingTableType, prospectiveTableType});
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
else if (AstExprError* err = function->name->as<AstExprError>())
|
|
|
|
{
|
2022-09-08 23:14:25 +01:00
|
|
|
functionType = singletonTypes->errorRecoveryType();
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
LUAU_ASSERT(functionType != nullptr);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
checkFunctionBody(sig.bodyScope, function->func);
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
|
2022-09-02 00:14:03 +01:00
|
|
|
std::unique_ptr<Constraint> c =
|
|
|
|
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
|
2022-07-29 05:24:07 +01:00
|
|
|
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
|
2022-06-03 23:15:45 +01:00
|
|
|
|
|
|
|
addConstraint(scope, std::move(c));
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatReturn* ret)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
|
|
|
TypePackId exprTypes = checkPack(scope, ret->list);
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, ret->location, PackSubtypeConstraint{exprTypes, scope->returnType});
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr innerScope = childScope(block, scope);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
visitBlockWithoutChildScope(innerScope, block);
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2022-09-02 00:14:03 +01:00
|
|
|
TypePackId varPackId = checkPack(scope, assign->vars);
|
2022-06-17 02:05:14 +01:00
|
|
|
TypePackId valuePack = checkPack(scope, assign->values);
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, assign->location, PackSubtypeConstraint{valuePack, varPackId});
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatCompoundAssign* assign)
|
|
|
|
{
|
|
|
|
// Synthesize A = A op B from A op= B and then build constraints for that instead.
|
|
|
|
|
|
|
|
AstExprBinary exprBinary{assign->location, assign->op, assign->var, assign->value};
|
|
|
|
AstExpr* exprBinaryPtr = &exprBinary;
|
|
|
|
|
|
|
|
AstArray<AstExpr*> vars{&assign->var, 1};
|
|
|
|
AstArray<AstExpr*> values{&exprBinaryPtr, 1};
|
|
|
|
AstStatAssign syntheticAssign{assign->location, vars, values};
|
|
|
|
|
|
|
|
visit(scope, &syntheticAssign);
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatIf* ifStatement)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
|
|
|
check(scope, ifStatement->condition);
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr thenScope = childScope(ifStatement->thenbody, scope);
|
2022-06-17 02:05:14 +01:00
|
|
|
visit(thenScope, ifStatement->thenbody);
|
|
|
|
|
|
|
|
if (ifStatement->elsebody)
|
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr elseScope = childScope(ifStatement->elsebody, scope);
|
2022-06-17 02:05:14 +01:00
|
|
|
visit(elseScope, ifStatement->elsebody);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias)
|
2022-06-24 02:56:00 +01:00
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
auto bindingIt = scope->privateTypeBindings.find(alias->name.value);
|
2022-08-04 23:35:33 +01:00
|
|
|
ScopePtr* defnIt = astTypeAliasDefiningScopes.find(alias);
|
|
|
|
// These will be undefined if the alias was a duplicate definition, in which
|
|
|
|
// case we just skip over it.
|
2022-08-11 22:01:33 +01:00
|
|
|
if (bindingIt == scope->privateTypeBindings.end() || defnIt == nullptr)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
2022-08-04 23:35:33 +01:00
|
|
|
return;
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
ScopePtr resolvingScope = *defnIt;
|
|
|
|
TypeId ty = resolveType(resolvingScope, alias->type, /* topLevel */ true);
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
if (alias->exported)
|
|
|
|
{
|
|
|
|
Name typeName(alias->name.value);
|
|
|
|
scope->exportedTypeBindings[typeName] = TypeFun{ty};
|
|
|
|
}
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
LUAU_ASSERT(get<FreeTypeVar>(bindingIt->second.type));
|
2022-06-24 02:56:00 +01:00
|
|
|
|
|
|
|
// Rather than using a subtype constraint, we instead directly bind
|
|
|
|
// the free type we generated in the first pass to the resolved type.
|
|
|
|
// This prevents a case where you could cause another constraint to
|
|
|
|
// bind the free alias type to an unrelated type, causing havoc.
|
2022-08-04 23:35:33 +01:00
|
|
|
asMutable(bindingIt->second.type)->ty.emplace<BoundTypeVar>(ty);
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, alias->location, NameConstraint{ty, alias->name.value});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareGlobal* global)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(global->type);
|
|
|
|
|
|
|
|
TypeId globalTy = resolveType(scope, global->type);
|
2022-08-11 22:01:33 +01:00
|
|
|
Name globalName(global->name.value);
|
|
|
|
|
|
|
|
module->declaredGlobals[globalName] = globalTy;
|
2022-08-04 23:35:33 +01:00
|
|
|
scope->bindings[global->name] = Binding{globalTy, global->location};
|
|
|
|
}
|
|
|
|
|
2022-08-11 22:01:33 +01:00
|
|
|
static bool isMetamethod(const Name& name)
|
|
|
|
{
|
|
|
|
return name == "__index" || name == "__newindex" || name == "__call" || name == "__concat" || name == "__unm" || name == "__add" ||
|
|
|
|
name == "__sub" || name == "__mul" || name == "__div" || name == "__mod" || name == "__pow" || name == "__tostring" ||
|
|
|
|
name == "__metatable" || name == "__eq" || name == "__lt" || name == "__le" || name == "__mode" || name == "__iter" || name == "__len";
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareClass* declaredClass)
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
std::optional<TypeId> superTy = std::nullopt;
|
|
|
|
if (declaredClass->superName)
|
|
|
|
{
|
|
|
|
Name superName = Name(declaredClass->superName->value);
|
|
|
|
std::optional<TypeFun> lookupType = scope->lookupType(superName);
|
|
|
|
|
|
|
|
if (!lookupType)
|
|
|
|
{
|
|
|
|
reportError(declaredClass->location, UnknownSymbol{superName, UnknownSymbol::Type});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't have generic classes, so this assertion _should_ never be hit.
|
|
|
|
LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0);
|
|
|
|
superTy = lookupType->type;
|
|
|
|
|
|
|
|
if (!get<ClassTypeVar>(follow(*superTy)))
|
|
|
|
{
|
|
|
|
reportError(declaredClass->location,
|
|
|
|
GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredClass->name.value)});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Name className(declaredClass->name.value);
|
|
|
|
|
|
|
|
TypeId classTy = arena->addType(ClassTypeVar(className, {}, superTy, std::nullopt, {}, {}, moduleName));
|
|
|
|
ClassTypeVar* ctv = getMutable<ClassTypeVar>(classTy);
|
|
|
|
|
|
|
|
TypeId metaTy = arena->addType(TableTypeVar{TableState::Sealed, scope->level});
|
|
|
|
TableTypeVar* metatable = getMutable<TableTypeVar>(metaTy);
|
|
|
|
|
|
|
|
ctv->metatable = metaTy;
|
|
|
|
|
|
|
|
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
|
|
|
|
|
|
|
for (const AstDeclaredClassProp& prop : declaredClass->props)
|
|
|
|
{
|
|
|
|
Name propName(prop.name.value);
|
|
|
|
TypeId propTy = resolveType(scope, prop.ty);
|
|
|
|
|
|
|
|
bool assignToMetatable = isMetamethod(propName);
|
|
|
|
|
|
|
|
// Function types always take 'self', but this isn't reflected in the
|
|
|
|
// parsed annotation. Add it here.
|
|
|
|
if (prop.isMethod)
|
|
|
|
{
|
|
|
|
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(propTy))
|
|
|
|
{
|
|
|
|
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
|
|
|
|
ftv->argTypes = arena->addTypePack(TypePack{{classTy}, ftv->argTypes});
|
|
|
|
|
|
|
|
ftv->hasSelf = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctv->props.count(propName) == 0)
|
|
|
|
{
|
|
|
|
if (assignToMetatable)
|
|
|
|
metatable->props[propName] = {propTy};
|
|
|
|
else
|
|
|
|
ctv->props[propName] = {propTy};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TypeId currentTy = assignToMetatable ? metatable->props[propName].type : ctv->props[propName].type;
|
|
|
|
|
|
|
|
// We special-case this logic to keep the intersection flat; otherwise we
|
|
|
|
// would create a ton of nested intersection types.
|
|
|
|
if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(currentTy))
|
|
|
|
{
|
|
|
|
std::vector<TypeId> options = itv->parts;
|
|
|
|
options.push_back(propTy);
|
|
|
|
TypeId newItv = arena->addType(IntersectionTypeVar{std::move(options)});
|
|
|
|
|
|
|
|
if (assignToMetatable)
|
|
|
|
metatable->props[propName] = {newItv};
|
|
|
|
else
|
|
|
|
ctv->props[propName] = {newItv};
|
|
|
|
}
|
|
|
|
else if (get<FunctionTypeVar>(currentTy))
|
|
|
|
{
|
|
|
|
TypeId intersection = arena->addType(IntersectionTypeVar{{currentTy, propTy}});
|
|
|
|
|
|
|
|
if (assignToMetatable)
|
|
|
|
metatable->props[propName] = {intersection};
|
|
|
|
else
|
|
|
|
ctv->props[propName] = {intersection};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(declaredClass->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareFunction* global)
|
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> generics = createGenerics(scope, global->generics);
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPacks = createGenericPacks(scope, global->genericPacks);
|
|
|
|
|
|
|
|
std::vector<TypeId> genericTys;
|
|
|
|
genericTys.reserve(generics.size());
|
|
|
|
for (auto& [name, generic] : generics)
|
2022-09-08 23:14:25 +01:00
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
genericTys.push_back(generic.ty);
|
2022-09-08 23:14:25 +01:00
|
|
|
scope->privateTypeBindings[name] = TypeFun{generic.ty};
|
|
|
|
}
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
std::vector<TypePackId> genericTps;
|
|
|
|
genericTps.reserve(genericPacks.size());
|
|
|
|
for (auto& [name, generic] : genericPacks)
|
2022-09-08 23:14:25 +01:00
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
genericTps.push_back(generic.tp);
|
2022-09-08 23:14:25 +01:00
|
|
|
scope->privateTypePackBindings[name] = generic.tp;
|
|
|
|
}
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
ScopePtr funScope = scope;
|
|
|
|
if (!generics.empty() || !genericPacks.empty())
|
2022-08-18 22:32:08 +01:00
|
|
|
funScope = childScope(global, scope);
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
TypePackId paramPack = resolveTypePack(funScope, global->params);
|
|
|
|
TypePackId retPack = resolveTypePack(funScope, global->retTypes);
|
|
|
|
TypeId fnType = arena->addType(FunctionTypeVar{funScope->level, std::move(genericTys), std::move(genericTps), paramPack, retPack});
|
|
|
|
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(fnType);
|
|
|
|
|
|
|
|
ftv->argNames.reserve(global->paramNames.size);
|
|
|
|
for (const auto& el : global->paramNames)
|
|
|
|
ftv->argNames.push_back(FunctionArgument{el.first.value, el.second});
|
|
|
|
|
|
|
|
Name fnName(global->name.value);
|
|
|
|
|
|
|
|
module->declaredGlobals[fnName] = fnType;
|
|
|
|
scope->bindings[global->name] = Binding{fnType, global->location};
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-09-02 00:14:03 +01:00
|
|
|
std::vector<TypeId> head;
|
|
|
|
std::optional<TypePackId> tail;
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
for (size_t i = 0; i < exprs.size; ++i)
|
|
|
|
{
|
|
|
|
AstExpr* expr = exprs.data[i];
|
|
|
|
if (i < exprs.size - 1)
|
2022-09-02 00:14:03 +01:00
|
|
|
head.push_back(check(scope, expr));
|
2022-06-17 02:05:14 +01:00
|
|
|
else
|
2022-09-02 00:14:03 +01:00
|
|
|
tail = checkPack(scope, expr);
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
if (head.empty() && tail)
|
|
|
|
return *tail;
|
2022-06-17 02:05:14 +01:00
|
|
|
else
|
2022-09-02 00:14:03 +01:00
|
|
|
return arena->addTypePack(TypePack{std::move(head), tail});
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* expr)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
RecursionCounter counter{&recursionCount};
|
|
|
|
|
|
|
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
|
|
|
{
|
|
|
|
reportCodeTooComplex(expr->location);
|
2022-09-08 23:14:25 +01:00
|
|
|
return singletonTypes->errorRecoveryTypePack();
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
TypePackId result = nullptr;
|
|
|
|
|
|
|
|
if (AstExprCall* call = expr->as<AstExprCall>())
|
|
|
|
{
|
2022-09-15 23:38:17 +01:00
|
|
|
TypeId fnType = check(scope, call->func);
|
|
|
|
|
|
|
|
const size_t constraintIndex = scope->constraints.size();
|
|
|
|
const size_t scopeIndex = scopes.size();
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
std::vector<TypeId> args;
|
|
|
|
|
|
|
|
for (AstExpr* arg : call->args)
|
|
|
|
{
|
|
|
|
args.push_back(check(scope, arg));
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO self
|
|
|
|
|
2022-09-15 23:38:17 +01:00
|
|
|
const size_t constraintEndIndex = scope->constraints.size();
|
|
|
|
const size_t scopeEndIndex = scopes.size();
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
astOriginalCallTypes[call->func] = fnType;
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
TypeId instantiatedType = arena->addType(BlockedTypeVar{});
|
2022-09-02 00:14:03 +01:00
|
|
|
TypePackId rets = arena->addTypePack(BlockedTypePack{});
|
2022-06-17 02:05:14 +01:00
|
|
|
FunctionTypeVar ftv(arena->addTypePack(TypePack{args, {}}), rets);
|
|
|
|
TypeId inferredFnType = arena->addType(ftv);
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
scope->unqueuedConstraints.push_back(
|
|
|
|
std::make_unique<Constraint>(NotNull{scope.get()}, call->func->location, InstantiationConstraint{instantiatedType, fnType}));
|
|
|
|
NotNull<const Constraint> ic(scope->unqueuedConstraints.back().get());
|
|
|
|
|
|
|
|
scope->unqueuedConstraints.push_back(
|
|
|
|
std::make_unique<Constraint>(NotNull{scope.get()}, call->func->location, SubtypeConstraint{inferredFnType, instantiatedType}));
|
2022-09-15 23:38:17 +01:00
|
|
|
NotNull<Constraint> sc(scope->unqueuedConstraints.back().get());
|
|
|
|
|
|
|
|
// We force constraints produced by checking function arguments to wait
|
|
|
|
// until after we have resolved the constraint on the function itself.
|
|
|
|
// This ensures, for instance, that we start inferring the contents of
|
|
|
|
// lambdas under the assumption that their arguments and return types
|
|
|
|
// will be compatible with the enclosing function call.
|
|
|
|
for (size_t ci = constraintIndex; ci < constraintEndIndex; ++ci)
|
|
|
|
scope->constraints[ci]->dependencies.push_back(sc);
|
|
|
|
|
|
|
|
for (size_t si = scopeIndex; si < scopeEndIndex; ++si)
|
|
|
|
{
|
|
|
|
for (auto& c : scopes[si].second->constraints)
|
|
|
|
{
|
|
|
|
c->dependencies.push_back(sc);
|
|
|
|
}
|
|
|
|
}
|
2022-09-02 00:14:03 +01:00
|
|
|
|
|
|
|
addConstraint(scope, call->func->location,
|
|
|
|
FunctionCallConstraint{
|
|
|
|
{ic, sc},
|
|
|
|
fnType,
|
|
|
|
rets,
|
|
|
|
call,
|
|
|
|
});
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
result = rets;
|
|
|
|
}
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (AstExprVarargs* varargs = expr->as<AstExprVarargs>())
|
|
|
|
{
|
|
|
|
if (scope->varargPack)
|
|
|
|
result = *scope->varargPack;
|
|
|
|
else
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryTypePack();
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
TypeId t = check(scope, expr);
|
|
|
|
result = arena->addTypePack({t});
|
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_ASSERT(result);
|
|
|
|
astTypePacks[expr] = result;
|
|
|
|
return result;
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
RecursionCounter counter{&recursionCount};
|
|
|
|
|
|
|
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
|
|
|
{
|
|
|
|
reportCodeTooComplex(expr->location);
|
2022-09-08 23:14:25 +01:00
|
|
|
return singletonTypes->errorRecoveryType();
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
TypeId result = nullptr;
|
|
|
|
|
|
|
|
if (auto group = expr->as<AstExprGroup>())
|
|
|
|
result = check(scope, group->expr);
|
|
|
|
else if (expr->is<AstExprConstantString>())
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->stringType;
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (expr->is<AstExprConstantNumber>())
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->numberType;
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (expr->is<AstExprConstantBool>())
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->booleanType;
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (expr->is<AstExprConstantNil>())
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->nilType;
|
2022-06-03 23:15:45 +01:00
|
|
|
else if (auto a = expr->as<AstExprLocal>())
|
|
|
|
{
|
|
|
|
std::optional<TypeId> ty = scope->lookup(a->local);
|
|
|
|
if (ty)
|
2022-06-17 02:05:14 +01:00
|
|
|
result = *ty;
|
|
|
|
else
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryType(); // FIXME? Record an error at this point?
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else if (auto g = expr->as<AstExprGlobal>())
|
|
|
|
{
|
|
|
|
std::optional<TypeId> ty = scope->lookup(g->name);
|
|
|
|
if (ty)
|
|
|
|
result = *ty;
|
2022-06-03 23:15:45 +01:00
|
|
|
else
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
/* prepopulateGlobalScope() has already added all global functions to the environment by this point, so any
|
|
|
|
* global that is not already in-scope is definitely an unknown symbol.
|
|
|
|
*/
|
|
|
|
reportError(g->location, UnknownSymbol{g->name.value});
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryType(); // FIXME? Record an error at this point?
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
}
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (expr->is<AstExprVarargs>())
|
|
|
|
result = flattenPack(scope, expr->location, checkPack(scope, expr));
|
|
|
|
else if (expr->is<AstExprCall>())
|
|
|
|
result = flattenPack(scope, expr->location, checkPack(scope, expr));
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto a = expr->as<AstExprFunction>())
|
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
FunctionSignature sig = checkFunctionSignature(scope, a);
|
|
|
|
checkFunctionBody(sig.bodyScope, a);
|
|
|
|
return sig.signature;
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else if (auto indexName = expr->as<AstExprIndexName>())
|
|
|
|
result = check(scope, indexName);
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (auto indexExpr = expr->as<AstExprIndexExpr>())
|
|
|
|
result = check(scope, indexExpr);
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto table = expr->as<AstExprTable>())
|
|
|
|
result = checkExprTable(scope, table);
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (auto unary = expr->as<AstExprUnary>())
|
|
|
|
result = check(scope, unary);
|
|
|
|
else if (auto binary = expr->as<AstExprBinary>())
|
|
|
|
result = check(scope, binary);
|
2022-08-18 22:32:08 +01:00
|
|
|
else if (auto ifElse = expr->as<AstExprIfElse>())
|
|
|
|
result = check(scope, ifElse);
|
2022-08-11 22:01:33 +01:00
|
|
|
else if (auto typeAssert = expr->as<AstExprTypeAssertion>())
|
|
|
|
result = check(scope, typeAssert);
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (auto err = expr->as<AstExprError>())
|
|
|
|
{
|
|
|
|
// Open question: Should we traverse into this?
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryType();
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(0);
|
|
|
|
result = freshType(scope);
|
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_ASSERT(result);
|
|
|
|
astTypes[expr] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexName* indexName)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
|
|
|
TypeId obj = check(scope, indexName->expr);
|
|
|
|
TypeId result = freshType(scope);
|
|
|
|
|
|
|
|
TableTypeVar::Props props{{indexName->index.value, Property{result}}};
|
|
|
|
const std::optional<TableIndexer> indexer;
|
|
|
|
TableTypeVar ttv{std::move(props), indexer, TypeLevel{}, TableState::Free};
|
|
|
|
|
|
|
|
TypeId expectedTableType = arena->addType(std::move(ttv));
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, indexName->expr->location, SubtypeConstraint{obj, expectedTableType});
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexExpr* indexExpr)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
TypeId obj = check(scope, indexExpr->expr);
|
|
|
|
TypeId indexType = check(scope, indexExpr->index);
|
|
|
|
|
|
|
|
TypeId result = freshType(scope);
|
|
|
|
|
|
|
|
TableIndexer indexer{indexType, result};
|
|
|
|
TypeId tableType = arena->addType(TableTypeVar{TableTypeVar::Props{}, TableIndexer{indexType, result}, TypeLevel{}, TableState::Free});
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, indexExpr->expr->location, SubtypeConstraint{obj, tableType});
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
TypeId operandType = check(scope, unary->expr);
|
|
|
|
|
|
|
|
switch (unary->op)
|
|
|
|
{
|
|
|
|
case AstExprUnary::Minus:
|
|
|
|
{
|
|
|
|
TypeId resultType = arena->addType(BlockedTypeVar{});
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, unary->location, UnaryConstraint{AstExprUnary::Minus, operandType, resultType});
|
2022-07-01 00:52:43 +01:00
|
|
|
return resultType;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
LUAU_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_UNREACHABLE();
|
2022-09-08 23:14:25 +01:00
|
|
|
return singletonTypes->errorRecoveryType();
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* binary)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
TypeId leftType = check(scope, binary->left);
|
|
|
|
TypeId rightType = check(scope, binary->right);
|
|
|
|
switch (binary->op)
|
|
|
|
{
|
|
|
|
case AstExprBinary::Or:
|
|
|
|
{
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, binary->location, SubtypeConstraint{leftType, rightType});
|
2022-07-01 00:52:43 +01:00
|
|
|
return leftType;
|
|
|
|
}
|
2022-08-18 22:32:08 +01:00
|
|
|
case AstExprBinary::Add:
|
|
|
|
{
|
|
|
|
TypeId resultType = arena->addType(BlockedTypeVar{});
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, binary->location, BinaryConstraint{AstExprBinary::Add, leftType, rightType, resultType});
|
2022-08-18 22:32:08 +01:00
|
|
|
return resultType;
|
|
|
|
}
|
2022-07-01 00:52:43 +01:00
|
|
|
case AstExprBinary::Sub:
|
|
|
|
{
|
|
|
|
TypeId resultType = arena->addType(BlockedTypeVar{});
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, binary->location, BinaryConstraint{AstExprBinary::Sub, leftType, rightType, resultType});
|
2022-07-01 00:52:43 +01:00
|
|
|
return resultType;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
LUAU_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_ASSERT(0);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIfElse* ifElse)
|
|
|
|
{
|
|
|
|
check(scope, ifElse->condition);
|
|
|
|
|
|
|
|
TypeId thenType = check(scope, ifElse->trueExpr);
|
|
|
|
TypeId elseType = check(scope, ifElse->falseExpr);
|
|
|
|
|
|
|
|
if (ifElse->hasElse)
|
|
|
|
{
|
|
|
|
TypeId resultType = arena->addType(BlockedTypeVar{});
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, ifElse->trueExpr->location, SubtypeConstraint{thenType, resultType});
|
|
|
|
addConstraint(scope, ifElse->falseExpr->location, SubtypeConstraint{elseType, resultType});
|
2022-08-18 22:32:08 +01:00
|
|
|
return resultType;
|
|
|
|
}
|
|
|
|
|
|
|
|
return thenType;
|
|
|
|
}
|
|
|
|
|
2022-08-11 22:01:33 +01:00
|
|
|
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert)
|
|
|
|
{
|
|
|
|
check(scope, typeAssert->expr);
|
|
|
|
return resolveType(scope, typeAssert->annotation);
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::checkExprTable(const ScopePtr& scope, AstExprTable* expr)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
|
|
|
TypeId ty = arena->addType(TableTypeVar{});
|
|
|
|
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
|
|
|
|
LUAU_ASSERT(ttv);
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
auto createIndexer = [this, scope, ttv](const Location& location, TypeId currentIndexType, TypeId currentResultType) {
|
2022-06-17 02:05:14 +01:00
|
|
|
if (!ttv->indexer)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-06-17 02:05:14 +01:00
|
|
|
TypeId indexType = this->freshType(scope);
|
|
|
|
TypeId resultType = this->freshType(scope);
|
|
|
|
ttv->indexer = TableIndexer{indexType, resultType};
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, location, SubtypeConstraint{ttv->indexer->indexType, currentIndexType});
|
|
|
|
addConstraint(scope, location, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType});
|
2022-06-17 02:05:14 +01:00
|
|
|
};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
for (const AstExprTable::Item& item : expr->items)
|
|
|
|
{
|
|
|
|
TypeId itemTy = check(scope, item.value);
|
2022-07-01 00:52:43 +01:00
|
|
|
if (get<ErrorTypeVar>(follow(itemTy)))
|
|
|
|
return ty;
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
if (item.key)
|
|
|
|
{
|
|
|
|
// Even though we don't need to use the type of the item's key if
|
|
|
|
// it's a string constant, we still want to check it to populate
|
|
|
|
// astTypes.
|
|
|
|
TypeId keyTy = check(scope, item.key);
|
|
|
|
|
|
|
|
if (AstExprConstantString* key = item.key->as<AstExprConstantString>())
|
|
|
|
{
|
|
|
|
ttv->props[key->value.begin()] = {itemTy};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-02 00:14:03 +01:00
|
|
|
createIndexer(item.key->location, keyTy, itemTy);
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-08 23:14:25 +01:00
|
|
|
TypeId numberType = singletonTypes->numberType;
|
2022-09-02 00:14:03 +01:00
|
|
|
// FIXME? The location isn't quite right here. Not sure what is
|
|
|
|
// right.
|
|
|
|
createIndexer(item.value->location, numberType, itemTy);
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
return ty;
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
ScopePtr signatureScope = nullptr;
|
|
|
|
ScopePtr bodyScope = nullptr;
|
2022-07-01 00:52:43 +01:00
|
|
|
TypePackId returnType = nullptr;
|
|
|
|
|
|
|
|
std::vector<TypeId> genericTypes;
|
|
|
|
std::vector<TypePackId> genericTypePacks;
|
|
|
|
|
|
|
|
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
|
|
|
|
|
|
|
|
// If we don't have any generics, we can save some memory and compute by not
|
|
|
|
// creating the signatureScope, which is only used to scope the declared
|
|
|
|
// generics properly.
|
|
|
|
if (hasGenerics)
|
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
signatureScope = childScope(fn, parent);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
// We need to assign returnType before creating bodyScope so that the
|
|
|
|
// return type gets propogated to bodyScope.
|
2022-07-29 05:24:07 +01:00
|
|
|
returnType = freshTypePack(signatureScope);
|
2022-07-01 00:52:43 +01:00
|
|
|
signatureScope->returnType = returnType;
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
bodyScope = childScope(fn->body, signatureScope);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
// We do not support default values on function generics, so we only
|
|
|
|
// care about the types involved.
|
|
|
|
for (const auto& [name, g] : genericDefinitions)
|
|
|
|
{
|
|
|
|
genericTypes.push_back(g.ty);
|
2022-08-11 22:01:33 +01:00
|
|
|
signatureScope->privateTypeBindings[name] = TypeFun{g.ty};
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& [name, g] : genericPackDefinitions)
|
|
|
|
{
|
|
|
|
genericTypePacks.push_back(g.tp);
|
2022-08-11 22:01:33 +01:00
|
|
|
signatureScope->privateTypePackBindings[name] = g.tp;
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
bodyScope = childScope(fn->body, parent);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
returnType = freshTypePack(bodyScope);
|
|
|
|
bodyScope->returnType = returnType;
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
// To eliminate the need to branch on hasGenerics below, we say that the
|
|
|
|
// signature scope is the body scope when there is no real signature
|
|
|
|
// scope.
|
|
|
|
signatureScope = bodyScope;
|
|
|
|
}
|
|
|
|
|
2022-09-15 23:38:17 +01:00
|
|
|
TypePackId varargPack = nullptr;
|
2022-09-08 23:14:25 +01:00
|
|
|
|
|
|
|
if (fn->vararg)
|
|
|
|
{
|
|
|
|
if (fn->varargAnnotation)
|
|
|
|
{
|
|
|
|
TypePackId annotationType = resolveTypePack(signatureScope, fn->varargAnnotation);
|
|
|
|
varargPack = annotationType;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
varargPack = arena->freshTypePack(signatureScope.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
signatureScope->varargPack = varargPack;
|
|
|
|
}
|
2022-09-15 23:38:17 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
varargPack = arena->addTypePack(VariadicTypePack{singletonTypes->anyType, /*hidden*/ true});
|
|
|
|
// We do not add to signatureScope->varargPack because ... is not valid
|
|
|
|
// in functions without an explicit ellipsis.
|
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_ASSERT(nullptr != varargPack);
|
2022-09-08 23:14:25 +01:00
|
|
|
|
2022-06-24 02:56:00 +01:00
|
|
|
if (fn->returnAnnotation)
|
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId annotatedRetType = resolveTypePack(signatureScope, *fn->returnAnnotation);
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(signatureScope, getLocation(*fn->returnAnnotation), PackSubtypeConstraint{returnType, annotatedRetType});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
std::vector<TypeId> argTypes;
|
|
|
|
|
|
|
|
for (AstLocal* local : fn->args)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId t = freshType(signatureScope);
|
2022-06-17 02:05:14 +01:00
|
|
|
argTypes.push_back(t);
|
2022-07-29 05:24:07 +01:00
|
|
|
signatureScope->bindings[local] = Binding{t, local->location};
|
2022-06-24 02:56:00 +01:00
|
|
|
|
|
|
|
if (local->annotation)
|
|
|
|
{
|
2022-08-04 23:35:33 +01:00
|
|
|
TypeId argAnnotation = resolveType(signatureScope, local->annotation, /* topLevel */ true);
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(signatureScope, local->annotation->location, SubtypeConstraint{t, argAnnotation});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-06-24 02:56:00 +01:00
|
|
|
// TODO: Vararg annotation.
|
2022-07-01 00:52:43 +01:00
|
|
|
// TODO: Preserve argument names in the function's type.
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2022-09-08 23:14:25 +01:00
|
|
|
FunctionTypeVar actualFunction{arena->addTypePack(argTypes, varargPack), returnType};
|
2022-07-01 00:52:43 +01:00
|
|
|
actualFunction.hasNoGenerics = !hasGenerics;
|
|
|
|
actualFunction.generics = std::move(genericTypes);
|
|
|
|
actualFunction.genericPacks = std::move(genericTypePacks);
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
TypeId actualFunctionType = arena->addType(std::move(actualFunction));
|
|
|
|
LUAU_ASSERT(actualFunctionType);
|
|
|
|
astTypes[fn] = actualFunctionType;
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
return {
|
|
|
|
/* signature */ actualFunctionType,
|
|
|
|
// Undo the workaround we made above: if there's no signature scope,
|
|
|
|
// don't report it.
|
|
|
|
/* signatureScope */ hasGenerics ? signatureScope : nullptr,
|
2022-07-29 05:24:07 +01:00
|
|
|
/* bodyScope */ bodyScope,
|
2022-07-01 00:52:43 +01:00
|
|
|
};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
visitBlockWithoutChildScope(scope, fn->body);
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
// If it is possible for execution to reach the end of the function, the return type must be compatible with ()
|
|
|
|
|
|
|
|
if (nullptr != getFallthrough(fn->body))
|
|
|
|
{
|
|
|
|
TypePackId empty = arena->addTypePack({}); // TODO we could have CSG retain one of these forever
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, fn->location, PackSubtypeConstraint{scope->returnType, empty});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty, bool topLevel)
|
2022-06-24 02:56:00 +01:00
|
|
|
{
|
|
|
|
TypeId result = nullptr;
|
|
|
|
|
|
|
|
if (auto ref = ty->as<AstTypeReference>())
|
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
std::optional<TypeFun> alias = scope->lookupType(ref->name.value);
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
if (alias.has_value() || ref->prefix.has_value())
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
// If the alias is not generic, we don't need to set up a blocked
|
|
|
|
// type and an instantiation constraint.
|
2022-09-02 00:14:03 +01:00
|
|
|
if (alias.has_value() && alias->typeParams.empty() && alias->typePackParams.empty())
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
result = alias->type;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<TypeId> parameters;
|
|
|
|
std::vector<TypePackId> packParameters;
|
|
|
|
|
|
|
|
for (const AstTypeOrPack& p : ref->parameters)
|
|
|
|
{
|
|
|
|
// We do not enforce the ordering of types vs. type packs here;
|
|
|
|
// that is done in the parser.
|
|
|
|
if (p.type)
|
|
|
|
{
|
|
|
|
parameters.push_back(resolveType(scope, p.type));
|
|
|
|
}
|
|
|
|
else if (p.typePack)
|
|
|
|
{
|
|
|
|
packParameters.push_back(resolveTypePack(scope, p.typePack));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This indicates a parser bug: one of these two pointers
|
|
|
|
// should be set.
|
|
|
|
LUAU_ASSERT(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
result = arena->addType(PendingExpansionTypeVar{ref->prefix, ref->name, parameters, packParameters});
|
2022-08-04 23:35:33 +01:00
|
|
|
|
|
|
|
if (topLevel)
|
|
|
|
{
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, ty->location, TypeAliasExpansionConstraint{/* target */ result});
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(ty->location, UnknownSymbol{ref->name.value, UnknownSymbol::Context::Type});
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryType();
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
2022-06-24 02:56:00 +01:00
|
|
|
else if (auto tab = ty->as<AstTypeTable>())
|
|
|
|
{
|
|
|
|
TableTypeVar::Props props;
|
|
|
|
std::optional<TableIndexer> indexer;
|
|
|
|
|
|
|
|
for (const AstTableProp& prop : tab->props)
|
|
|
|
{
|
|
|
|
std::string name = prop.name.value;
|
|
|
|
// TODO: Recursion limit.
|
|
|
|
TypeId propTy = resolveType(scope, prop.type);
|
|
|
|
// TODO: Fill in location.
|
|
|
|
props[name] = {propTy};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tab->indexer)
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
|
|
|
indexer = TableIndexer{
|
|
|
|
resolveType(scope, tab->indexer->indexType),
|
|
|
|
resolveType(scope, tab->indexer->resultType),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
result = arena->addType(TableTypeVar{props, indexer, scope->level, TableState::Sealed});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (auto fn = ty->as<AstTypeFunction>())
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
2022-07-01 00:52:43 +01:00
|
|
|
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
|
2022-07-29 05:24:07 +01:00
|
|
|
ScopePtr signatureScope = nullptr;
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
std::vector<TypeId> genericTypes;
|
|
|
|
std::vector<TypePackId> genericTypePacks;
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
// If we don't have generics, we do not need to generate a child scope
|
|
|
|
// for the generic bindings to live on.
|
|
|
|
if (hasGenerics)
|
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
signatureScope = childScope(fn, scope);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
for (const auto& [name, g] : genericDefinitions)
|
|
|
|
{
|
|
|
|
genericTypes.push_back(g.ty);
|
2022-08-11 22:01:33 +01:00
|
|
|
signatureScope->privateTypeBindings[name] = TypeFun{g.ty};
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& [name, g] : genericPackDefinitions)
|
|
|
|
{
|
|
|
|
genericTypePacks.push_back(g.tp);
|
2022-08-11 22:01:33 +01:00
|
|
|
signatureScope->privateTypePackBindings[name] = g.tp;
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// To eliminate the need to branch on hasGenerics below, we say that
|
|
|
|
// the signature scope is the parent scope if we don't have
|
|
|
|
// generics.
|
2022-07-29 05:24:07 +01:00
|
|
|
signatureScope = scope;
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId argTypes = resolveTypePack(signatureScope, fn->argTypes);
|
|
|
|
TypePackId returnTypes = resolveTypePack(signatureScope, fn->returnTypes);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
// TODO: FunctionTypeVar needs a pointer to the scope so that we know
|
|
|
|
// how to quantify/instantiate it.
|
|
|
|
FunctionTypeVar ftv{argTypes, returnTypes};
|
|
|
|
|
|
|
|
// This replicates the behavior of the appropriate FunctionTypeVar
|
|
|
|
// constructors.
|
|
|
|
ftv.hasNoGenerics = !hasGenerics;
|
|
|
|
ftv.generics = std::move(genericTypes);
|
|
|
|
ftv.genericPacks = std::move(genericTypePacks);
|
|
|
|
|
|
|
|
ftv.argNames.reserve(fn->argNames.size);
|
2022-06-24 02:56:00 +01:00
|
|
|
for (const auto& el : fn->argNames)
|
|
|
|
{
|
|
|
|
if (el)
|
|
|
|
{
|
|
|
|
const auto& [name, location] = *el;
|
2022-07-01 00:52:43 +01:00
|
|
|
ftv.argNames.push_back(FunctionArgument{name.value, location});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
ftv.argNames.push_back(std::nullopt);
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
}
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
result = arena->addType(std::move(ftv));
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (auto tof = ty->as<AstTypeTypeof>())
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
|
|
|
TypeId exprType = check(scope, tof->expr);
|
|
|
|
result = exprType;
|
|
|
|
}
|
|
|
|
else if (auto unionAnnotation = ty->as<AstTypeUnion>())
|
|
|
|
{
|
|
|
|
std::vector<TypeId> parts;
|
|
|
|
for (AstType* part : unionAnnotation->types)
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
|
|
|
parts.push_back(resolveType(scope, part));
|
|
|
|
}
|
|
|
|
|
|
|
|
result = arena->addType(UnionTypeVar{parts});
|
|
|
|
}
|
|
|
|
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
|
|
|
|
{
|
|
|
|
std::vector<TypeId> parts;
|
|
|
|
for (AstType* part : intersectionAnnotation->types)
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
|
|
|
parts.push_back(resolveType(scope, part));
|
|
|
|
}
|
|
|
|
|
|
|
|
result = arena->addType(IntersectionTypeVar{parts});
|
|
|
|
}
|
|
|
|
else if (auto boolAnnotation = ty->as<AstTypeSingletonBool>())
|
|
|
|
{
|
|
|
|
result = arena->addType(SingletonTypeVar(BooleanSingleton{boolAnnotation->value}));
|
|
|
|
}
|
|
|
|
else if (auto stringAnnotation = ty->as<AstTypeSingletonString>())
|
|
|
|
{
|
|
|
|
result = arena->addType(SingletonTypeVar(StringSingleton{std::string(stringAnnotation->value.data, stringAnnotation->value.size)}));
|
|
|
|
}
|
|
|
|
else if (ty->is<AstTypeError>())
|
|
|
|
{
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryType();
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(0);
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryType();
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
astResolvedTypes[ty] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTypePack* tp)
|
2022-06-24 02:56:00 +01:00
|
|
|
{
|
|
|
|
TypePackId result;
|
|
|
|
if (auto expl = tp->as<AstTypePackExplicit>())
|
|
|
|
{
|
|
|
|
result = resolveTypePack(scope, expl->typeList);
|
|
|
|
}
|
|
|
|
else if (auto var = tp->as<AstTypePackVariadic>())
|
|
|
|
{
|
|
|
|
TypeId ty = resolveType(scope, var->variadicType);
|
|
|
|
result = arena->addTypePack(TypePackVar{VariadicTypePack{ty}});
|
|
|
|
}
|
|
|
|
else if (auto gen = tp->as<AstTypePackGeneric>())
|
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
if (std::optional<TypePackId> lookup = scope->lookupPack(gen->genericName.value))
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
result = *lookup;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type});
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryTypePack();
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(0);
|
2022-09-08 23:14:25 +01:00
|
|
|
result = singletonTypes->errorRecoveryTypePack();
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
astResolvedTypePacks[tp] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const AstTypeList& list)
|
2022-06-24 02:56:00 +01:00
|
|
|
{
|
|
|
|
std::vector<TypeId> head;
|
|
|
|
|
|
|
|
for (AstType* headTy : list.types)
|
|
|
|
{
|
|
|
|
head.push_back(resolveType(scope, headTy));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<TypePackId> tail = std::nullopt;
|
|
|
|
if (list.tailType)
|
|
|
|
{
|
|
|
|
tail = resolveTypePack(scope, list.tailType);
|
|
|
|
}
|
|
|
|
|
|
|
|
return arena->addTypePack(TypePack{head, tail});
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(const ScopePtr& scope, AstArray<AstGenericType> generics)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> result;
|
|
|
|
for (const auto& generic : generics)
|
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId genericTy = arena->addType(GenericTypeVar{scope.get(), generic.name.value});
|
2022-07-01 00:52:43 +01:00
|
|
|
std::optional<TypeId> defaultTy = std::nullopt;
|
|
|
|
|
|
|
|
if (generic.defaultValue)
|
|
|
|
defaultTy = resolveType(scope, generic.defaultValue);
|
|
|
|
|
|
|
|
result.push_back({generic.name.value, GenericTypeDefinition{
|
|
|
|
genericTy,
|
|
|
|
defaultTy,
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::createGenericPacks(
|
2022-07-29 05:24:07 +01:00
|
|
|
const ScopePtr& scope, AstArray<AstGenericTypePack> generics)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> result;
|
|
|
|
for (const auto& generic : generics)
|
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), generic.name.value}});
|
2022-07-01 00:52:43 +01:00
|
|
|
std::optional<TypePackId> defaultTy = std::nullopt;
|
|
|
|
|
|
|
|
if (generic.defaultValue)
|
|
|
|
defaultTy = resolveTypePack(scope, generic.defaultValue);
|
|
|
|
|
|
|
|
result.push_back({generic.name.value, GenericTypePackDefinition{
|
|
|
|
genericTy,
|
|
|
|
defaultTy,
|
|
|
|
}});
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location location, TypePackId tp)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
if (auto f = first(tp))
|
|
|
|
return *f;
|
|
|
|
|
|
|
|
TypeId typeResult = freshType(scope);
|
|
|
|
TypePack onePack{{typeResult}, freshTypePack(scope)};
|
|
|
|
TypePackId oneTypePack = arena->addTypePack(std::move(onePack));
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, location, PackSubtypeConstraint{tp, oneTypePack});
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
return typeResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConstraintGraphBuilder::reportError(Location location, TypeErrorData err)
|
|
|
|
{
|
|
|
|
errors.push_back(TypeError{location, moduleName, std::move(err)});
|
2022-09-08 23:14:25 +01:00
|
|
|
|
|
|
|
if (FFlag::DebugLuauLogSolverToJson)
|
|
|
|
logger->captureGenerationError(errors.back());
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
|
|
|
|
{
|
|
|
|
errors.push_back(TypeError{location, moduleName, CodeTooComplex{}});
|
2022-09-08 23:14:25 +01:00
|
|
|
|
|
|
|
if (FFlag::DebugLuauLogSolverToJson)
|
|
|
|
logger->captureGenerationError(errors.back());
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
struct GlobalPrepopulator : AstVisitor
|
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
const NotNull<Scope> globalScope;
|
2022-07-01 00:52:43 +01:00
|
|
|
const NotNull<TypeArena> arena;
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
GlobalPrepopulator(NotNull<Scope> globalScope, NotNull<TypeArena> arena)
|
2022-07-01 00:52:43 +01:00
|
|
|
: globalScope(globalScope)
|
|
|
|
, arena(arena)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(AstStatFunction* function) override
|
|
|
|
{
|
|
|
|
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
|
2022-07-29 05:24:07 +01:00
|
|
|
globalScope->bindings[g->name] = Binding{arena->addType(BlockedTypeVar{})};
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
GlobalPrepopulator gp{NotNull{globalScope.get()}, arena};
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
program->visit(&gp);
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void collectConstraints(std::vector<NotNull<Constraint>>& result, NotNull<Scope> scope)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
|
|
|
for (const auto& c : scope->constraints)
|
2022-06-17 02:05:14 +01:00
|
|
|
result.push_back(NotNull{c.get()});
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
for (NotNull<Scope> child : scope->children)
|
2022-06-03 23:15:45 +01:00
|
|
|
collectConstraints(result, child);
|
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope> rootScope)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-06-17 02:05:14 +01:00
|
|
|
std::vector<NotNull<Constraint>> result;
|
2022-06-03 23:15:45 +01:00
|
|
|
collectConstraints(result, rootScope);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Luau
|