luau/Analysis/src/ConstraintGraphBuilder.cpp

1366 lines
46 KiB
C++
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ConstraintGraphBuilder.h"
#include "Luau/Ast.h"
#include "Luau/Constraint.h"
2022-07-01 00:52:43 +01:00
#include "Luau/RecursionCounter.h"
#include "Luau/ToString.h"
LUAU_FASTINT(LuauCheckRecursionLimit);
2022-06-24 02:56:00 +01:00
#include "Luau/Scope.h"
namespace Luau
{
2022-06-17 02:05:14 +01:00
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
2022-07-01 00:52:43 +01:00
ConstraintGraphBuilder::ConstraintGraphBuilder(
const ModuleName& moduleName, ModulePtr module, TypeArena* arena, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope)
2022-07-01 00:52:43 +01:00
: moduleName(moduleName)
, module(module)
2022-07-01 00:52:43 +01:00
, singletonTypes(getSingletonTypes())
, arena(arena)
, rootScope(nullptr)
2022-07-01 00:52:43 +01:00
, ice(ice)
, globalScope(globalScope)
{
LUAU_ASSERT(arena);
}
2022-07-29 05:24:07 +01:00
TypeId ConstraintGraphBuilder::freshType(const ScopePtr& scope)
{
2022-07-29 05:24:07 +01:00
return arena->addType(FreeTypeVar{scope.get()});
}
2022-07-29 05:24:07 +01:00
TypePackId ConstraintGraphBuilder::freshTypePack(const ScopePtr& scope)
{
2022-07-29 05:24:07 +01:00
FreeTypePack f{scope.get()};
return arena->addTypePack(TypePackVar{std::move(f)});
}
2022-07-29 05:24:07 +01:00
ScopePtr ConstraintGraphBuilder::childScope(Location location, const ScopePtr& parent)
{
2022-07-29 05:24:07 +01:00
auto scope = std::make_shared<Scope>(parent);
scopes.emplace_back(location, scope);
2022-07-29 05:24:07 +01:00
scope->returnType = parent->returnType;
parent->children.push_back(NotNull(scope.get()));
2022-07-29 05:24:07 +01:00
return scope;
}
2022-07-29 05:24:07 +01:00
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, ConstraintV cv)
{
2022-06-24 02:56:00 +01:00
scope->constraints.emplace_back(new Constraint{std::move(cv)});
}
2022-07-29 05:24:07 +01:00
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c)
{
scope->constraints.emplace_back(std::move(c));
}
void ConstraintGraphBuilder::visit(AstStatBlock* block)
{
LUAU_ASSERT(scopes.empty());
LUAU_ASSERT(rootScope == nullptr);
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-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-24 02:56:00 +01:00
// TODO: We should share the global scope.
rootScope->privateTypeBindings["nil"] = TypeFun{singletonTypes.nilType};
rootScope->privateTypeBindings["number"] = TypeFun{singletonTypes.numberType};
rootScope->privateTypeBindings["string"] = TypeFun{singletonTypes.stringType};
rootScope->privateTypeBindings["boolean"] = TypeFun{singletonTypes.booleanType};
rootScope->privateTypeBindings["thread"] = TypeFun{singletonTypes.threadType};
2022-06-24 02:56:00 +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>())
{
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)
{
defnScope = childScope(alias->location, scope);
}
TypeId initialType = freshType(scope);
TypeFun initialFun = TypeFun{initialType};
for (const auto& [name, gen] : createGenerics(defnScope, alias->generics))
{
initialFun.typeParams.push_back(gen);
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);
defnScope->privateTypePackBindings[name] = genPack.tp;
2022-08-04 23:35: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-07-29 05:24:07 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
{
2022-07-01 00:52:43 +01:00
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
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);
else if (auto s = stat->as<AstStatWhile>())
visit(scope, s);
2022-06-17 02:05:14 +01:00
else if (auto f = stat->as<AstStatFunction>())
visit(scope, f);
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);
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);
else
LUAU_ASSERT(0);
}
2022-07-29 05:24:07 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
{
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-06-24 02:56:00 +01:00
addConstraint(scope, SubtypeConstraint{ty, annotation});
}
varTypes.push_back(ty);
2022-07-29 05:24:07 +01:00
scope->bindings[local] = Binding{ty, location};
}
2022-06-17 02:05:14 +01:00
for (size_t i = 0; i < local->values.size; ++i)
{
2022-06-17 02:05:14 +01:00
if (local->values.data[i]->is<AstExprConstantNil>())
{
// 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)
{
TypePackId exprPack = checkPack(scope, local->values.data[i]);
if (i < local->vars.size)
{
std::vector<TypeId> tailValues{varTypes.begin() + i, varTypes.end()};
TypePackId tailPack = arena->addTypePack(std::move(tailValues));
2022-06-24 02:56:00 +01:00
addConstraint(scope, PackSubtypeConstraint{exprPack, tailPack});
2022-06-17 02:05:14 +01:00
}
}
else
{
TypeId exprType = check(scope, local->values.data[i]);
2022-06-17 02:05:14 +01:00
if (i < varTypes.size())
2022-06-24 02:56:00 +01:00
addConstraint(scope, SubtypeConstraint{varTypes[i], exprType});
}
}
}
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);
addConstraint(scope, SubtypeConstraint{t, singletonTypes.numberType});
};
checkNumber(for_->from);
checkNumber(for_->to);
checkNumber(for_->step);
2022-07-29 05:24:07 +01:00
ScopePtr forScope = childScope(for_->location, scope);
forScope->bindings[for_->var] = Binding{singletonTypes.numberType, for_->var->location};
2022-07-14 23:52:26 +01:00
visit(forScope, for_->body);
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatWhile* while_)
{
check(scope, while_->condition);
ScopePtr whileScope = childScope(while_->location, scope);
visit(whileScope, while_->body);
}
2022-07-29 05:24:07 +01:00
void addConstraints(Constraint* constraint, NotNull<Scope> scope)
{
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-07-29 05:24:07 +01:00
for (NotNull<Scope> childScope : scope->children)
addConstraints(constraint, childScope);
}
2022-07-29 05:24:07 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function)
{
// 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-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-07-01 00:52:43 +01:00
checkFunctionBody(sig.bodyScope, function->func);
2022-07-01 00:52:43 +01:00
std::unique_ptr<Constraint> c{
2022-07-29 05:24:07 +01:00
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
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-17 02:05:14 +01:00
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
{
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;
addConstraint(scope, SubtypeConstraint{containingTableType, prospectiveTableType});
}
else if (AstExprError* err = function->name->as<AstExprError>())
{
functionType = singletonTypes.errorRecoveryType();
}
2022-07-01 00:52:43 +01:00
LUAU_ASSERT(functionType != nullptr);
2022-07-01 00:52:43 +01:00
checkFunctionBody(sig.bodyScope, function->func);
std::unique_ptr<Constraint> c{
2022-07-29 05:24:07 +01:00
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
addConstraint(scope, std::move(c));
}
2022-07-29 05:24:07 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatReturn* ret)
{
TypePackId exprTypes = checkPack(scope, ret->list);
2022-06-24 02:56:00 +01:00
addConstraint(scope, PackSubtypeConstraint{exprTypes, scope->returnType});
}
2022-07-29 05:24:07 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
{
2022-07-29 05:24:07 +01:00
ScopePtr innerScope = childScope(block->location, scope);
2022-07-01 00:52:43 +01:00
visitBlockWithoutChildScope(innerScope, block);
}
2022-07-29 05:24:07 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
2022-06-17 02:05:14 +01:00
{
TypePackId varPackId = checkExprList(scope, assign->vars);
TypePackId valuePack = checkPack(scope, assign->values);
2022-06-24 02:56:00 +01:00
addConstraint(scope, PackSubtypeConstraint{valuePack, varPackId});
2022-06-17 02:05:14 +01:00
}
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-07-29 05:24:07 +01:00
ScopePtr thenScope = childScope(ifStatement->thenbody->location, scope);
2022-06-17 02:05:14 +01:00
visit(thenScope, ifStatement->thenbody);
if (ifStatement->elsebody)
{
2022-07-29 05:24:07 +01:00
ScopePtr elseScope = childScope(ifStatement->elsebody->location, 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
{
// TODO: Exported type aliases
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.
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);
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
addConstraint(scope, NameConstraint{ty, alias->name.value});
}
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);
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};
}
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
{
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)
{
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)
genericTys.push_back(generic.ty);
std::vector<TypePackId> genericTps;
genericTps.reserve(genericPacks.size());
for (auto& [name, generic] : genericPacks)
genericTps.push_back(generic.tp);
ScopePtr funScope = scope;
if (!generics.empty() || !genericPacks.empty())
funScope = childScope(global->location, scope);
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)
{
if (exprs.size == 0)
return arena->addTypePack({});
std::vector<TypeId> types;
TypePackId last = nullptr;
for (size_t i = 0; i < exprs.size; ++i)
{
if (i < exprs.size - 1)
types.push_back(check(scope, exprs.data[i]));
else
last = checkPack(scope, exprs.data[i]);
}
LUAU_ASSERT(last != nullptr);
return arena->addTypePack(TypePack{std::move(types), last});
}
2022-07-29 05:24:07 +01:00
TypePackId ConstraintGraphBuilder::checkExprList(const ScopePtr& scope, const AstArray<AstExpr*>& exprs)
2022-06-17 02:05:14 +01:00
{
TypePackId result = arena->addTypePack({});
TypePack* resultPack = getMutable<TypePack>(result);
LUAU_ASSERT(resultPack);
for (size_t i = 0; i < exprs.size; ++i)
{
AstExpr* expr = exprs.data[i];
if (i < exprs.size - 1)
resultPack->head.push_back(check(scope, expr));
else
resultPack->tail = checkPack(scope, expr);
}
if (resultPack->head.empty() && resultPack->tail)
return *resultPack->tail;
else
return result;
}
2022-07-29 05:24:07 +01:00
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* expr)
{
2022-07-01 00:52:43 +01:00
RecursionCounter counter{&recursionCount};
if (recursionCount >= FInt::LuauCheckRecursionLimit)
{
reportCodeTooComplex(expr->location);
return singletonTypes.errorRecoveryTypePack();
}
2022-06-17 02:05:14 +01:00
TypePackId result = nullptr;
if (AstExprCall* call = expr->as<AstExprCall>())
{
std::vector<TypeId> args;
for (AstExpr* arg : call->args)
{
args.push_back(check(scope, arg));
}
// TODO self
TypeId fnType = check(scope, call->func);
astOriginalCallTypes[call->func] = fnType;
2022-07-01 00:52:43 +01:00
TypeId instantiatedType = arena->addType(BlockedTypeVar{});
2022-06-24 02:56:00 +01:00
addConstraint(scope, InstantiationConstraint{instantiatedType, fnType});
2022-06-17 02:05:14 +01:00
TypePackId rets = freshTypePack(scope);
FunctionTypeVar ftv(arena->addTypePack(TypePack{args, {}}), rets);
TypeId inferredFnType = arena->addType(ftv);
2022-06-24 02:56:00 +01:00
addConstraint(scope, SubtypeConstraint{inferredFnType, instantiatedType});
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
result = singletonTypes.errorRecoveryTypePack();
}
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-07-29 05:24:07 +01:00
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
{
2022-07-01 00:52:43 +01:00
RecursionCounter counter{&recursionCount};
if (recursionCount >= FInt::LuauCheckRecursionLimit)
{
reportCodeTooComplex(expr->location);
return singletonTypes.errorRecoveryType();
}
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>())
result = singletonTypes.stringType;
else if (expr->is<AstExprConstantNumber>())
result = singletonTypes.numberType;
else if (expr->is<AstExprConstantBool>())
result = singletonTypes.booleanType;
else if (expr->is<AstExprConstantNil>())
result = singletonTypes.nilType;
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
result = singletonTypes.errorRecoveryType(); // FIXME? Record an error at this point?
}
else if (auto g = expr->as<AstExprGlobal>())
{
std::optional<TypeId> ty = scope->lookup(g->name);
if (ty)
result = *ty;
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});
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);
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?
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-24 02:56:00 +01:00
addConstraint(scope, 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});
addConstraint(scope, SubtypeConstraint{obj, tableType});
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{});
addConstraint(scope, UnaryConstraint{AstExprUnary::Minus, operandType, resultType});
return resultType;
}
default:
LUAU_ASSERT(0);
}
LUAU_UNREACHABLE();
return singletonTypes.errorRecoveryType();
}
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:
{
addConstraint(scope, SubtypeConstraint{leftType, rightType});
return leftType;
}
case AstExprBinary::Sub:
{
TypeId resultType = arena->addType(BlockedTypeVar{});
addConstraint(scope, BinaryConstraint{AstExprBinary::Sub, leftType, rightType, resultType});
return resultType;
}
default:
LUAU_ASSERT(0);
}
LUAU_ASSERT(0);
return nullptr;
}
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-06-24 02:56:00 +01:00
auto createIndexer = [this, scope, ttv](TypeId currentIndexType, TypeId currentResultType) {
2022-06-17 02:05:14 +01:00
if (!ttv->indexer)
{
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-24 02:56:00 +01:00
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexType, currentIndexType});
addConstraint(scope, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType});
2022-06-17 02:05:14 +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-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-06-24 02:56:00 +01:00
createIndexer(keyTy, itemTy);
2022-06-17 02:05:14 +01:00
}
}
else
{
TypeId numberType = singletonTypes.numberType;
2022-06-24 02:56:00 +01:00
createIndexer(numberType, itemTy);
2022-06-17 02:05:14 +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-07-29 05:24:07 +01:00
signatureScope = childScope(fn->location, 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-07-29 05:24:07 +01:00
bodyScope = childScope(fn->body->location, 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);
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);
signatureScope->privateTypePackBindings[name] = g.tp;
2022-07-01 00:52:43 +01:00
}
}
else
{
2022-07-29 05:24:07 +01:00
bodyScope = childScope(fn->body->location, 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-06-24 02:56:00 +01:00
if (fn->returnAnnotation)
{
2022-07-29 05:24:07 +01:00
TypePackId annotatedRetType = resolveTypePack(signatureScope, *fn->returnAnnotation);
addConstraint(signatureScope, 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-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-07-29 05:24:07 +01:00
addConstraint(signatureScope, 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-06-17 02:05:14 +01:00
FunctionTypeVar actualFunction{arena->addTypePack(argTypes), 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-06-24 02:56:00 +01:00
addConstraint(scope, PackSubtypeConstraint{scope->returnType, empty});
}
}
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>())
{
// TODO: Support imported types w/ require tracing.
LUAU_ASSERT(!ref->prefix);
std::optional<TypeFun> alias = scope->lookupType(ref->name.value);
2022-08-04 23:35:33 +01:00
if (alias.has_value())
{
// If the alias is not generic, we don't need to set up a blocked
// type and an instantiation constraint.
if (alias->typeParams.empty() && alias->typePackParams.empty())
{
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);
}
}
result = arena->addType(PendingExpansionTypeVar{*alias, parameters, packParameters});
if (topLevel)
{
addConstraint(scope, TypeAliasExpansionConstraint{
/* target */ result,
});
}
}
}
else
{
reportError(ty->location, UnknownSymbol{ref->name.value, UnknownSymbol::Context::Type});
result = singletonTypes.errorRecoveryType();
}
}
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),
};
}
// TODO: Remove TypeLevel{} here, we don't need it.
result = arena->addType(TableTypeVar{props, indexer, TypeLevel{}, TableState::Sealed});
}
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-07-29 05:24:07 +01:00
signatureScope = childScope(fn->location, 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);
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);
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>())
{
result = singletonTypes.errorRecoveryType();
}
else
{
LUAU_ASSERT(0);
result = singletonTypes.errorRecoveryType();
}
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>())
{
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});
result = singletonTypes.errorRecoveryTypePack();
}
2022-06-24 02:56:00 +01:00
}
else
{
LUAU_ASSERT(0);
result = singletonTypes.errorRecoveryTypePack();
}
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-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));
addConstraint(scope, PackSubtypeConstraint{tp, oneTypePack});
return typeResult;
}
void ConstraintGraphBuilder::reportError(Location location, TypeErrorData err)
{
errors.push_back(TypeError{location, moduleName, std::move(err)});
}
void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
{
errors.push_back(TypeError{location, moduleName, CodeTooComplex{}});
}
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)
{
for (const auto& c : scope->constraints)
2022-06-17 02:05:14 +01:00
result.push_back(NotNull{c.get()});
2022-07-29 05:24:07 +01:00
for (NotNull<Scope> child : scope->children)
collectConstraints(result, child);
}
2022-07-29 05:24:07 +01:00
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope> rootScope)
{
2022-06-17 02:05:14 +01:00
std::vector<NotNull<Constraint>> result;
collectConstraints(result, rootScope);
return result;
}
} // namespace Luau